Powershell Get-Mailbox loop - powershell

I am using Get-Mailbox to grab user/mailbox names, then Get-MailboxPermission search.
Get-Mailbox -ResultSize unlimited -Filter {name -like "a*"} | Get-MailboxPermission | where { ($_.AccessRights -eq “FullAccess, ChangePermission”) | blah blah
I have a different line for each starting letter (a*, b*, c* etc). This works fine but it seems like this could be done with some kind of loop (foreach or foreach-object) with an array reference ( #("a","b") - or it may need to be #("a*","b*") ) but I can work out the wildcard part later probably.
I don't run without a filter since there are too many mailboxes and memory usage is intense, using a letter by letter reference, the memory seems to drop back down at each letter (and running garbage collection between each letter seems to help - the output of each letter is written to a .csv).
Thought anyone - I feel I'm missing something simple since so far trying with an array is not working. The result it usually spits back ALL mailboxes vs the curated letter ones (a,b,c).

You can loop through an array to avoid the multiple lines.
$alphabet = [char[]]([int][char]'a'..[int][char]'z')
foreach ($letter in $alphabet) {
Get-Mailbox -ResultSize unlimited -Filter "name -like '$letter*'" | Get-MailboxPermission | where { ($_.AccessRights -eq “FullAccess, ChangePermission”) | blah blah
} # end foreach
This topic discusses graceful alphabet arrays.

Related

Get-Aduser -Filter Option -notlike does not work

Attempting to use Get-Aduser to find entries in Active directory that are not in a text file. The -like option appears to work but cannot seem to get the -notlike to work.
When I use the -nolike option, the entries in the text file appear as part of the output file. Using the -like option the powershell works.
Here is the contents of the text file
svcXXSQL001Agent
svcXXSQL001DBEng
svcXXSQL001Int
svcXXSQLUAT501DBEng
svcxxapp211
Here is my existing code:
$server=get-content C:\temp\test.txt
foreach ($name in $server) {
Get-ADUser -SearchBase “OU=ServiceAccts,DC=nlong,DC=com” -Filter "name -notlike '$name'" | sort | Where-Object {$_.Name -like "svcxxsql*"} | Select- Object Name | Out-File -FilePath C:\temp\foo.txt
}
Thanks for the input, Norm.
Expecting the output without the given names is a false assumption, let me demonstrate using the numbers 1 and 2, and the Where-Object cmdlet in place of the Get-ADUser filter all numbers from 1 to 5 except for 1 or 2:
$numbers = 1,2,3
foreach($number in $numbers){
# Let's output all numbers from 1 to 3, except for $number
1..3 |Where-Object {$_ -notlike $number}
}
You will find the output to be:
2
3
1
3
1
2
In the first iteration of the loop, we receive the number 2 along with the number 3 - this is obviously not our intention, they were supposed to be filtered, but it ended up in the output because we filter only against 1 number at a time.
We can use either the -notcontains or -notin operators to filter against a collection of terms instead:
$numbers = 1,2,3
1..3 |Where-Object {$numbers -notcontains $_}
# or
1..3 |Where-Object {$_ -notin $numbers}
In your example, you would have to retrieve all the AD users and filter using the Where-Object cmdlet:
Get-ADUser -SearchBase "OU=ServiceAccts,DC=nlong,DC=com" |Where-Object {$_.Name -notin $server} | sort | Where-Object {$_.Name -like "svcxxsql*"} | Select-Object Name | Out-File -FilePath C:\temp\foo.txt
Since you're only interested in accounts that start with svcxxsql, we might as well place that as the filter:
Get-ADUser -SearchBase "OU=ServiceAccts,DC=nlong,DC=com" -Filter {Name -like "svcxxsql*"} |Where-Object {$_.Name -notin $server} | sort | Select-Object Name | Out-File -FilePath C:\temp\foo.txt
While this is old, here's a more efficient method using an actual LDAP filter that you construct from information supplied in the file.
Assuming the file contains the actual sAMAccountNames you wish to exclude:
$servers = get-content C:\temp\test.txt
# Begin the filter - sAMAccountType=805306368 is user objects only, no contacts
$filter = '(&(sAMAccountType=805306368)(!(|'
# recursively append each samAccountName to exclude in the filter
foreach ($u in $servers) {
$filter = $filter + "(samAccountName=$u)"
}
#finish the filter
$filter = $filter + ')))'
#ldap filter looks like this
# (&(sAMAccountType=805306368)(!(|(samAccountName=svcXXSQL001Agent)(samAccountName=svcXXSQL001DBEng)(samAccountName=svcXXSQL001Int)(...))))
# run the query
Get-aduser -LDAPFilter $filter -SearchBase "OU=ServiceAccts,DC=nlong,DC=com"
Active Directory can technically take an LDAP query that's 10MB in size, although obviously that'd be really excessive. So I recommend the above method be used only if it's a limited number of items you want to exclude.
I use a similar method to build a query for users that are members of certain groups but not others, and for that, it's significantly faster than grabbing groups with thousands of users each and trying to compare members that are exclusive to one.
As always, test and compare the time to execute the different methods:
grabbing everything and discarding unwanted results after
grabbing a partially-filtered set and discarding after (like the original answer)
constructing a more complex targeted query (this example)
Also consider that processing loads occur in different places. It can take a long time for a DC to execute a very long, complex LDAP query, with extra CPU and potential timeouts. But it can take even longer to grab all the properties from every single object from AD, with potential connection timeouts, with more data travelling across the wire to the target system, then getting all that data processed locally.
In my experience, it's the "grab everything" queries with big result sets that cause the most load, on DCs, network and target systems (Perfmon LDAP monitoring on the DCs can be interesting). That's that's why it's often best for the DC to do the filtering work, as long as the LDAP filters are sensible.

PowerShell Office 365 Script to get user and mailbox information together

I am brand new to PowerShell (started this morning). I have successfully connected to my Office 365 and have been able to get lists of users from Office 365 and mailbox fields from the Exchange portion. What I can't figure out is how to combine them.
What I am looking for is the ability to export certain fields from the mailbox object but only for those mailboxes that belong to a non-blocked, licensed Office 365 users. We have a lot of users whose mailboxes have not been removed but they may no longer be licensed or they may be blocked.
Here are the two exports I have running now. They are complete exports. I tried to filter to the Office 265 users by isLicensed but I never got any results so I just downloaded everything and post processed them with Excel. But I need to run this on a regular basis...
Here's the code:
Get-Mailbox -ResultSize Unlimited | Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2 | Export-CSV C:\temp\o365\mailboxes.csv
Get-MsolUser -all | Select-Object SignInName, DisplayName, Office, Department, Title, IsLicensed | export-csv c:\temp\o365\Users.csv
Any assistance would be appreciated.
Okay, so as I understand what you're trying to do... You want to get a list of all O365 users for whom the IsLicensed property is $true and the BlockCredential property is $false. Of those users, you then want to pull some data from their mailbox objects; DisplayName, Name, PrimarySMTPAddress, and CustomAttribute2.
There are a couple of ways that we can do this. The first is easier to throw together in the shell but takes longer to actually run. The second requires some set up but completes quickly.
First method
Since we know what our criteria is for what we want from Get-MsolUser, we'll use the pipeline to pull out what we want and toss it straight into Get-Mailbox.
Get-MsolUser -All | Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false} |
Select-Object UserPrincipalName |
ForEach-Object {Get-Mailbox -Identity $_.UserPrincipalName | Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2}
O365 PowerShell doesn't like giving us ways to filter our initial query, so we handle that in the second step, here...
Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false}
This means, for each item passed in from Get-MsolUser -All, we only want those which have the properties Islicensed set to $true and BlockCredential set to $false.
Now, we only care about the results of Get-MsolUser in so far as determining what mailboxes to look up, so we'll grab a single property from each of the objects matching our prior filter.
Select-Object UserPrincipalName
If you only run everything up to this point, you'd get a list of UPNs in your shell for all of the accounts that we're now going to pipe into Get-Mailbox.
Moving on to our loop in this... If you haven't learned ForEach-Object yet, it's used to run a scriptblock (everything between the {}) against each item in the pipeline, one at a time.
Get-Mailbox -Identity $_.UserPrincipalName
Welcome to the pipeline operator ($_). Our previous Select-Object is feeding a collection of objects through the pipeline and this placeholder variable will hold each one as we work on them. Since these objects all have a UserPrincipalName property, we reference that for the value to pass to the Identity parameter of Get-Mailbox.
Sidebar
Here's a simple example of how this works..
PS> 1,2,3 | ForEach-Object {Write-Host $_}
1
2
3
Each item is passed along the pipeline, where we write them out one at a time. This is very similar to your standard foreach loop. You can learn more about their differences in this Scripting Guy post.
Moving on...
Select-Object DisplayName,Name,PrimarySMTPAddress,CustomAttribute2
We wrap it up with one last Select-Object for the information that you want. You can then look at the results in the shell or pipe into Export-Csv for working with in Excel.
Now... Since the pipeline works sequentially, there's some overhead to it. We're running a command, collecting the results, and then passing those results into the next command one at a time. When we get to our Get-Mailbox, we're actually running Get-Mailbox once for every UPN that we've collected. This takes about 2.5 minutes in my organization and we have less than 500 mailboxes. If you're working with larger numbers, the time to run this can grow very quickly.
Second method
Since a large amount of the processing overhead in the first method is with using the pipeline, we can eliminate most of it by handling our data collection as early and thoroughly as possible.
$Users = Get-MsolUser -All | Where-Object {$_.IsLicensed -eq $true -and $_.BlockCredential -eq $false} | Select-Object -ExpandProperty UserPrincipalName
$Mailboxes = Get-Mailbox | Select-Object UserPrincipalName,DisplayName,Name,PrimarySMTPAddress,CustomAttribute2
$Results = foreach ($User in $Users) {
$Mailboxes | Where-Object UserPrincipalName -eq $User
}
$Results | Export-Csv myFile.csv
The first 2 lines are pretty self-explanatory. We get all the user account info that we care about (just the UPNs) and then we grab all the mailbox properties that we care about.
foreach ($User in $Users)
Each entry in $Users will be stored in $User, where we'll then use it in the scriptblock that follows (in the {}).
$Mailboxes | Where-Object UserPrincipalName -eq $User
Each item in $Mailboxes is piped into Where-Object where we then check if the UserPrincipalName property is equal to the current value of $User. All of the matches are then stored in $Results, which can again be piped to Export-Csv for work in Excel.
While this method is harder to write out in the shell, and requires a little extra initial set up, it runs significantly faster; 22 seconds for my org, versus 2.5 minutes with the first method.
I should also point out that the use of UserPrincipalName with the mailbox dataset is simply to help ensure a solid match between those and the account dataset. If you don't want it in your final results, you can always pipe $Results into another Select-Object and specify only the properties that you care about.
Hope this helps!

Getting Calculated Expression to Display Properly in Powershell Exchange Command

So I have a command to look at all the mailboxes in my environment and return the oldest item in any folder that is not the "Contacts" folder of that particular mailbox. The whole thing seems to work except for the calculated expression that I threw in at the end of the command:
#{Name="Address";Expression={Get-Mailbox | ForEach-Object {$_.PrimarySmtpAddress}
The problem is this seems to return every Smtp Address for each line/object instead of one Smtp Address per line/object.
Here's the entire command:
Get-Mailbox | ForEach-Object {Get-MailboxFolderStatistics -IncludeOldestandNewestItems -Identity $_.Alias | Where-Object {($_.OldestItemReceivedDate -ne $null) -and ($_.FolderPath -ne "/Contacts")} | Sort OldestItemReceivedDate | Select First 1 OldestItemReceivedDate, Identity, #{Name="Address";Expression={Get-Mailbox | ForEach-Object {$_.PrimarySmptAddress}}}}
Ideally this would return the date of the oldest item, the folder where it was found, and the primary SMTP Address but it doesn't seem to be pulling only the corresponding SMTP Address. It looks like it's pulling every Primary SMTP Address every iteration. I'm sure it's something with my command but I can't figure out where. Any help would be much appreciated.
The calculated expression has access to the current pipeline object. However you are not using that when creating your expression. You are just calling every mailbox for each user as you have seen. Use the current pipeline object with $_. Get-Mailbox is smart enough to match needed values by property name.
#{Name="Address";Expression={Get-Mailbox $_ | ForEach-Object {$_.PrimarySmtpAddress}}}}
However you might be able to go about this is a different way. You already called all mailboxes at the start of your pipeline. No sense calling it a second time again.
Get-Mailbox | Select-Object Identity, #{Name="Address";Expression={$_.PrimarySmtpAddress}}, #{Name="OldestItemReceivedDate";Expression={
Get-MailboxFolderStatistics -IncludeOldestandNewestItems -Identity $_.Alias | Where-Object {
($_.OldestItemReceivedDate -ne $null) -and ($_.FolderPath -ne "/Contacts")} |
Sort-Object OldestItemReceivedDate |
Select-Object -ExpandProperty OldestItemReceivedDate -Last 1
}}
Now we have 2 calculated properties and we only need to call Get-Mailbox once for each user. You have some spelling mistakes and logic errors that I tried to fix. You will know if it does what you want.

