Powershell Get-ADUser sam -pr Mail, OtherMailbox, Manager. How to get Manager email if in OtherMailbox attribute? - active-directory

I have an odd scenario. We had to move certain user mail attributes to otherMailbox to avoid AAD sync guest account create errors coming from affiliate. Now I have to do something like this to make sure an email is picked if mail is empty in AD. The below works fine.
Get-ADUser SamAccountName -pr mail, othermailbox | select #{N='Mail';E={if (!($_.mail)) {$_.otherMailbox}else{$_.mail}}}
Now the hard part. How would I get a user's manager's email in this case, if their mail was moved to OtherMailbox? Doing something like these next 2 lines, I can get one or the other. But how to do the IF ELSE like above in these cases?
(Get-ADUser $_.manager -Properties mail, othermailbox).otherMailbox
(Get-ADUser $_.manager -Properties mail, othermailbox).mail
It doesn't seem possible to do something like this without a foreach loop where a variable can be checked for which attribute exists or not? I'm ok with re-writing this to take care of. Just wondering if there's some other method I might be missing? Thx

Working now (foreach removed to shorten code example). Don't use select after $AD or $MGR Get-ADUser array or things get messy. Expression logic only works on BaseType Microsoft.ActiveDirectory.Management.ADAccount for $AD and $MGR variable types.
$AD = Get-ADUser $User.SamAccountName -Properties Company,employeeId,givenname,initials,sn,Mail,OtherMailbox,SamAccountName,Department,Title,telephoneNumber,mobile,Manager
$MGR = Get-ADUser $AD.Manager -Properties Mail, OtherMailbox
$AD | select #{Name="Org ID"; Expression={$OrgID}},
#{Name="Employee ID"; Expression={$AD.employeeId}},
#{Name="FirstName"; Expression={$AD.givenname}},
#{Name="MiddleName"; Expression={$AD.initials}},
#{Name="LastName"; Expression={$AD.sn}},
#{N='Email';E={if (!($AD.mail)) {$AD.otherMailbox}else{$AD.mail}}},
#{Name="Username"; Expression={$AD.samaccountname}},
#{Name="Manager Email Address"; Expression={if (!($MGR.mail)) {$MGR.otherMailbox}else{$MGR.mail}}} |
export-csv Path -NoTypeInformation -Append

Related

Cannot add items to Powershell array

