AADConnectDsc.psm1
#Region '.\Prefix.ps1' -1 $script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common' Import-Module -Name $script:dscResourceCommonModulePath #EndRegion '.\Prefix.ps1' 3 #Region '.\Enum\AttributeMappingFlowType.ps1' -1 enum AttributeMappingFlowType { Direct Constant Expression } #EndRegion '.\Enum\AttributeMappingFlowType.ps1' 7 #Region '.\Enum\AttributeValueMergeType.ps1' -1 enum AttributeValueMergeType { Update Replace MergeCaseInsensitive Merge } #EndRegion '.\Enum\AttributeValueMergeType.ps1' 8 #Region '.\Enum\ComparisonOperator.ps1' -1 enum ComparisonOperator { EQUAL NOTEQUAL LESSTHAN LESSTHAN_OR_EQUAL CONTAINS NOTCONTAINS STARTSWITH NOTSTARTSWITH ENDSWITH NOTENDSWITH GREATERTHAN GREATERTHAN_OR_EQUAL ISNULL ISNOTNULL ISIN ISNOTIN ISBITSET ISBITNOTSET ISMEMBEROF ISNOTMEMBEROF } #EndRegion '.\Enum\ComparisonOperator.ps1' 23 #Region '.\Enum\Ensure.ps1' -1 enum Ensure { Absent Present Unknown } #EndRegion '.\Enum\Ensure.ps1' 6 #Region '.\Classes\AADConnectDirectoryExtensionAttribute.ps1' -1 [DscResource()] class AADConnectDirectoryExtensionAttribute { [DscProperty(Key = $true)] [string]$Name [DscProperty(Key = $true)] [string]$AssignedObjectClass [DscProperty(Mandatory = $true)] [string]$Type [DscProperty(Mandatory = $true)] [bool]$IsEnabled [DscProperty()] [Ensure] $Ensure AADConnectDirectoryExtensionAttribute() { $this.Ensure = 'Present' } [bool]Test() { $currentState = Convert-ObjectToHashtable -Object $this.Get() $desiredState = Convert-ObjectToHashtable -Object $this if ($currentState.Ensure -ne $desiredState.Ensure) { return $false } if ($desiredState.Ensure -eq [Ensure]::Absent) { return $true } $compare = Test-DscParameterState -CurrentValues $currentState -DesiredValues $desiredState -TurnOffTypeChecking -SortArrayValues return $compare } [AADConnectDirectoryExtensionAttribute]Get() { $currentState = [AADConnectDirectoryExtensionAttribute]::new() $attribute = Get-AADConnectDirectoryExtensionAttribute -Name $this.Name -ErrorAction SilentlyContinue | Where-Object { $_.AssignedObjectClass -eq $this.AssignedObjectClass -and $_.Type -eq $this.Type } $currentState.Ensure = [Ensure][int][bool]$attribute $CurrentState.Name = $this.Name $currentState.AssignedObjectClass = $this.AssignedObjectClass $currentState.Type = $attribute.Type $currentState.IsEnabled = $attribute.IsEnabled return $currentState } [void]Set() { $param = Convert-ObjectToHashtable $this if ($this.Ensure -eq 'Present') { $cmdet = Get-Command -Name Add-AADConnectDirectoryExtensionAttribute $param = Sync-Parameter -Command $cmdet -Parameters $param Add-AADConnectDirectoryExtensionAttribute @param -Force } else { $cmdet = Get-Command -Name Remove-AADConnectDirectoryExtensionAttribute $param = Sync-Parameter -Command $cmdet -Parameters $param Remove-AADConnectDirectoryExtensionAttribute @param } } } #EndRegion '.\Classes\AADConnectDirectoryExtensionAttribute.ps1' 79 #Region '.\Classes\AADSyncRule.ps1' -1 [DscResource()] class AADSyncRule { [DscProperty(Key = $true)] [string]$Name [DscProperty()] [string]$Description [DscProperty()] [bool]$Disabled [DscProperty(NotConfigurable)] [string]$Identifier [DscProperty(NotConfigurable)] [string]$Version [DscProperty()] [ScopeConditionGroup[]]$ScopeFilter [DscProperty()] [JoinConditionGroup[]]$JoinFilter [DscProperty()] [AttributeFlowMapping[]]$AttributeFlowMappings [DscProperty(Mandatory = $true)] [string]$ConnectorName [DscProperty(NotConfigurable)] [string]$Connector [DscProperty()] [int]$Precedence [DscProperty()] [string]$PrecedenceAfter [DscProperty()] [string]$PrecedenceBefore [DscProperty(Mandatory = $true)] [string]$TargetObjectType [DscProperty(Mandatory = $true)] [string]$SourceObjectType [DscProperty(Mandatory = $true)] [string]$Direction [DscProperty(Mandatory = $true)] [string]$LinkType [DscProperty()] [bool]$EnablePasswordSync [DscProperty()] [string]$ImmutableTag [DscProperty()] [bool]$IsStandardRule [DscProperty(NotConfigurable)] [bool]$IsLegacyCustomRule [DscProperty()] [Ensure]$Ensure AADSyncRule() { $this.Ensure = 'Present' } [bool]Test() { $currentState = Convert-ObjectToHashtable -Object $this.Get() $desiredState = Convert-ObjectToHashtable -Object $this if ($currentState.Ensure -ne $desiredState.Ensure) { return $false } if ($desiredState.Ensure -eq [Ensure]::Absent) { return $true } $param = @{ CurrentValues = $currentState DesiredValues = $desiredState TurnOffTypeChecking = $true SortArrayValues = $true } $param.ExcludeProperties = if ($this.IsStandardRule) { $this.GetType().GetProperties().Name -notin 'Name', 'IsDisabled', 'Connector' } else { 'Precedence', 'Version', 'Identifier', 'Connector', 'IsStandardRule', 'IsLegacyCustomRule' } $compare = Test-DscParameterState @param -ReverseCheck return $compare } [AADSyncRule]Get() { $syncRule = Get-ADSyncRule -Name $this.Name $currentState = [AADSyncRule]::new() $currentState.Name = $this.Name if ($syncRule.Count -gt 1) { Write-Error "There is more than one sync rule with the name '$($this.Name)'." $currentState.Ensure = 'Unknown' return $currentState } $currentState.Ensure = [Ensure][int][bool]$syncRule $currentState.ConnectorName = (Get-ADSyncConnector | Where-Object Identifier -EQ $syncRule.Connector).Name $currentState.Connector = $syncRule.Connector $currentState.Description = $syncRule.Description $currentState.Disabled = $syncRule.Disabled $currentState.Direction = $syncRule.Direction $currentState.EnablePasswordSync = $syncRule.EnablePasswordSync $currentState.Identifier = $syncRule.Identifier $currentState.LinkType = $syncRule.LinkType $currentState.Precedence = $syncRule.Precedence foreach ($scg in $syncRule.ScopeFilter) { $scg2 = [ScopeConditionGroup]::new() foreach ($sc in $scg.ScopeConditionList) { $sc2 = [ScopeCondition]::new($sc.Attribute, $sc.ComparisonValue, $sc.ComparisonOperator) $scg2.ScopeConditionList += $sc2 } $currentState.ScopeFilter += $scg2 } foreach ($jcg in $syncRule.JoinFilter) { $jcg2 = [JoinConditionGroup]::new() foreach ($jc in $jcg.JoinConditionList) { $jc2 = [JoinCondition]::new($jc.CSAttribute, $jc.MVAttribute, $jc.CaseSensitive) $jcg2.JoinConditionList += $jc2 } $currentState.JoinFilter += $jcg2 } foreach ($af in $syncRule.AttributeFlowMappings) { $af2 = [AttributeFlowMapping]::new() $af2.Source = $af.Source[0] $af2.Destination = $af.Destination $af2.ExecuteOnce = $af.ExecuteOnce $af2.FlowType = $af.FlowType $af2.ValueMergeType = $af.ValueMergeType if ($af.Expression) { $af2.Expression = $af.Expression } $currentState.AttributeFlowMappings += $af2 } $currentState.SourceObjectType = $syncRule.SourceObjectType $currentState.TargetObjectType = $syncRule.TargetObjectType $currentState.Version = $syncRule.Version $currentState.IsStandardRule = $syncRule.IsStandardRule $currentState.IsLegacyCustomRule = $syncRule.IsLegacyCustomRule return $currentState } [void]Set() { $this.Connector = (Get-ADSyncConnector | Where-Object Name -EQ $this.ConnectorName).Identifier $existingRule = Get-ADSyncRule -Name $this.Name $this.Identifier = if ($existingRule) { $existingRule.Identifier } else { New-Guid2 -InputString $this.Name } $allParameters = Convert-ObjectToHashtable -Object $this if ($this.Ensure -eq 'Present') { if ($this.IsStandardRule) { $existingRule.Disabled = $this.Disabled $existingRule | Add-ADSyncRule } else { $cmdet = Get-Command -Name New-ADSyncRule $param = Sync-Parameter -Command $cmdet -Parameters $allParameters $rule = New-ADSyncRule @param if ($this.ScopeFilter) { foreach ($scg in $this.ScopeFilter) { $scopeConditions = foreach ($sc in $scg.ScopeConditionList) { [Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition]::new($sc.Attribute, $sc.ComparisonValue, $sc.ComparisonOperator) } $rule | Add-ADSyncScopeConditionGroup -ScopeConditions $scopeConditions } } if ($this.JoinFilter) { foreach ($jcg in $this.JoinFilter) { $joinConditions = foreach ($jc in $jcg.JoinConditionList) { [Microsoft.IdentityManagement.PowerShell.ObjectModel.JoinCondition]::new($jc.CSAttribute, $jc.MVAttribute, $jc.CaseSensitive) } $rule | Add-ADSyncJoinConditionGroup -JoinConditions $joinConditions } } if ($this.AttributeFlowMappings) { foreach ($af in $this.AttributeFlowMappings) { $afHashTable = Convert-ObjectToHashtable -Object $af $param = Sync-Parameter -Command (Get-Command -Name Add-ADSyncAttributeFlowMapping) -Parameters $afHashTable $param.SynchronizationRule = $rule Add-ADSyncAttributeFlowMapping @param } } #if ($existingRule) #{ # Remove-ADSyncRule -Identifier $rule.Identifier #} $rule | Add-ADSyncRule } } else { if ($existingRule) { Remove-ADSyncRule -Identifier $this.Identifier } } } } #EndRegion '.\Classes\AADSyncRule.ps1' 272 #Region '.\Classes\AttributeFlowMapping.ps1' -1 class AttributeFlowMapping { AttributeFlowMapping() { } [DscProperty(Key)] [string]$Destination [DscProperty()] [bool]$ExecuteOnce [DscProperty(Key)] [string]$Expression [DscProperty(Key)] [AttributeMappingFlowType]$FlowType [DscProperty(NotConfigurable)] [string]$MappingSourceAsString [DscProperty(Key)] [string]$Source [DscProperty()] [AttributeValueMergeType]$ValueMergeType } #EndRegion '.\Classes\AttributeFlowMapping.ps1' 28 #Region '.\Classes\JoinCondition.ps1' -1 class JoinCondition { [DscProperty()] [string]$CSAttribute [DscProperty()] [string]$MVAttribute [DscProperty()] [bool]$CaseSensitive JoinCondition() { } JoinCondition([string]$CSAttribute, [string]$MVAttribute, [bool]$CaseSensitive) { $this.CSAttribute = $CSAttribute $this.MVAttribute = $MVAttribute $this.CaseSensitive = $CaseSensitive } } #EndRegion '.\Classes\JoinCondition.ps1' 23 #Region '.\Classes\JoinConditionGroup.ps1' -1 class JoinConditionGroup { [DscProperty()] [JoinCondition[]]$JoinConditionList ScopeConditionGroup() { } } #EndRegion '.\Classes\JoinConditionGroup.ps1' 11 #Region '.\Classes\ScopeCondition.ps1' -1 class ScopeCondition { [DscProperty()] [string]$Attribute [DscProperty()] [string]$ComparisonValue [DscProperty()] [ComparisonOperator]$ComparisonOperator ScopeCondition() { } ScopeCondition([hashtable]$Definition) { $this.Attribute = $Definition['Attribute'] $this.ComparisonValue = $Definition['ComparisonValue'] $this.ComparisonOperator = $Definition['ComparisonOperator'] } ScopeCondition([string]$Attribute, [string]$ComparisonValue, [string]$ComparisonOperator) { $this.Attribute = $Attribute $this.ComparisonValue = $ComparisonValue $this.ComparisonOperator = $ComparisonOperator } } #EndRegion '.\Classes\ScopeCondition.ps1' 30 #Region '.\Classes\ScopeConditionGroup.ps1' -1 class ScopeConditionGroup { [DscProperty()] [ScopeCondition[]]$ScopeConditionList ScopeConditionGroup() { } } #EndRegion '.\Classes\ScopeConditionGroup.ps1' 11 #Region '.\Private\New-Guid2.ps1' -1 function New-Guid2 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $InputString ) $md5 = [System.Security.Cryptography.MD5]::Create() $hash = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($InputString)) return [System.Guid]::new($hash).Guid } #EndRegion '.\Private\New-Guid2.ps1' 14 #Region '.\Public\Add-AADConnectDirectoryExtensionAttribute.ps1' -1 function Add-AADConnectDirectoryExtensionAttribute { [CmdletBinding(DefaultParameterSetName = 'ByProperties')] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$Name, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$Type, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$AssignedObjectClass, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [bool]$IsEnabled, [Parameter(Mandatory = $true, ParameterSetName = 'SingleObject')] [string]$FullAttributeString, [Parameter()] [switch]$Force ) process { $currentAttributes = Get-AADConnectDirectoryExtensionAttribute if ($FullAttributeString) { $attributeValues = $FullAttributeString -split '\.' if ($attributeValues.Count -ne 4) { Write-Error "The attribute string did not have the correct format. Make sure it is like 'attributeName.group.String.True'" return } $Name = $attributeValues[0] $AssignedObjectClass = $attributeValues[1] $Type = $attributeValues[2] $IsEnabled = $attributeValues[3] } if ($currentAttributes | Where-Object { $_.Name -eq $Name -and $_.AssignedObjectClass -eq $AssignedObjectClass -and $_.Type -eq $Type -and $_.IsEnabled -eq $IsEnabled }) { Write-Error "The attribute '$Name' with the type '$Type' assigned to the class '$AssignedObjectClass' is already defined." return } if (($existingAttribute = $currentAttributes | Where-Object { $_.Name -eq $Name -and $_.Type -ne $Type }) -and -not $Force) { Write-Error "The attribute '$Name' is already defined with the type '$($existingAttribute.Type)'." return } else { $existingAttribute | Remove-AADConnectDirectoryExtensionAttribute } $settings = Get-ADSyncGlobalSettings $attributeParameter = $settings.Parameters | Where-Object Name -eq Microsoft.OptionalFeature.DirectoryExtensionAttributes $currentAttributeList = $attributeParameter.Value -split ',' $newAttributeString = "$Name.$AssignedObjectClass.$Type.$IsEnabled" $currentAttributeList += $newAttributeString $attributeParameter.Value = $currentAttributeList -join ',' $settings.Parameters.AddOrReplace($attributeParameter) Set-ADSyncGlobalSettings -GlobalSettings $settings | Out-Null } } #EndRegion '.\Public\Add-AADConnectDirectoryExtensionAttribute.ps1' 77 #Region '.\Public\Convert-ObjectToHashtable.ps1' -1 function Convert-ObjectToHashtable { [OutputType([hashtable])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object]$Object ) process { $hashtable = @{ } foreach ($property in $Object.PSObject.Properties.Where({ $null -ne $_.Value })) { $hashtable.Add($property.Name, $property.Value) } $hashtable } } <# function Convert-ObjectToHashtable { <# .SYNOPSIS Takes a single object and converts its properties and values into a hashtable. .DESCRIPTION Takes a single object and converts its properties and values into a hashtable. .PARAMETER Object The Object to turn into a hashtable .PARAMETER ExcludeEmpty Switch to exclude empty properties .EXAMPLE Convert-ObjectToHashtable -object Value -ExcludeEmpty .NOTES Source: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/turning-objects-into-hash-tables-2 > #region parameter [CmdletBinding(ConfirmImpact = 'Low')] [OutputType([hashtable[]])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [psobject] $object, [Parameter()] [Switch] $ExcludeEmpty ) process { $object.PSObject.Properties | Sort-Object -Property Name | Where-Object { ($ExcludeEmpty.IsPresent -eq $false) -or ($null -ne $_.Value) } | ForEach-Object -Begin { $hashtable = ([Ordered]@{}) } -Process { $hashtable[$_.Name] = $_.Value } -End { Write-Output -InputObject $hashtable } } } #> #EndRegion '.\Public\Convert-ObjectToHashtable.ps1' 74 #Region '.\Public\Get-AADConnectDirectoryExtensionAttribute.ps1' -1 function Get-AADConnectDirectoryExtensionAttribute { param ( [Parameter()] [string]$Name ) $settings = Get-ADSyncGlobalSettings $attributeParameter = $settings.Parameters | Where-Object Name -eq Microsoft.OptionalFeature.DirectoryExtensionAttributes $attributes = $attributeParameter.Value -split ',' if (-not $attributes) { return } if ($Name) { $attributes = $attributes | Where-Object { $_ -like "$Name.*" } if (-not $attributes) { Write-Error "The attribute '$Name' is not defined." return } } foreach ($attribute in $attributes) { $attribute = $attribute -split '\.' [pscustomobject]@{ Name = $attribute[0] Type = $attribute[2] AssignedObjectClass = $attribute[1] IsEnabled = $attribute[3] } } } #EndRegion '.\Public\Get-AADConnectDirectoryExtensionAttribute.ps1' 36 #Region '.\Public\Get-ADSyncRule.ps1' -1 function Get-ADSyncRule { [CmdletBinding(DefaultParameterSetName = 'ByName')] param( [Parameter(ParameterSetName = 'ByName')] [string] $Name, [Parameter(ParameterSetName = 'ByIdentifier')] [guid] $Identifier ) if ($Identifier) { ADSync\Get-ADSyncRule -Identifier $Identifier } elseif ($Name) { ADSync\Get-ADSyncRule | Where-Object Name -eq $Name } else { ADSync\Get-ADSyncRule } } #EndRegion '.\Public\Get-ADSyncRule.ps1' 23 #Region '.\Public\Remove-AADConnectDirectoryExtensionAttribute.ps1' -1 function Remove-AADConnectDirectoryExtensionAttribute { [CmdletBinding(DefaultParameterSetName = 'ByProperties')] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$Name, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$Type, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ByProperties')] [string]$AssignedObjectClass, [Parameter(Mandatory = $true, ParameterSetName = 'SingleObject')] $FullAttributeString ) process { $currentAttributes = Get-AADConnectDirectoryExtensionAttribute if ($FullAttributeString) { $attributeValues = $FullAttributeString -split '\.' if ($attributeValues.Count -ne 4) { Write-Error "The attribute string did not have the correct format. Make sure it is like 'attributeName.group.String.True'". return } $Name = $attributeValues[0] $AssignedObjectClass = $attributeValues[1] $Type = $attributeValues[2] $IsEnabled = $attributeValues[3] } if (-not ($existingAttribute = $currentAttributes | Where-Object { $_.Name -eq $Name -and $_.AssignedObjectClass -eq $AssignedObjectClass -and $_.Type -eq $Type })) { Write-Error "The attribute '$Name' with the type '$Type' assigned to the class '$AssignedObjectClass' is not defined." return } $settings = Get-ADSyncGlobalSettings $attributeParameter = $settings.Parameters | Where-Object Name -eq Microsoft.OptionalFeature.DirectoryExtensionAttributes $currentAttributeList = $attributeParameter.Value -split ',' $attributeStringToRemove = "$($existingAttribute.Name).$($existingAttribute.AssignedObjectClass).$($existingAttribute.Type).$($existingAttribute.IsEnabled)" $currentAttributeList = $currentAttributeList -ne $attributeStringToRemove $attributeParameter.Value = $currentAttributeList -join ',' $settings.Parameters.AddOrReplace($attributeParameter) Set-ADSyncGlobalSettings -GlobalSettings $settings | Out-Null } } #EndRegion '.\Public\Remove-AADConnectDirectoryExtensionAttribute.ps1' 57 |