List all mailboxes that forward to a specific user

I have this script that lists all mailboxes that are forwarding email, however, I am curious if there would be a way to make it return all mailboxes that forward to a specific user. Basically I'm trying to find out every mailbox that forwards mail to "johndoe". Any help would be greatly appreciated! This is for exchange 2007 btw...
Here's the script so far:
$fwds = get-mailbox | Where-Object { $_.ForwardingAddress -ne $null }
| select Name, ForwardingAddress
foreach ($fwd in $fwds) {$fwd | add-member -membertype noteproperty
-name “ContactAddress” -value (get-contact $fwd.ForwardingAddress).WindowsEmailAddress}
$fwds
Exchange uses CanonicalName for the forwarding address, so you'll need to look that up from the user name. Since it could be a Mailbox, DL, or Contact, the easiest way I know of is to use Get-Recipient, and grab the Identity property.
$RecipientCN = (get-recipient johndoe).Identity
get-mailbox | Where-Object { $_.ForwardingAddress -eq $RecipientCN }
#mjolinor's version works, but is rather slow, since it loads all the mailboxes. On my system it took about 30 seconds to go through ~300 mailboxes.
This can be speed up by adding a filter to the Get-Mailbox command to only return ones that are actually being forwarded, like so:
$RecipientCN = (get-recipient johndoe).Identity
Get-Mailbox -ResultSize Unlimited -Filter {ForwardingAddress -ne $null} | Where-Object {$_.ForwardingAddress -eq $RecipientCN}
But wait, we can get even faster! Why not search for the correct user right in the filter? Probably because it's hard to get the syntax right, because using variables in -Filter gets confusing.
The trick is to use double quotes around the entire filter expression, and single quotes around the variable:
$RecipientCN = (get-recipient johndoe).Identity
Get-Mailbox -ResultSize Unlimited -Filter "ForwardingAddress -eq '$RecipientCN'"
This version returns the same results in 0.6s - about 50 times faster.