I'm relatively new to Powershell but haven't been able to find an answer online.
I'm trying to get the number of emails per disabled user in exchange 2010, but also need to get the user's title form AD as the organization groups users by type using the Title attribute in AD
I've written the following but I'm unable to get the data I need, it just returns Length and numbers to the CSV file e.g.
"length"
"10"
"3"
"34"
If I leave $title out of the assignment of $Disabled+= the user's name and item count is added to the csv file, but I really need the title also. Can anyone point out where I'm going wrong.
Import-Module ActiveDirectory
$i=0
$disUsers = Get-ADUser -Filter * -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
$Disabled = #()
$disUsers | Foreach-Object{
$sam = $_.SamAccountName
$title = $_.Title
$mailDetail=Get-MailboxStatistics $sam | Select -Property DisplayName,ItemCount
$Disabled += $title, $mailDetail
$i++;
}
$Disabled | Export-Csv -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
Working with the code provided by Steve unfortunately gives the following errors
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'ADCDisabledMail' Key being added: 'ADCDisabledMail'" ...
Exception calling "Add" with "2" argument(s): "Key cannot be null. Parameter name: key"...
EDIT
With help from Steven I was able to get this working with the following
'Import-Module ActiveDirectory'
$i=0
$disUsers=Get-ADUser -Filter {mailNickName -like '*'} -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
$dis2 = $disUsers.count
$DisabledUser = #()
$disUsers | Foreach-Object{
Write-Host "Processing record $i of $dis2"
$sam = $_.SamAccountName
$title = $_.Title
$mailDetail=Get-MailboxStatistics $sam | Select-Object DisplayName, #{ Name = 'Title'; Expression = {$title}}, ItemCount
$DisabledUser+= $mailDetail
$i++;
}
$DisabledUser | Export-Csv -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
It sounds like what you are really trying to do is relate data to create a small report. You are dealing with data coming from different commands so you need a property to join on. In this case I would look at the LegacyExchangeDN AD attribute and the LegacyDN property returned by Get-MailboxStatistics. The code might look something like:
$DisabledUsers = #{}
Get-ADUser -SearchBase 'ou=User Disabled Accounts,dc=test,dc=com' -Filter * -Properties 'Title','legacyExchangeDN' |
ForEach-Object{ $DisabledUsers.Add( $_.legacyExchangeDN, $_ ) }
$DisabledUsers.Values.SamAccountName |
Get-MailboxStatistics |
Select-Object DisplayName, ItemCount, #{ Name = 'Title'; Expression = { $DisabledUsers[$_.LegacyDN].Title } }
This will output something like:
DisplayName ItemCount Title
----------- --------- -----
Mr. Smith 113576 Executives
If you would rather it go directly to a CSV file simply add the Export-CSV command after the Select-Object command, like below:
$DisabledUsers = #{}
Get-ADUser -SearchBase 'ou=User Disabled Accounts,dc=test,dc=com' -Filter * -Properties 'Title','legacyExchangeDN' |
ForEach-Object{ $DisabledUsers.Add( $_.legacyExchangeDN, $_ ) }
$DisabledUsers.Values.SamAccountName |
Get-MailboxStatistics |
Select-Object DisplayName, ItemCount, #{ Name = 'Title'; Expression = { $DisabledUsers[$_.LegacyDN].Title } } |
Export-CSV -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
I would've used Get-User from the Exchange Management Shell, however it doesn't have the LegacyExchangeDN as a returned property. It does have SamAccountName, but using it would've forced me to bridge everything through Get-Mailbox. At any rate, this is a very common technique to use a hash table to reference related values in a different collection.
I'm sure some additional work will be needed to get the report just right.
An aside, try to avoid using the += operator to append arrays. The best way to get an array is to let PowerShell provide it as I did above. However, if you can't get around it the most common alternative is an ArrayList. Like most things there are several ways to go about it below is just 1 example.
# To create:
$ArrList = [Collections.ArrayList]#()
#To Add a value:
[Void]$ArrList.Add( 'ValueOrObjectHere' )
Note: Documentation / discussion of += and ArrayList's are easy to
find with the Google machine...
Update:
Addressing Errors Noted in most recent edit:
The first error is basically impossible. Forgive me but I must assume you made some mistake to generate this error. LegacyExchangeDN should always start with '/o=...' and they key cited by the error was 'ADCDisabledMail' . Also LegacyExchangeDNs are naturally unique in Active Directory, so there's almost no chance you'd have a duplicate. As such, I made no effort, and none is warranted, to prevent such an unlikely error.
Note: If you are repeatedly testing the code you have to recreate the
hash, $DisabledUsers = #{} else the hash will exist from the
previous run and duplicate key errors are a certainty...
The second error, they 'key cannot be null' might be due to non-mailbox enabled AD accounts in the referenced OU effectively causing the LegacyExchangeDN attribute to be null for those users. Hence, null key.... You can avoid that by modifying the filter to only return mail enabled users:
$disUsers = Get-ADUser -Filter { mailNickName -like '*' } -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
Note: For reference, mailNickName is the alias propertry typically
returned with Get-Mailbox

PowerShell Send Mail To Address In Array

