functions/schema/Test-FMSchema.ps1
function Test-FMSchema { <# .SYNOPSIS Compare the current schema with the configured / desired configuration state. .DESCRIPTION Compare the current schema with the configured / desired configuration state. Only compares the custom configured settings, ignores any changes outside. (So it's not a delta comparison to the AD baseline) .PARAMETER Server The server / domain to work with. .PARAMETER Credential The credentials to use for this operation. .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions. This is less user friendly, but allows catching exceptions in calling scripts. .EXAMPLE PS C:\> Test-FMSchema Tests the current domain's schema configuration. #> [CmdletBinding()] Param ( [PSFComputer] $Server, [PSCredential] $Credential, [switch] $EnableException ) begin { $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential $parameters['Debug'] = $false Assert-ADConnection @parameters -Cmdlet $PSCmdlet Invoke-Callback @parameters -Cmdlet $PSCmdlet Assert-Configuration -Type Schema -Cmdlet $PSCmdlet Set-FMDomainContext @parameters try { $rootDSE = Get-ADRootDSE @parameters -ErrorAction Stop } catch { Stop-PSFFunction -String 'Test-FMSchema.Connect.Failed' -StringValues $Server -ErrorRecord $_ -EnableException $EnableException -Exception $_.Exception.GetBaseException() return } $forest = Get-ADForest @parameters $parameters["Server"] = $forest.SchemaMaster #region Display Code $mayContainToString = { $updates = do { $this.New | Where-Object { $_ -notin @($this.Old) } | Format-String '+{0}' $this.Old | Where-Object { $_ -notin @($this.New) } | Format-String '-{0}' } until ($true) 'mayContain: {0}' -f ($updates -join ', ') } $mustContainToString = { $updates = do { $this.New | Where-Object { $_ -notin @($this.Old) } | Format-String '+{0}' $this.Old | Where-Object { $_ -notin @($this.New) } | Format-String '-{0}' } until ($true) 'mustContain: {0}' -f ($updates -join ', ') } #endregion Display Code } process { # Pick up termination flag from Stop-PSFFunction and interrupt if begin failed to connect if (Test-PSFFunctionInterrupt) { return } $allAttributes = Get-ADObject @parameters -LDAPFilter "(attributeID=*)" -SearchBase $rootDSE.schemaNamingContext -ErrorAction Ignore -Properties * $allClasses = Get-ADObject @parameters -LDAPFilter "(objectClass=classSchema)" -SearchBase $rootDSE.schemaNamingContext -ErrorAction Ignore -Properties * #region Process Configuration foreach ($schemaSetting in (Get-FMSchema)) { $schemaObject = $null $schemaObject = $allAttributes.Where{ $_.attributeID -eq $schemaSetting.OID }[0] if (-not $schemaObject) { # If we already want to disable the attribute, no need to create it if ($schemaSetting.IsDefunct) { continue } if ($schemaSetting.Optional) { continue } [PSCustomObject]@{ PSTypeName = 'ForestManagement.Schema.TestResult' Type = 'Create' ObjectType = 'Schema' Identity = $schemaSetting.AdminDisplayName Changed = $null Server = $forest.SchemaMaster ADObject = $null Configuration = $schemaSetting } continue } if ($schemaSetting.IsDefunct -and -not $schemaObject.isDefunct) { [PSCustomObject]@{ PSTypeName = 'ForestManagement.Schema.TestResult' Type = 'Decommission' ObjectType = 'Schema' Identity = $schemaSetting.AdminDisplayName Changed = @('IsDefunct') Server = $forest.SchemaMaster ADObject = $schemaObject Configuration = $schemaSetting } } if ($schemaSetting.Name -cne $schemaObject.cn) { [PSCustomObject]@{ PSTypeName = 'ForestManagement.Schema.TestResult' Type = 'Rename' ObjectType = 'Schema' Identity = $schemaSetting.AdminDisplayName Changed = @('Name') Server = $forest.SchemaMaster ADObject = $schemaObject Configuration = $schemaSetting } } $changes = [System.Collections.ArrayList]@() $param = @{ Configuration = $schemaSetting ADObject = $schemaObject CaseSensitive = $true IfExists = $true AsUpdate = $true Changes = $changes Type = 'Schema' } Compare-AdcProperty @param -Property oMSyntax Compare-AdcProperty @param -Property attributeSyntax Compare-AdcProperty @param -Property SingleValued -ADProperty isSingleValued Compare-AdcProperty @param -Property adminDescription -AsString Compare-AdcProperty @param -Property adminDisplayName Compare-AdcProperty @param -Property ldapDisplayName Compare-AdcProperty @param -Property searchflags Compare-AdcProperty @param -Property PartialAttributeSet -ADProperty isMemberOfPartialAttributeSet Compare-AdcProperty @param -Property AdvancedView -ADProperty showInAdvancedViewOnly if (-not $schemaSetting.IsDefunct -and $schemaObject.isDefunct) { Compare-AdcProperty @param -Property isDefunct } if (-not $schemaSetting.IsDefunct -and $schemaSetting.PSObject.Properties.Name -contains 'MayBeContainedIn') { $mayContain = $allClasses.Where{ $_.MayContain -contains $schemaSetting.LdapDisplayName } if (-not $mayContain -and $schemaSetting.MayBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MayContain -NewValue $schemaSetting.MayBeContainedIn -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mayContainToString)) } elseif ($mayContain.Name -and -not $schemaSetting.MayBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MayContain -OldValue $mayContain.Name -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mayContainToString)) } elseif (-not $mayContain.Name -and -not $schemaSetting.MayBeContainedIn) { # Nothing wrong here } elseif ($mayContain.Name | Compare-Object $schemaSetting.MayBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MayContain -OldValue $mayContain.Name -NewValue $schemaSetting.MayBeContainedIn -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mayContainToString)) } } if (-not $schemaSetting.IsDefunct -and $schemaSetting.PSObject.Properties.Name -contains 'MustBeContainedIn') { $mustContain = $allClasses.Where{ $_.mustContain -contains $schemaSetting.LdapDisplayName } if (-not $mustContain -and $schemaSetting.MustBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MustContain -NewValue $schemaSetting.MustBeContainedIn -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mustContainToString)) } elseif ($mustContain.Name -and -not $schemaSetting.MustBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MustContain -OldValue $mustContain.Name -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mustContainToString)) } elseif (-not $mustContain.Name -and -not $schemaSetting.MustBeContainedIn) { # Nothing wrong here } elseif ($mustContain.Name | Compare-Object $schemaSetting.MustBeContainedIn) { $null = $changes.Add((New-AdcChange -Property MustContain -OldValue $mustContain.Name -NewValue $schemaSetting.MustBeContainedIn -Identity $schemaObject.DistinguishedName -Type Schema -ToString $mustContainToString)) } } if ($changes.Count -gt 0) { [PSCustomObject]@{ PSTypeName = 'ForestManagement.Schema.TestResult' Type = 'Update' ObjectType = 'Schema' Identity = $schemaSetting.AdminDisplayName Changed = $changes.ToArray() Server = $forest.SchemaMaster ADObject = $schemaObject Configuration = $schemaSetting } } } #endregion Process Configuration #region Process AD Only if (-not (Get-PSFConfigValue -FullName 'ForestManagement.Schema.Attributes.ReportUnconfigured')) { return } $unconfigured = $allAttributes | Where-Object attributeID -NotIn (Get-FMSchema).OID foreach ($unexpectedAttribute in $unconfigured) { if ($unexpectedAttribute.IsDefunct) { continue } [PSCustomObject]@{ PSTypeName = 'ForestManagement.Schema.TestResult' Type = 'Unmanaged' ObjectType = 'Schema' Identity = $unexpectedAttribute.AdminDisplayName Changed = $null Server = $forest.SchemaMaster ADObject = $unexpectedAttribute Configuration = $null } } #endregion Process AD Only } } |