PowerShell Set Groups Custom Attributes

Scenario:

Powershell Set Groups Custom Attributes

The script that we will see today is an updated version of an older post that was used to set groups custom attributes based on which Organizational Unit is under. The reason of the creation of such script was to ensure that delegation of permissions on Exchange Online will work without any issue. Delegation on Exchange Online has been provided on local It administrators using some filters. Part of that filter is to use CustomAttribute4 of distribution and security groups in order to provide administration access to them. If the value is not correct then they are not able to administer those objects.

Problems of initial version

The previous version of the script, was not so flexible and every time there was a change I have to check the script that I am not missing anything. Let’s see the problem below:

Static Arrays

When there was a change on Organizational Units in Active Directory I had to check all the respective arrays that is correct. There is a possibility to miss an Organizational Unit. If you miss one then the custom attribute will not be set and local IT will not be able to administer the groups below that Organizational Unit.

Old code:

$CYGroups = @("domain.com/Offices/Cyprus/Groups/DistributionGroups",` "domain.com/Offices/Cyprus/Groups/SecurityGroups") $CNGroups = @("domain.com/Offices/China/Groups/DistributionGroups",` "domain.com/Offices/China/Groups/SecurityGroups") $DEGroups = @("domain.com/Offices/Germany/Groups/DistributionGroups",` "domain.com/Offices/Germany/Groups/SecurityGroups") $GRGroups = @("domain.com/Offices/Greece/Groups/DistributionGroups",` "domain.com/Offices/Greece/Groups/SecurityGroups") $INGroups = @("domain.com/Offices/India/Groups/DistributionGroups",` "domain.com/Offices/India/Groups/SecurityGroups") $UKGroups = @("domain.com/Offices/UnitedKingdom/Groups/DistributionGroups",` "domain.com/Offices/UnitedKingdom/Groups/SecurityGroups")

[adinserter name=”In Article”]

Multiple ElseIf

In the previous version, I was using multiple elseif in order to check the current value of the custom attribute and change it accordingly. This resulted in to have a lot of code, that a big part of it was duplicate. In this version I have replaced it with a switch statement. The code is less and clearer to read.

Old Code:

if (($CYGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "Cyprus")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "Cyprus" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>Cyprus</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } elseif (($CNGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "China")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "China" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>China</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } elseif (($DEGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "Germany")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "Germany" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>Germany</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } elseif (($GRGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "Greece")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "Greece" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>Greece</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } elseif (($INGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "India")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "India" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>India</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } elseif (($UKGroups -contains $Group.OrganizationalUnit) -and ($Group.CustomAttribute4 -ne "United Kingdom")){ Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -CustomAttribute4 "United Kingdom" -ForceUpgrade $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>United Kingdom</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp }

[adinserter name=”In Article”]

Backtick

Previously there was use of backtick to present the code in a more readable way. Now, bakcticking has been removed and I have used the splatting and normal command line continuation where is applicable.

Old Code:

$CYGroups = @("domain.com/Offices/Cyprus/Groups/DistributionGroups",` "domain.com/Offices/Cyprus/Groups/SecurityGroups") send-mailmessage ` -To $To ` -Subject "Distribution Groups CustomAttribute4 Report $(Get-Date -format dd/MM/yyyy)" ` -Body $Email ` -BodyAsHtml ` -Priority high ` -UseSsl ` -Port 587 ` -SmtpServer 'smtp.office365.com' ` -From $From ` -Credential $EmailCredentials

In order to make overcome the above issue that I had, I have made changes on my script, so the arrays will be dynamic, and if there is any addition in Organizational Units, I will not need to edit my script. It runs faster than before, it is smaller and easier to read.

How it works

The script is setup to work on a hybrid environment. In Hybrid environment all federated groups should be administered from Exchange On-Prem. First the script will connect to Exchange On-Prem so we will have the related cmdlets available. Later a few variables are defined in order to be used later to send the email report at the end of the script. After that it will retrieve all Organizational Units from Active Directory based on the filter that we define. In the script below the filter will keep only the Organizational Units that include Group and Distribution or Security in their Canonical Name. So we have two combinations, the first one is Group and Distribution and the second one is Group and Security. All groups are saved in a variable and then we filter that variable in order to create one array for each location. One more array is created later containing all groups in our system. Then the script will process that array

[adinserter name=”In Article”]

You can download the script here or copy it from below. (Note that code within the script might not be copied correctly due to syntax highlighting.)

Hope you like it.

You feedback is appreciated.

If you have any questions or anything else please let me know in the comments below.

Related Links:

[adinserter name=”In Article”]

Solution / Script:

<# .SYNOPSIS Name: Set-GroupCustomAttribute.ps1 The purpose of this script is to set CustomAttribute4 of groups into a specific one based on their Organizational Unit in Active Directory. .DESCRIPTION This is a script that collects all groups in Active Direcotry and sets CustomAttribute4 of the group to the desired one based on Organizational Unit and the responsible IT. Report is sent with the changes applied if there are any. An error email report will also be sent if there are any errors duing run. .RELATED LINKS Home .NOTES Version: 1.1 Update: - Converted static array of groups to dynamic - Converted static arrays of OUs to dynamic - Code improvements Release Date: 30-09-2016 Author: Stephanos Constantinou .EXAMPLE Run the script Set-GroupCustomAttribute.ps1 #> $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange-server.domain.com/PowerShell/ -Authentication Kerberos Import-PSSession $Session $File = "C:\Scripts\Password.txt" $Key = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32) $EmailUser = "[email protected]" $Password = Get-Content $File | ConvertTo-SecureString -Key $Key $EmailCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $EmailUser,$Password $To = '[email protected]' $From = '[email protected]' $EmailResult = "" $AllGroupsOUs = (Get-ADOrganizationalUnit -Properties * -Filter * | Where-Object {($_.CanonicalName -like "*Groups*") -and (($_.CanonicalName -like "*DistributionGroups") -or ($_.CanonicalName -like "*SecurityGroups"))}).CanonicalName $CyprusGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*Cyprus*"} $GreeceGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*Greece*"} $GermanyGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*Germany*"} $ChinaGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*China*"} $UnitedKingdomGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*UnitedKingdom*"} $IndiaGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*India*"} $DCGroupsOUs = $AllGroupsOUs | Where-Object {$_ -like "*Datacenter*"} $AllGroups = Get-DistributionGroup -OrganizationalUnit "domain.com/Offices" -ResultSize Unlimited $EmailResult = "" foreach ($Group in $AllGroups){ if ($Group.MemberJoinRestriction -eq "Open") {Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -MemberJoinRestriction "Closed" -ForceUpgrade} if ($Group.MemberDepartRestriction -eq "Open") {Set-DistributionGroup -Identity $Group.PrimarySmtpAddress -MemberDepartRestriction "Closed" -ForceUpgrade} Switch -Regex ($Group){ {$CYGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "Cyprus";Break} {$ChinaGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "China";Break} {$GermanyGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "Germany";Break} {$GreeceGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "Greece";Break} {$IndiaGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "India";Break} {$UnitedKingdomGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "UnitedKingdom";Break} {$DCGroupsOUs -contains $Group.OrganizationalUnit}{ $Attribute4Value = "Frankfurt";Break} } if ($Group.CustomAttribute4 -ne $Attribute4Value){ $GroupPrimarySmtpAddress = $Group.PrimarySmtpAddress $GroupParams = @{ Identity = $GroupPrimarySmtpAddress CustomAttribute4 = $Attribute4Value ForceUpgrade = $true} Set-DistributionGroup @GroupParams $EmailTemp = @" <tr> <td class="colorm">$Group</td> <td>$Attribute4Value</td> </tr> "@ $EmailResult = $EmailResult + "`r`n" + $EmailTemp } } $EmailUp = @" <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0D%0A%0D%0Abody%20%7B%20font-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20color%3A%23434242%3B%7D%0D%0ATABLE%20%7B%20font-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20border-width%3A%201px%3Bborder-style%3A%20solid%3Bborder-color%3A%20black%3Bborder-collapse%3A%20collapse%3B%7D%0D%0ATR%20%7Bborder-width%3A%201px%3Bpadding%3A%2010px%3Bborder-style%3A%20solid%3Bborder-color%3A%20white%3B%20%7D%0D%0ATD%20%7Bfont-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20border-width%3A%201px%3Bpadding%3A%2010px%3Bborder-style%3A%20solid%3Bborder-color%3A%20white%3B%20background-color%3A%23C3DDDB%3B%7D%0D%0A.colorm%20%7Bbackground-color%3A%2358A09E%3B%20color%3Awhite%3B%7D%0D%0A.colort%7Bbackground-color%3A%2358A09E%3B%20padding%3A20px%3B%20color%3Awhite%3B%20font-weight%3Abold%3B%7D%0D%0A.colorn%7Bbackground-color%3Atransparent%3B%7D%0D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<style>" title="<style>" /> <body> <h3>Script has been completed successfully</h3> <h4>CustomAttribute4 has been set for the below groups</h4> <table> <tr> <td class="colort">Group Name</td> <td class="colort">Custom Attribute4 Value</td> </tr> "@ $EmailDown = @" </table> </body> "@ $Email = $EmailUp + $EmailResult + $EmailDown if ($EmailResult -ne ""){ $EmailParams = @{ To = $To Subject = "Distribution Groups CustomAttribute4 Report $(Get-Date -format dd/MM/yyyy)" Body = $Email BodyAsHtml = $True Priority = "High" UseSsl = $True Port = "587" SmtpServer = "smtp.office365.com" Credential = $EmailCredentials From = $From} send-mailmessage @EmailParams } if ($error -ne $null){ foreach ($value in $error){ $ErrorEmailTemp = @" <tr> <td class="colorm">$value</td> </tr> "@ $ErrorEmailResult = $ErrorEmailResult + "`r`n" + $ErrorEmailTemp } $ErrorEmailUp = @" <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0D%0Abody%20%7Bfont-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20color%3A%23434242%3B%7D%0D%0ATABLE%20%7Bfont-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20border-width%3A%201px%3Bborder-style%3A%20solid%3Bborder-color%3A%20black%3Bborder-collapse%3A%20collapse%3B%7D%0D%0ATR%20%7Bborder-width%3A%201px%3Bpadding%3A%2010px%3Bborder-style%3A%20solid%3Bborder-color%3A%20white%3B%20%7D%0D%0ATD%20%7Bfont-family%3ASegoe%2C%20%22Segoe%20UI%22%2C%20%22DejaVu%20Sans%22%2C%20%22Trebuchet%20MS%22%2C%20Verdana%2C%20sans-serif%20!important%3B%20border-width%3A%201px%3Bpadding%3A%2010px%3Bborder-style%3A%20solid%3Bborder-color%3A%20white%3B%20background-color%3A%23C3DDDB%3B%7D%0D%0A.colorm%20%7Bbackground-color%3A%2358A09E%3B%20color%3Awhite%3B%7D%0D%0Ah3%20%7Bcolor%3A%23BD3337%20!important%3B%7D%0D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<style>" title="<style>" /> <body> <h3 style="color:#BD3337 !important;"> WARNING!!!</h3> <p>There were errors while setting up CustomAttribute4 on Groups</p> <p>Please check the errors and act accordingly</p> <table> "@ $ErrorEmailDown = @" </table> </body> "@ $ErrorEmail = $ErrorEmailUp + $ErrorEmailResult + $ErrorEmailDown $ErrorEmailParams = @{ To = $To Subject = "Distribution Groups CustomAttribute4 Report $(Get-Date -format dd/MM/yyyy) - WARNING" Body = $ErrorEmail BodyAsHtml = $True Priority = "High" UseSsl = $True Port = "587" SmtpServer = "smtp.office365.com" Credential = $EmailCredentials From = $From} send-mailmessage @ErrorEmailParams }

[adinserter name=”Matched-Content”]