I'm building a script that checks for user accounts with soon-expiring passwords and sends emails to these people reminding them to change their passwords. Yes, our domain settings already generate those prompts counting down from 14 days. Yes, people ignore these. Yes, I'm trying to save work due to people failing to change their passwords in time while working remotely.
In the script I go through an array of the OUs I am responsible for and get the user accounts with expiring passwords and store these in an array containing their DisplayName, Mail, and calculated values containing DaysLeft and ExpiryDate:
$diff = New-TimeSpan -Start ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")) -End (Get-Date)
$OusToSearch = #(
'OU=ORG1,DC=corp,DC=com'
'OU=ORG2,DC=corp,DC=com'
'OU=ORG3,DC=corp,DC=com'
)
ForEach ($OU in $OusToSearch) {
$PwdExpUsersInOU = Get-ADUser -SearchBase $OU -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and Mail -like '*'} -Properties "DisplayName", "mail", "msDS-UserPasswordExpiryTimeComputed" | Where-Object {
$diff = New-TimeSpan -End ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")) -Start (Get-Date)
$diff.Days -le 14 -and $diff.Days -ge 0
} | Select-Object "DisplayName","Mail",#{Name="DaysLeft";Expression={$diff.Days}},#{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}
$PwdExpUsers += $PwdExpUsersInOU
}
Thanks to other askers and answerers here for lots of that code.
This part works. At this point I have an array $PwdExpUsers containing the relevant information.
Next I break this array apart into several smaller arrays in order to send tailored email addressed to each person that query returns with messages appropriate to how soon their passwords are expiring:
Note 1: This is currently testing code and filters for and only creates emails for those with passwords expiring tomorrow, as should be obvious from the code below. Once I get the addressing issue figured out I'll expand this to handle the rest of the array.
Note 2: The message content is HTML I've already stored in variables ($MsgTomorrow) earlier in the script. It works fine.
$Outlook = New-Object -ComObject Outlook.Application
$ExpTomorrow = $PwdExpUsers | Where-Object -FilterScript {$PSItem.DaysLeft -eq '0'}
foreach ($Account in $ExpTomorrow) {
$Mail = $Outlook.CreateItem(0)
$Mail.Importance = 2
$Mail.To = $PSItem.Mail
$Mail.Subject = "IMPORTANT: Your computer login password expires tomorrow"
$Mail.HTMLBody = $MsgTomorrow
$Mail.Save()
}
At the end of this I have emails in my Drafts folder with the correct subject and body, but no email address in the To field.
I assume I am failing to grasp how the $PSItem.Mail value is being passed along, and what $Mail.To expects to receive.
A valid solution would also remove the ForEach bit entirely and allow me to create a single email addresses to all members of the array $ExpTomorrow.
$PSItem -aka $_ ... This is the variable for the value that is running through the pipe line.
Try $Mail.To = $Account.Mail
If this doesn't do it please post example content of $ExpTomorrow
───────
You can also try it this way without outlook.
#
$smtp = "mail.server.com"
$from = "123abc#xyz.com"
$subject = "Email Subject Example - Password expiring!"
foreach($Account in $ExpTomorrow){
# All HTML will go into the body.
$body = "You are bad at maintaining your passwords, please do it now."
$to = $Account.Mail
# Now send the email using Send-MailMessage
send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -BodyAsHtml -Priority high
}

ACL "fuzzy" comparision

