Part of a script that I'm working on grabs users from AD passes to the variable $TargetUsers
This variable is then passed on to the following to change the UPN suffix for each user:
$OldSuffix = "#olddomain.com"
$NewSuffix = "#newdomain.com"
Foreach ($User3 in $TargetUsers) {
$Upn3 = $User3.UserPrincipalName
$NewUpn = $User3.UserPrincipalName.Replace($OldSuffix,$NewSuffix)
Get-ADUser -Filter "UserPrincipalName -eq '$Upn3'" | Set-ADuser `
-Remove #{proxyAddresses=#("SMTP:$($User3.givenName).$($User3.sn)$OldSuffix","sip:$($User3.givenName).$($User3.sn)$OldSuffix")} `
-Add #{proxyAddresses=#("SMTP:$($User3.givenName.ToLower()).$($User3.sn.ToLower())$NewSuffix","smtp:$($User3.givenName.ToLower()).$($User3.sn.ToLower())$OldSuffix","sip:$($User3.givenName.ToLower()).$($User3.sn.ToLower())$NewSuffix")} `
-Replace #{ co = "United Kingdom" } `
-Email "$($User3.givenName.ToLower()).$($User3.sn.ToLower())$NewSuffix" `
-UserPrincipalName $NewUpn
$NewUpn2 = Get-ADuser -Filter "UserPrincipalName -eq '$NewUpn'" | Select UserPrincipalName
"$(Get-Date -f HH:mm:ss): $($Upn3): AD Attributes updated & UPN Suffix changed to $NewUpn2" | Tee-Object $UserMigrationLog -Append
}
This outputs to host and log file "...UPN Suffix changed to #{UserPrincipalName=Firstname.surname#newdomain.com}"
How can I get this to exclude the "#{UserPrincipalName=" & trailing "}" at the end?
Ah sorted it.
$NewUpn2 = Get-ADuser -Filter "UserPrincipalName -eq '$NewUpn'" | % {$_.UserPrincipalName}
Did the trick.
Related
I want to replace AD attribute "userPrincipalName" value according to CSV file header value
here is what csv file(group.csv) contains
sAMAccountName
--------------
test.user1
test.user2
below the script
$data = Import-Csv -Path .\group.csv -Header 'sAMAccountName'
foreach($user in $data){
Get-ADUser -Filter {sAMAccountName -eq "$($user.sAMAccountName)"} | Set-ADUser -Replace #{userPrincipalName="$($user.sAMAccountName)#RES.GROUP"}
}
here I want to replace AD attribute "userPrincipalName" with the value of sAMAccountName from csv file, something like sAMAccountName#RES.GROUP
this script does not work, can anyone please correct it?
Ok, since your comment shows the CSV file indeed does not have a header, I would suggest changing the code to:
$data = Import-Csv -Path .\group.csv -Header 'sAMAccountName'
foreach($user in $data) {
$adUser = Get-ADUser -Filter "SamAccountName -eq '$($user.sAMAccountName)'" -ErrorAction SilentlyContinue
if ($adUser) {
$newUPN = '{0}#res.group' -f $user.sAMAccountName
$adUser | Set-ADUser -UserPrincipalName $newUPN
}
else {
Write-Warning "No user with SamAccountName '$($user.sAMAccountName)' could be found.."
}
}
This way, any mistakes in the file will not make the code quit when a user with that samaccountname cannot be found. Instead, in that case you will see a warning about it and the code will continue with the rest of the data.
It might be worth mentioning that you can use parameter -Server on both the Get-ADUser and Set-ADUser cmdlets to make sure you use the same domain server (DC) to set the new UPN. Otherwise, you can set it on one DC, but are looking at another which doesn't show the change immediately because the servers need time to synchronize..
Now that we have cleared up the question about the CSV and to answer your comment:
If you want to do this as a two-script solution, here's how you can do that
step 1: get all users in the search OU that have a UserPrincipalName ending in '*#test.group'
$searchBase = "OU=Teams,OU=Prod,DC=RES,DC=TEST,DC=GROUP"
Get-ADUser -SearchBase $searchBase -Filter "UserPrincipalName -like '*#test.group'" |
# select ony the SamAccountName and write to CSV with column header
Select-Object SamAccountName | Export-Csv -Path .\group.csv -NoTypeInformation
step 2: read the csv created above and
$searchBase = "OU=Teams,OU=Prod,DC=RES,DC=TEST,DC=GROUP"
$data = Import-Csv -Path .\group.csv
$result = foreach($user in $data) {
$adUser = Get-ADUser -SearchBase $searchBase -Filter "SamAccountName -eq '$($user.sAMAccountName)'" -ErrorAction SilentlyContinue
# if we have a user object AND its UserPrincipalName is not as desired go ahead and change that
if ($adUser) {
if ($adUser.UserPrincipalName -notlike '*#res.test.group') {
$newUPN = '{0}#res.test.group' -f $user.sAMAccountName
$adUser | Set-ADUser -UserPrincipalName $newUPN
# output this user object to be collected in variable $result
$adUser
}
else {
Write-Host "User $($user.sAMAccountName) already has UPN '$($adUser.UserPrincipalName)'"
}
}
else {
Write-Warning "User with SamAccountName '$($user.sAMAccountName)' not found.."
}
}
# now that we have changed some users, create a second csv with all users that were actually changed
if (#($result).Count) {
$result | Select-Object SamAccountName | Export-Csv -Path .\Updatedgroup.csv -NoTypeInformation
}
else {
Write-Host 'No users needed updating'
}
It seems a waste writing only the users SamAccountName property to the csv files.. Especially since Get-ADUser by default already returns these properties: DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, UserPrincipalName
Ok so I'm trying to automate the creation of security groups under a specific OU. The keystone is group membership, so basically If member of group A -> create group B.
So I'm filling a variable with the name attribute of an "All Managers" group. Then I want to loop through those $names and check for the existence of another group using {Name -like 'Reports to $name'}
The looping part is throwing me off, here's what I've got:
import-module activedirectory
$ou = "ou=Stuff, ou=Resources, ou=Groups, ou=Org, dc=domain, dc=net"
$creds = "domain.net\user"
$server = "<domain controller>"
$managers = get-adGroupMember -server $server -Credential $creds -identity "All Managers" | select name
$name = $managers.name
$ReportsTo = Get-adgroup -server $server -credential $creds -searchbase $ou -filter "Name -like 'Reports to *'" | where {$_.name -replace 'Reports to ' -in $name} | select name
$Reports = $reportsto.name
$reports
ForEach ($manager in $managers){
If ($ReportsTo -NotContains $name)
#{$name}
{New-ADGroup -name "Reports to "$name -groupscope Global -path $ou}
}
it's ugly I know, but like i said I'm not sure how to process the loop, they are still new to me.
You may do the following:
$ou = 'ou=Stuff,ou=Resources,ou=Groups,ou=Org,dc=domain,dc=net'
$creds = "domain.net\user"
$server = "<domain controller>"
# Get Manager names only
$managers = Get-ADGroupMember -Server $server -Credential $creds -Identity 'All Managers' |
Select-Object -ExpandProperty Name
# Get Group names that are in the format Reports to Manager Name
$ReportsTo = Get-ADGroup -Server $server -Credential $creds -SearchBase $ou -Filter "Name -like 'Reports to *'" |
where {$_.Name -replace 'Reports to ' -in $managers} |
Select-Object -ExpandProperty Name
# Find manager names that don't have a Reports to group
Compare-Object $managers ($ReportsTo -replace 'Reports to ') -PassThru | Foreach-Object {
# Pipe found manager names into New-ADGroup command's -Name parameter
New-ADGroup -Name "Reports to $_" -GroupScope Global -Path $ou -Server $server -Credential $creds
}
Compare-Object by default only lists differences.
Been a while since I posted, but i've hit a road-block which an annoying issue
I have a need to scan all of the companies domains for user accounts based on full name, as is FIRST LAST
The same code works fine when running a get-aduser -identity -server domain.name, but using
Get-aduser -filter -server doesn't work inside a For loop, and I'm not sure why!
Here's the code:
$AllDomains = (Get-ADForest).domains
Function Check-ADUser {
Param(
$FullName,
$ADList
)
$ADUserArray = #()
ForEach ($SubDomain in $ADList) {
write-host "Checking for $FullName on $SubDomain ..."
$UserADDomain = Get-ADUser -Server $SubDomain -Filter {(Name -eq $Fullname)} -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}} `
| Select-Object DomainName -ExpandProperty DomainName
} #ForEach $Domain
The results return black
Here's the code that work fine:
$AllDomains = (Get-ADForest).domains
Function Check-ADUser {
Param(
$FullName,
$ADList
)
$ADUserArray = #()
ForEach ($SubDomain in $ADList) {
write-host "Checking for $FullName on $SubDomain ..."
$UserADDomain = Get-ADUser -Server $SubDomain -Identity $userName -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}} `
| Select-Object DomainName -ExpandProperty DomainName
} #ForEach $Domain
The function is called via a for loop against each user as such
$Users = #"
Rocky Balboa
Bruce Willis
Gene Simmons
Liz Phair
Steven Segal
"# | ForEach {$_.Split([String[]]"`r`n",[StringSplitOPtions]::None)}
$outarray = #()
ForEach ($user in $Users) {
$aa = Check-ADUser -FullName $User -ADList $AllDomains
}
The only real difference in the code within the function, is the use the -filter instead of -identity on the get-aduser cmdlet
What's odd, is that if I run the code outside of the for loop, it works! I'm thinking it's a Powershell gotcha! any help appreciated :-)
Owen
Use the filter statement like this,
If you are interested in performance, limit the properties to canonicalName instead of *.
After reading the last part of the docs, I think removing the ( ) within curly braces should resolve your issue as well.
$UserADDomain = Get-ADUser -Server $SubDomain -Filter "Name -eq '$Fullname'" -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}}
if ($null -ne $UserADDomain) {
return $UserADDomain
}
See Microsoft docs on Filter
Excerpt:
Note: For String parameter type, PowerShell will cast the filter query to a string while processing the command. When using a string variable as a value in the filter component, make sure that it complies with the PowerShell Quoting Rules. For example, if the filter expression is double-quoted, the variable should be enclosed using single quotation marks: Get-ADUser -Filter "Name -like '$UserName'". On the contrary, if curly braces are used to enclose the filter, the variable should not be quoted at all: Get-ADUser -Filter "Name -like '$UserName'".
How can I output the screen results to a txt file when I run this code?
#removes disabled clinical or corp accounts from SGs in the I-Drive OU
$searchOU = "OU=I-Drive,OU=SAS,OU=Application Security Groups,OU=Groups,OU=Enterprise,DC=z,DC=x,DC=y"
Get-ADGroup -Filter 'GroupCategory -eq "Security"' -SearchBase $searchOU | ForEach-Object{ $group = $_
Get-ADGroupMember -Identity $group | Get-ADUser | Where-Object {$_.Enabled -eq $false} | ForEach-Object{ $user = $_
$uname = $user.Name
$gname = $group.Name
Write-Host "Removing $uname from $gname" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $user -Confirm:$false
}
}
Pipe the output of Get-ADGroup to Set-Content like so:
Get-ADGroup -Filter 'GroupCategory -eq "Security"' -SearchBase $searchOU | ForEach-Object{
$group = $_
Get-ADGroupMember -Identity $group | Get-ADUser | Where-Object { $_.Enabled -eq $false} | ForEach-Object{
$user = $_
$uname = $user.Name
$gname = $group.Name
Write-Host "Removing $uname from $gname" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $user -Confirm:$false
}
} | Set-Content filename.txt
If you want any additional output (warnings, verbose, errors) change the last line a bit to redirect the other streams:
} *>&1 | Set-Content filename.txt
Alternatively, you can also use the built-in transcript logging to log everything to a file as well just call one of the following from within your script:
Start-Transcript
or if you want the log to go to a specific place:
Start-Transcript -Path "\Path\To\LogFile.log"
Note that transcript logging is more useful in scripts than during an interactive session.
Store the value you'd like to log in a variable say $abc
Write to log file and keep appending $abc | Out-File -FilePath "C:\Somewhere\log.txt" -Append -Encoding UTF8
Refer - Log output of ForEach loop
I have a task to get userPrincipalName attribute from users who are in several groups in our multiple-domain AD forest.
The problem is that I can't use Select-Object to get a user's UPN from Get-ADGroupMember because this cmdlet only returns a limited number of properties (samaccountname, name, SID and DN), and UPN isn't one of them.
I wrote this code (get "name" and than search UPN by "name"):
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
$LocalSite = (Get-ADDomainController -Discover).Site
$NewTargetGC = Get-ADDomainController -Discover -Service 6 -SiteName
$LocalSite
IF (!$NewTargetGC)
{ $NewTargetGC = Get-ADDomainController -Discover -Service 6 -NextClosestSite }
$NewTargetGCHostName = $NewTargetGC.HostName
$LocalGC = “$NewTargetGCHostName” + “:3268”
$domains = (Get-ADForest).domains
$MembersOfSFDC_Groups = foreach ($domain in $domains) {
$Group = Get-ADGroup -Filter { Name -like "*groupname*" } -Server $Domain
$Group | Get-ADGroupMember -Server $domain | Select #{
Name="Domain";Expression={$Domain}},#{
Name="Group";Expression={$Group.Name}}, name}
$DisplayNames = $MembersOfSFDC_Groups.name
$DisplayNames |Out-file (Join-Path $ScriptPath 'DisplayNames.txt')
Get-content (Join-Path $ScriptPath 'DisplayNames.txt') |
$displaynames | ForEach-Object {
Get-ADUser -Server $LocalGC -Filter {Name -eq $_} |
Select-Object -Property userPrincipalName} |
Out-File (Join-Path $ScriptPath 'upnOfSDFC_AD_GroupsMembers.txt')
But next problem is that this code is running about 30 min (Measure-Command cmdlet). We have a huge number of users across multiple domains.
My question is how to improve my code to get user's UPN more faster?
I know about System.DirectoryServices.DirectorySearcher, but don't know how to implementing this method with my txt-file (list of "names").
Any help will be much appreciated.
You can actually get it from one line of code. Simples... :)
Get-ADGroupMember -Identity "group name" |%{get-aduser $_.SamAccountName | select userPrincipalName } > c:\scripts\upnofADgroup.txt
Fastest approach is probably avoiding Get-ADGroupMember altogether, and just search for the group, and then search for objects that are members of that group:
$Group = Get-ADGroup -Filter { Name -like "*groupname*" } -Server $Domain
$Members = Get-ADObject -LDAPFilter "(memberOf=$($Group.DistinguishedName))" -Properties UserPrincipalName
$Members |Select-Object UserPrincipalName |Out-File (Join-Path $ScriptPath 'upnOfSDFC_AD_GroupsMembers.txt')
Now you're down to 2 queries, rather than 2 + N (where N is the number of members)
Ok, guys, I'v got it:
function Get-DomainFromDN ($param)
{
$dn1 = $param -split "," | ? {$_ -like "DC=*"}
$dn2 = $dn1 -join "." -replace ("DC=", "")
$script:test = $dn2
return $dn2
}
foreach ($Group in $Groups) {
$Members = Get-ADObject -LDAPFilter "(&(objectCategory=user)(memberOf=$($Group.DistinguishedName)))" -Properties UserPrincipalName -Server (Get-DomainFromDN ($group.DistinguishedName))
$UPN_Of_SFDC_Groups += $Members |Select-Object UserPrincipalName }
$UPN_Of_SFDC_Groups | Out-file (Join-Path $ScriptPath 'upnOfSDFC_AD_GroupsMembers.txt')