Remove white space from PowerShell output

I'm returning the mail property of a distribution group within Active Directory using the command below in PowerShell.
Get-ADGroup $GroupName -Properties Mail | Select-Object Mail | Format-Wide
The output looks like (asterisks used to represent white space):
*
*
mygroup#mycompany.com
*
*
Is there any way that I can remove the white space added at the beginning and end of the output?
I think this should work (V2):
(Get-ADGroup $GroupName -Properties Mail | Select-Object Mail | Format-Wide | out-string).split("`n") -match '\S'
Edit: that's way more complicated than it needs to be.
(Get-ADGroup $GroupName -Properties Mail | Select-Object Mail | Format-Wide | Out-String).trim()
That is how PowerShell formats output. I have complained on several occasions about the excess blank lines before and after output. If you want to avoid that, then you format the output yourself. You can do that like so:
$res = #(Get-ADGroup $GroupName -Properties Mail | Select-Object Mail)
for ($i = 0; $i -lt $res.Length - 1 + 4; $i += 4) {
"{0,-28} {1,-28} {2,-28} {3,-28}" -f $res[$i].Mail,$res[$i+1].Mail,$res[$i+2].Mail,$res[$i+3].Mail
}
This assumes your current console is 120 chars wide. If it is 80, change the -28 above to -18.
BTW the key point here is that PowerShell deals in objects and when it renders those objects to the screen it has a formatting engine that determines things like blank lines before and after the output. If you don't like PowerShell's default formatting, you're free to format objects (displaying whichever properties you want) as you please but it is a bit more work.
All that said, if the command returns only one object, why not just do this:
(Get-ADGroup $GroupName -Properties Mail).Mail
The Select-Object Mail, Format-Wide and Out-String are not necessary. Heck, with PowerShell V3 this will work even if the command returns multiple objects.
A combination of the examples in the checked answer worked for me in a similar situation:
... Format-Table | Out-String).split("`n").trim()
After re-reading the original question it seems I had a different problem to solve. I was looking for a way to trim white space from the end of output lines. The checked answer led me to try the above code which did what I was looking for.