I'm trying to compare ACLs on a folder with a reference set of ACLs, and then list any exceptions. The "fuzzy" part of the equation is that I want to be able to disregard any unknown SID. So creating a reference folder with the perms I want to test won't work to use Compare-Object between it and my test folder.
The underlying scenario is that I am cleaning up old user directories where the actual user account has been deleted (this is where the non-resolved SID comes in). By default, the folders include perms for Administrator and the like, which I don't care about. There are some folders, however, where another user has been granted explicit permissions, and I want to capture these. Unfortunately, there aren't any shortcuts I can use to check: e.g. -IsInherited or the like to exclude ACLs I don't care about.
Per the below, I can dump the ACLs out into an array
$acl = get-acl f:\user_folder
$access = $acl.Access | ForEach-Object { $_.identityReference.value }
$access
BUILTIN\Administrators
MYDOMAIN\JBLOGGS
S-1-5-21-4444444444-9999999-1111111111-74390
MYDOMAIN\Domain_Group ###Yes, the group has an underscore in the name
I can create another array of the users I want to ignore, including a partial string to match any unresolved SID.
$defaults = #("BUILTIN\Administrators","MYDOMAIN\DomainGroup","S-1-5-21")
So how do I compare my $defaults array with the $access array and output only the exceptions like "MYDOMAIN\JBLOGGS"?
I'm trying a foreach, but I'm stumped about grabbing that exception. The following still outputs the SID I want to avoid. I'm hoping to also avoid too many nested "IFs".
$access | ForEach { If ($defaults -notcontains $_) { Write-Output $_ } }
MYDOMAIN\JBLOGGS
S-1-5-21-4444444444-9999999-1111111111-74390 #Do not want!
If I put the wildcard $_* into the -notcontains, I get the whole contents of $access again.
I'd do something like this:
$defaults = 'BUILTIN\Administrators', 'MYDOMAIN\DomainGroup', 'S-1-5-21*'
$acl.Access | Where-Object {
$id = $_.IdentityReference
-not ($defaults | Where-Object { $_ -like $id })
} | Select-Object -Expand value
$defaults | Where-Object { $_ -like $id } does a wildcard match of the given identity against all items of $defaults. The wildcard * at the end of S-1-5-21* allows to match all strings starting with S-1-5-21. The negation -not inverts the result so that only identities not having a match in $defaults pass the filter.
give the users you want to ignore some right on a dummy folder, get the acl of that folder and then compare whith the acl of your actual folder
$genericACL = get-acl c:\temp\dummy
$folderacl = get-acl f:\user_folder
$exceptions= $folderacl.Access.identityreference.value |?{ ($_ -notin $genericACL.access.identityreference.value) -and ($_.strartswith('S-1-5-21') -eq $false)) }
In the end, it was fairly simple, thanks to the help above.
I managed to omit the fact in the original question where I required it to work in Powershell v2.
$defaults = #("BUILTIN\Administrators","MYDOMAIN\DomainGroup")
$acl = get-acl $folder
$access = $acl.Access | ForEach-Object { $_.identityReference.value }
# check that no other account still has access to the folder
$access | ForEach {
If ($defaultACL -notcontains $_ -and $_ -notlike 'S-1-5-21*') {
write-output "Extra perms:$user $_"
}

How to change multiple users UPN suffix?

I'm preparing for a move to office365 and since we have the mydomain.local domain I need to add an alternative UPN (same as my SMTP namespace) so mydomain.com. I added the alternate UPN to my domain and now I want to change multiple users UPN at once.
I select multiple users > right click > properties > account > UPN suffix and select the UPN from the drop-down. When that's done I click OK or Apply and I get following error for all selected user:
The specified directory service attribute or value does not exist.
When I change it from one user it works without a problem.
My question now is, can someone help me solve tell me why this error is showing or what way I can achieve this.
Thanks
You can try http://admodify.codeplex.com/.
There is an article showing an example of its uage here: http://blogs.technet.com/exchange/archive/2004/08/04/208045.aspx
Use the following powershell scripts. Change "contoso.local" to your actual domain name.
$localUsers = Get-ADUser -Filter {UserPrincipalName -like "contoso.local"} -Properties UserPrincipalName -ResultSetSize $null
$localUsers | foreach { $newUpn = $_.UserPrincipalName.Replace("contoso.local", "yourdomain.com"; $_ | Set-ADUser -UserPrincipalName $newUpn}
It is best to use a script to change bulk users rather than using the method you mentioned.
You can use either a PowerShell script (recommended) or a VBScript for this.
PowerShell script (using a CSV file):
http://gallery.technet.microsoft.com/Change-UPN-592177ea
PowerShell script (for all users in an OU searchbase):
http://community.spiceworks.com/scripts/show/1457-mass-change-upn-suffix
VBScript:
http://blogs.technet.com/b/heyscriptingguy/archive/2004/12/06/how-can-i-assign-a-new-upn-to-all-my-users.aspx?Redirected=true

Powershell - compare Active Directory usernames with e-mail address

I am currently trying to find all AD users that have been created using the model "firstname#domain.com" versus our new standard of "FirstInitialLastName#domain.com". I'm using the Quest ActiveRoles modules, specifically Get-QADUser to pull down my user details:
Get-QADUser -enabled -IncludedProperties PrimarySMTPAddress | ?{$_.Type -match "User"} | Select-Object FirstName,PrimarySMTPAddress ...
That gets me a list of user first names and their SMTP address. Where I am stumped is how to compare the results.
I thought normalizing the values (either adding "#domain.com" to the first name string or stripping "#domain.com" from the SMTP string) and then doing a -ieq test would be the best approach. I have found I can do the first with:
%{ $address=$($_.FirstName + "#domain.com";) }
But I can't figure out how to then test $address against the PrimarySMTPAddress string. I can create a second variable with:
%{ $smtp=$($_.PrimarySMTPAddress); }
and get the result:
[PS] C:\>$addy -ieq $smtp
True
I'm just unclear how to do it all in stream so that I can process my tree at once. If this is something that's just more suited to a script than a single line, that's fine too. Coming from the glorious world of BASH my brain just wanted to one-line it.
Get-QADUser -Enabled -Email * -SizeLimit 0 |
Where-Object {$_.Email.Split('#')[0] -eq $_.FirstName }
Try this:
Get-QADUser -enable -IncludedProperties PrimarySMTPAddress |
? { $_.PrimarySMTPAddress -match ("^"+[regex]::escape("$($_.firstname)")+"#domain.com") }

Resources