Jorge's Quest For Knowledge!

All about Windows Server, ADDS, ADFS & FIM (It Is Just Like An Addiction, The More You Have, The More You Want To Have!)

Archive for the ‘Active Directory Domain Services (ADDS)’ Category

(2014-07-24) Possible Issues When Running Both W2K3 And W2K12R2 DCs For The Same AD Domain

Posted by Jorge on 2014-07-24


If you are upgrading your AD from W2K3 to W2K12R2, you might be experiencing issues when running both OS versions at the same time. The guys at ASKDS have written a great blog post about this.

Click on the following link to read all about it and what you can do about it.

It turns out that weird things can happen when you mix Windows Server 2003 and Windows Server 2012 R2 domain controllers

-

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
http://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Windows Server | Leave a Comment »

(2014-07-18) Checking New Schema Extensions For Potential Schema Conflicts

Posted by Jorge on 2014-07-18


Justin Hall, who works at Microsoft has published a PowerShell script which checks/validates new schema extensions for potential conflicts. The original script, including additional information can be found here. I have re-written and updated that script to perform additional checks against the AD schema, but also to perform checks within the new extensions themselves. At the same time I also resolved some bugs that I had found in the script.

-

The code of the PowerShell script that does this is included below. Screen dumps are also included after the code sample.

-

!!! DISCLAIMER/REMARKS !!!:

  • The script is freeware, you are free to distribute it, but always refer to this website as the location where you got it
  • This script is furnished "as is". No warranty is expressed or implied!
  • Always test first in lab environment to see if it meets your needs!
  • Use this script at your own risk!
  • I do not warrant this script to be fit for any purpose, use or environment
  • I have tried to check everything that needed to be checked, but I do not guarantee the script does not have bugs.
  • I do not guarantee the script will not damage or destroy your system(s), environment or whatever.
  • I do not accept liability in any way if you screw up, use the script wrong or in any other way where damage is caused to your environment/systems!
  • If you do not accept these terms do not use the script and delete it immediately!

!!! DISCLAIMER/REMARKS !!!:

-

I have also linked a file with the contents below to this blog post. Get it HERE.

-

# Abstract: This PoSH Script Checks/Validates (New) Schema Extensions # Written by: Justin Hallo [MSFT] # Original Location: http://gallery.technet.microsoft.com/scriptcenter/0672d181-ab2c-4c92-8466-d93a67412207 # # Re-Written by: Jorge de Almeida Pinto [MVP-DS] # BLOG: http://jorgequestforknowledge.wordpress.com/ # # 2010-05-07: (v0.1): Initial version of the script # 2014-07-07: (v0.2): Re-written script with additional checks in current schema en extensions file and resolved bugs # # -----> !!! DISCLAIMER/REMARKS !!! <------ # * The script is freeware, you are free to distribute it, but always refer to this website (http://jorgequestforknowledge.wordpress.com/) as the location where you got it # * This script is furnished "AS IS". No warranty is expressed or implied! # * Always test first in lab environment to see if it meets your needs! # * Use this script at your own risk! # * I do not warrant this script to be fit for any purpose, use or environment # * I have tried to check everything that needed to be checked, but I do not guarantee the script does not have bugs. # * I do not guarantee the script will not damage or destroy your system(s), environment or whatever. # * I do not accept any liability in any way if you screw up, use the script wrong or in any other way where damage is caused to your environment/systems! # * If you do not accept these terms do not use the script and delete it immediately! # -----> !!! DISCLAIMER/REMARKS !!! <------ Param( [Parameter(Mandatory=$true)] [array]$inputLDIFFileList, # [Mandatory] The List Of Input LDIF Files (Comma Separated) With The Schema Extensions To Check For Conflicts [Parameter(Mandatory=$true)] [string]$outputLDIFFile, # [Mandatory] The Output LDIF File With The Results Of The Analyses Of The Schema Extensions [Parameter(Mandatory=$false)] [string]$currentSchemaLDIFFile # [Optional] The LDIF File Containing The AD Schema To Check Against. When Not Specified It will Exported ) $global:errorCount = 0 $global:warningCount = 0 # Configure The Appropriate Screen And Buffer Size To Make Sure Everything Fits Nicely $uiConfig = (Get-Host).UI.RawUI $uiConfig.WindowTitle = "+++ AD SCHEMA EXTENSION CONFLICT ANALYZER +++" $uiConfig.Foregroundcolor = "White" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 120 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 120) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 120 } If ($uiConfigScreenSizeMaxHeight -lt 75) { $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5 } Else { $uiConfigScreenSize.Height = 75 } $uiConfig.BufferSize = $uiConfigBufferSize $uiConfig.WindowSize = $uiConfigScreenSize Clear-Host Write-Host " *****************************************************" -Foregroundcolor Magenta Write-Host " * *" -Foregroundcolor Magenta Write-Host " * --> AD SCHEMA EXTENSION CONFLICT ANALYZER <-- *" -Foregroundcolor Magenta Write-Host " * *" -Foregroundcolor Magenta Write-Host " * Written By: Justin Hall [MSFT] *" -Foregroundcolor Magenta Write-Host " *(http://gallery.technet.microsoft.com/scriptcenter)*" -Foregroundcolor Magenta Write-Host " * *" -Foregroundcolor Magenta Write-Host " * Re-Written By: Jorge de Almeida Pinto [MVP-DS] *" -Foregroundcolor Magenta Write-Host " * (http://jorgequestforknowledge.wordpress.com/) *" -Foregroundcolor Magenta Write-Host " * *" -Foregroundcolor Magenta Write-Host " *****************************************************" -Foregroundcolor Magenta Function global:DisplayScriptUsage { Write-Host "====================================================" Write-Host "`n EXTENSION CHECKER SCRIPT USAGE `n" Write-Host "====================================================" Write-Host "USAGE SUMMARY" Write-Host "`n -inputLDIFFileList " -Foregroundcolor darkCyan Write-Host "`n [MANDATORY] Provide the path to the input file containing the custom schema extensions here.`n" Write-Host "`n -outputLDIFFile " -Foregroundcolor darkCyan Write-Host "`n [MANDATORY] Provide the path to the output LDF file containing the annotations and suggested corrections. `n" Write-Host "`n -currentSchemaLDIFFile " -Foregroundcolor darkCyan Write-Host "`n [OPTIONAL] This is a required field If the operation is being run on a test DC." Write-Host " If the validate operation is being performed on a production DC, the current schema file need not be provided. The script will extract it.`n" Write-Host "EXAMPLES" -Foregroundcolor darkCyan Write-Host " .\AD-Schema-Extension-Conflict-Analyzer.ps1 -inputLDIFFileList sampleldf1.ldf,sampleldf2.ldf -outputLDIFFile results.ldf -currentSchemaLDIFFile myProductionSchema.ldf `n" Exit } # The function below will check current production schema file and/or the input custom extension file to make sure that the class / attribute being referenced (in systemMayContain, # systemMustContain, mayContain, mustContain, subClassOf , systemAuxiliaryClass , auxiliaryClass , systemPossSuperiors , and possSuperiors) has already been created. # If it has not been created but is still being referenced in one of the attributes above then that is an error and it needs to be flagged. Function global:ConfirmExistenceofReferencedAttributesOrClasses($attrClassList) { $attrsClasses = $attrClassList.Split(",") ForEach($item in $attrsClasses) { If (!($currentSchemaLDIFFileContent | Select-String -pattern $item -quiet)) { # The attribute/class under consideration was not found in current production schema file. We need to check the input file now. May be it is a new addition. $searchString = "CN=" + $item If(!($consolidatedLDIFFileContent | Select-String -pattern $searchString -quiet)) { $searchString = "lDAPDisplayName: " + $item If(!($consolidatedLDIFFileContent | Select-String -pattern $searchString -quiet)) { # No declaration of the attribute/class under consideration was found in the inputFile too. Looks like an error. The attribute/class should have been created before referencing it. Write-Host "[ERROR] : The class or attribute under consideration " $item " is being referenced but has not been created so far. Make sure it is created before using it!" -Foregroundcolor Red $tempError = "### ERROR : The class or attribute under consideration - " + $item + " - is being referenced but has not been created so far. Make sure it is created before using it!" $global:errorCount += 1 Add-Content -path $outputLDIFFile -value $tempError } } } } } # The function below will check current production schema file to look for a particular value of attributeID. attributeID is expected to be unique! Function global:checkUniquenessOfattributeIDCurrentSchema([string]$attributeIDValue) { If($currentSchemaLDIFFileContent | Select-String -pattern $attributeIDValue | ForEach { $_.Line | Select-String $attributeIDValue -quiet}) { Write-Host "[ERROR] : The attributeID value is already used in the schema. Make sure the attributeID OID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The attributeID value is already used in the schema. Make sure the attributeID OID is unique!" $global:errorCount += 1 } } # The function below will check extension file to look for a particular value of attributeID. attributeID is expected to be unique! Function global:checkUniquenessOfattributeIDNewExtensions([string]$attributeIDValue) { If(($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)(governsID:|attributeID:).*$attributeIDValue$" | Measure).Count -gt 1) { Write-Host "[ERROR] : The attributeID value is already used in the extension. Make sure the attributeID OID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The attributeID value is already used in the extension. Make sure the attributeID OID is unique!" $global:errorCount += 1 } } # The function below will check current production schema file to look for a particular value of governsID. governsID is expected to be unique! Function global:checkUniquenessOfgovernsIDCurrentSchema([string]$governsIDValue) { If($currentSchemaLDIFFileContent | Select-String -pattern $governsIDValue | ForEach { $_.Line | Select-String $governsIDValue -quiet}) { Write-Host "[ERROR] : The governsID value is already used in the schema. Make sure the governsID OID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The governsID value is already used in the schema. Make sure the governsID OID is unique!" $global:errorCount += 1 } } # The function below will check extension file to look for a particular value of governsID. governsID is expected to be unique! Function global:checkUniquenessOfgovernsIDNewExtensions([string]$governsIDValue) { If(($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)(governsID:|attributeID:).*$governsIDValue$" | Measure).Count -gt 1) { Write-Host "[ERROR] : The governsID value is already used in the extension. Make sure the governsID OID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The governsID value is already used in the extension. Make sure the governsID OID is unique!" $global:errorCount += 1 } } # The function below will check current production schema file to look for a particular value of mAPIID. mAPIID is expected to be unique! Function global:checkUniquenessOfmAPIIDCurrentSchema([string]$mAPIIDValue) { If($currentSchemaLDIFFileContent | Select-String -pattern "mAPIID" -quiet) { If($currentSchemaLDIFFileContent | Select-String -pattern "^(?i)mAPIID:.*$mAPIIDValue$" | ForEach { $_.Line | Select-String $mAPIIDValue -quiet}) { Write-Host "[ERROR] : The mAPIID value is already used in the schema. Make sure the mAPIID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The mAPIID value is already used in the schema. Make sure the mAPIID is unique!" $global:errorCount += 1 } } } # The function below will check extension file to look for a particular value of mAPIID. mAPIID is expected to be unique! Function global:checkUniquenessOfmAPIIDNewExtensions([string]$mAPIIDValue) { If(($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)mAPIID:.*$mAPIIDValue$" | Measure).Count -gt 1) { Write-Host "[ERROR] : The mAPIID value is already used in the extension. Make sure the mAPIID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The mAPIID value is already used in the extension. Make sure the mAPIID is unique!" $global:errorCount += 1 } } # The function below will go through current production schema file to make sure the attribute/class that is being added is not already present in the current production schema Function global:checkExistenceCurrentSchema([string]$attribValue) { ForEach ($line in $currentSchemaLDIFFileContent) { $SchemaValues = @($line.Split(":")) If ($SchemaValues[1].length -gt 0) { $SchemaValues[1] = $SchemaValues[1].TrimStart() } If ($SchemaValues[0].ToLower() -like "cn" -And $SchemaValues[1].Contains($attribValue)) { Write-Host "[ERROR] : Attempt being made to add an attribute/class which already exists in the schema!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : Attempt being made to add an attribute/class which already exists in the schema!" $global:errorCount += 1 } If ($SchemaValues[0].ToLower() -like "ldapdisplayname" -And $SchemaValues[1].Contains($attribValue)) { Write-Host "[ERROR] : The lDAPDisplayName value is already used in the schema. Make sure the lDAPDisplayName is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The lDAPDisplayName value is already used in the schema. Make sure the lDAPDisplayName is unique!" $global:errorCount += 1 } } } # The function below will check extension file to make sure the attribute/class that is being added is not being added twice Function global:checkExistenceCNNewExtensions([string]$attribValue) { If (($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)cn:.*$attribValue$" | Measure).Count -gt 1) { Write-Host "[ERROR] : Attempt being made to add an attribute/class twice through the extension!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : Attempt being made to add an attribute/class twice through the extension!" $global:errorCount += 1 } } # The function below will check extension file to look for a particular value of lDAPDisplayName. lDAPDisplayName is expected to be unique! Function global:checkExistenceLDAPDisplayNameNewExtensions([string]$attribValue) { If (($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)lDAPDisplayName:.*$attribValue$" | Measure).Count -gt 1) { Write-Host "[ERROR] : The lDAPDisplayName value is already used in the extension. Make sure the lDAPDisplayName is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The lDAPDisplayName value is already used in the extension. Make sure the lDAPDisplayName is unique!" $global:errorCount += 1 } } # The function below will go through current production schema file to look for the attribute $attrName which has been set as rdnAttid for another attribute # The syntax of $attrName should be UNICODE 2.5.5.9 Function global:checkSyntaxOfRdnattidAttribute($attrName) { $encounteredDn = 0 # The variable $encounteredDn will track whether or not we have come across the block that describes the attribute $attrName. # Every "dn: " statement in the current production schema file will be checked till $attrName is encountered. On encountering it $encounteredDn will be set to 1. # Once $attrName has been found its attributeSyntax attribute needs to be verified to make sure its value is 2.5.5.9 ForEach ($line in $currentSchemaLDIFFileContent) { If($encounteredDn -eq 0) { $SchemaValues = @($line.Split(":")) If ($SchemaValues[1].length -gt 0) { $SchemaValues[1] = $SchemaValues[1].TrimStart() } If (!$SchemaValues[0].CompareTo("dn") -AND $SchemaValues[1].Contains($attrName)) { $encounteredDn = 1 Continue } } Else { $SchemaValues = @($line.Split(":")) If ($SchemaValues[1].length -gt 0) { $SchemaValues[1] = $SchemaValues[1].TrimStart(" ") } If (!$SchemaValues[0].CompareTo("attributeSyntax")) { If($SchemaValues[1].CompareTo("2.5.5.9")) { # This means attributeSyntax of the attribute which is the rdnAttid for another attribute is not INTEGER. This is not expected. Write-Host "[ERROR] : AttributeSyntax of rDNAttID attribute should be INTEGER 2.5.5.9. Please correct this!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### WARNING : AttributeSyntax of rDNAttID attribute should be INTEGER 2.5.5.9. Please correct this!" $global:errorCount += 1 } Break } } } } Function global:ValidateExtensions { # Valid Syntaxes for Attributes in the Active Directory Schema $attributeSyntaxList = @{ "2.5.5.1" = "(DISTNAME)"; "2.5.5.2" = "(OBJECT_ID)"; "2.5.5.3" = "(CASE_STRING)"; "2.5.5.4" = "(NOCASE_STRING)"; "2.5.5.5" = "(PRINT_CASE_STRING)"; "2.5.5.6" = "(NUMERIC_STRING)"; "2.5.5.7" = "(DISTNAME_BINARY)"; "2.5.5.8" = "(BOOLEAN)"; "2.5.5.9" = "(INTEGER)"; "2.5.5.10" = "(OCTET_STRING)"; "2.5.5.11" = "(TIME)"; "2.5.5.12" = "(UNICODE)"; "2.5.5.13" = "(ADDRESS)"; "2.5.5.14" = "(DISTNAME_STRING)"; "2.5.5.15" = "(NT_SECURITY_DESCRIPTOR)"; "2.5.5.16" = "(I8)"; "2.5.5.17" = "(SID)" } # The systemFlags Attribute Specifies An Integer Value That Contains Flags That Define Additional Properties Of The Class $systemFlagsList = @{ 0x1 = "FLAG_ATTR_NOT_REPLICATED"; 0x2 = "FLAG_ATTR_REQ_PARTIAL_SET_MEMBER"; 0x4 = "FLAG_ATTR_IS_CONSTRUCTED"; 0x8 = "FLAG_ATTR_IS_OPERATIONAL"; 0x10 = "FLAG_SCHEMA_BASE_OBJECT"; 0x20 = "FLAG_ATTR_IS_RDN"; 0x2000000 = "FLAG_DISALLOW_MOVE_ON_DELETE"; 0x4000000 = "FLAG_DOMAIN_DISALLOW_MOVE"; 0x8000000 = "FLAG_DOMAIN_DISALLOW_RENAME"; 0x10000000 = "FLAG_CONFIG_ALLOW_LIMITED_MOVE"; 0x20000000 = "FLAG_CONFIG_ALLOW_MOVE"; 0x40000000 = "FLAG_CONFIG_ALLOW_RENAME"; 0x80000000 = "FLAG_DISALLOW_DELETE" } # The searchFlags Property Specifies The Characteristics And Behavior Of The Attribute $searchFlagsList = @{ 1 = "fATTINDEX"; 2 = "fPDNTATTINDEX"; 4 = "fANR"; 8 = "fPRESERVEONDELETE"; 16 = "fCOPY"; 32 = "fTUPLEINDEX"; 64 = "fSUBTREEATTINDEX"; 128 = "fCONFIDENTIAL"; 256 = "fNEVERVALUEAUDIT"; 512 = "fRODCFilteredAttribute"; 1024 = "fEXTENDEDLINKTRACKING"; 2048 = "fBASEONLY"; 4096 = "fPARTITIONSECRET"; } # Valid Syntaxes for Attributes in the Active Directory Schema $oMSyntaxList = @{ "0" = "(NO_MORE_SYNTAXES)"; "1" = "(BOOLEAN)"; "2" = "(INTEGER)"; "3" = "(BIT_STRING)"; "4" = "(OCTET_STRING)"; "5" = "(NULL)"; "6" = "(OBJECT_IDENTIFIER_STRING)"; "7" = "(OBJECT_DESCRIPTOR_STRING)"; "8" = "(ENCODING_STRING)"; "10" = "(ENUMERATION)"; "18" = "(NUMERIC_STRING)"; "19" = "(PRINTABLE_STRING)"; "20" = "(TELETEX_STRING)"; "21" = "(VIDEOTEX_STRING)"; "22" = "(IA5_STRING)"; "23" = "(UTC_TIME_STRING)"; "24" = "(GENERALISED_TIME_STRING)"; "25" = "(GRAPHIC_STRING)"; "26" = "(VISIBLE_STRING)"; "27" = "(GENERAL_STRING)"; "64" = "(UNICODE_STRING)"; "65" = "(I8)"; "66" = "(OBJECT_SECURITY_DESCRIPTOR)"; "127" = "(OBJECT )" } # This attribute specifies the unique object ID (OID) for the attribute or class $oMObjectClassList = @{ "KoZIhvcUAQEBBg==" = "1.2.840.113556.1.1.1.6 (REPLICA-LINK)"; "KoZIhvcUAQEBCw==" = "1.2.840.113556.1.1.1.11 (DN-BINARY)"; "KoZIhvcUAQEBDA==" = "1.2.840.113556.1.1.1.12 (DN-STRING)"; "KwwCh3McAIVK" = "1.35.44.2.1011.60.0.746 (DS-DN)"; "KwwCh3McAIU+" = "1.35.44.2.1011.60.0.734 (ACCESS-POINT)"; "KwwCh3McAIVc" = "1.35.44.2.1011.60.0.764 (PRESENTATION-ADDRESS)"; "VgYBAgULHQ==" = "2.6.6.1.2.5.43.61 (OR-NAME)" } # Mapping Between attributeSyntax And OmSyntax $attributeSyntaxToOmSyntaxList = @{ "2.5.5.1" = "127"; "2.5.5.2" = "6"; "2.5.5.3" = "27"; "2.5.5.4" = "20"; "2.5.5.5" = "19,22"; "2.5.5.6" = "18"; "2.5.5.7" = "127"; "2.5.5.8" = "1"; "2.5.5.9" = "2,10"; "2.5.5.10" = "4"; "2.5.5.11" = "23,24"; "2.5.5.12" = "64"; "2.5.5.13" = "127"; "2.5.5.14" = "127"; "2.5.5.15" = "66"; "2.5.5.16" = "65"; "2.5.5.17" = "4" } $linkedAttrsDisplayNameList = @{} $linkedAttrsOIDList = @{} $schemaUpdateNowFlag = "FALSE" $startedAddingClassesFlag = "FALSE" $cnValue = $null $ldapDisplayNameValue = $null # Now Start Scanning The (Combined) Input LDIF File $linecount = 0 Add-Content -path $outputLDIFFile -value "############################################################################################################" Add-Content -path $outputLDIFFile -value "#Scanned LDIF File With Corrections/Suggestions" Add-Content -path $outputLDIFFile -value "############################################################################################################" ForEach($line in $consolidatedLDIFFileContent) { $linecount++ If ($line.StartsWith("#")) { # Also Add Comments To Output Add-Content -path $outputLDIFFile -value $line Continue } Else { # To Get An Array Of Items, Values[0]..Values[x-1] Where [x] Is The Number Of Items Created Add-Content -path $outputLDIFFile -value $line # Split The Lines In a Left Part And A Right Part, Using The : As the Separator And Remove Any Traling Spaces $Values = @($line.Split(":")) If ($values[1].length -gt 0) { $values[1] = $values[1].TrimStart() } Else { Continue } # Show The Info On Screen Write-Host "Line.................#"$linecount Write-Host "Attribute............:"$values[0] Write-Host "Value................:"$values[1] $tempStr = "" $cnValue = $null $lDAPDisplayNameValue = $null # Convert The Left Side To Lower For The Switch Below To Work Correctly And Always Have Expected Results $values[0] = $values[0].ToLower() switch -wildcard ($values[0]) { "attributesyntax" { $attributeSyntaxValue = $values[1].TrimStart() If( !$attributeSyntaxList.ContainsKey($attributeSyntaxValue)) { Write-Host "[ERROR] : The value provided for attribute syntax is invalid. Please correct the value!" -Foregroundcolor Red $tempError = "### ERROR : The value provided for attribute syntax is invalid. Please correct the value!" Add-Content -path $outputLDIFFile -value $tempError $attributeSyntaxValue = $null $global:errorCount += 1 continue } Write-Host "attributeSyntax is...:" $attributeSyntaxList[$values[1]] $tempStr = "# attributeSyntax is:" + $attributeSyntaxList[$values[1]] Add-Content -path $outputLDIFFile -value $tempStr } "systemflags" { $values[1] = $values[1].TrimStart() $flags = $null For ($i = 1; $i -le 0x8000000; $i *= 2) { If ($values[1] -band $i) { If ($flags.length -gt 0) { $flags = $flags + " | " + $systemFlagsList[$i] } Else { If (!$systemFlagsList.ContainsKey($i)) { Write-Host "[ERROR] : The systemFlags value is invalid. Please correct it!" Add-Content -path $outputLDIFFile -value "### ERROR : The systemFlags value is invalid. Please correct it!" $global:errorCount += 1 Continue } $flags = $systemFlagsList[$i] } If ($i -eq 0x10) { # systemFlags contains 0x10 which means base schema object. This needs to be flagged. Write-Host "[WARNING] : This attribute/class has been marked as BASE_SCHEMA_OBJECT. This would need approval from MSFT Active Directory schema team!" -Foregroundcolor Yellow Add-Content -path $outputLDIFFile -value "### WARNING : This attribute/class has been marked as BASE_SCHEMA_OBJECT. This would need approval from MSFT Active Directory schema team!" } } } $tempStr = "# systemFlags is:" + $flags Write-Host "systemFlags is.......:" $flags Add-Content -path $outputLDIFFile -value $tempStr } "searchflags" { $values[1] = $values[1].TrimStart() $flags = ""; For ($i = 1; $i -le 512; $i *= 2) { If ($values[1] -band $i) { If ($flags.length -gt 0) { $flags = $flags + " | " + $searchFlagsList[$i] } Else { $flags = $searchFlagsList[$i] } If ($i -eq 8) { # fPreserveOnDelete Is Set. Make Sure This Is Really Required Add-Content -path $outputLDIFFile -value "### WARNING : This attribute has been marked as fPreserveOnDelete. Please confirm the need to do so!" Write-Host "[WARNING] : This attribute has been marked as fpreserveOnDelete. Please confirm the need to do so!" -Foregroundcolor Yellow $global:warningCount += 1 } If($i -eq 128) { # fConfidential Is Set. Make Sure This Is Really Required Add-Content -path $outputLDIFFile -value "### WARNING : This attribute has been marked as fConfidential. Please confirm the need to do so!" Write-Host "[WARNING] : This attribute has been marked as fConfidential. Please confirm the need to do so!" -Foregroundcolor Yellow $global:warningCount += 1 } } } Write-Host "searchFlags is.......:" $flags $tempStr = "# searchFlags:" + $flags Add-Content -path $outputLDIFFile -value $tempStr } "omsyntax" { $values[1] = $values[1].TrimStart() Write-Host "oMSyntax is..........:" $oMSyntaxList[$values[1]] If(!$oMSyntaxList.ContainsKey($values[1])) { Write-Host "[ERROR] : The value provided for oMSyntax is invalid. Please correct the value!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The value provided for oMSyntax is invalid. Please correct the value!" $global:errorCount += 1 Continue } $tempStr = "# oMSyntax:" + $oMSyntaxList[$values[1]] Add-Content -path $outputLDIFFile -value $tempStr $compareResult = $values[1].CompareTo($attributeSyntaxToOmSyntaxList[$attributeSyntaxValue]) If ($compareResult -ne 0) { $omValues = @($attributeSyntaxToOmSyntaxList[$attributeSyntaxValue].Split(",")) $compareResult = $values[1].CompareTo($omValues[0]) If($compareResult -ne 0) { $compareResult = $values[1].CompareTo($omValues[1]) If($compareResult -ne 0) { Add-Content -path $outputLDIFFile -value "### ERROR : oMSyntax does not seem to match the attributeSyntax value specified earlier for this attribute!" Write-Host "###[ERROR] : oMSyntax does not seem to match the attributeSyntax value specified earlier for this attribute!" -Foregroundcolor Red } } } $attributeSyntaxValue = "" } "omobjectclass" { $values[1] = $values[1].TrimStart() If (!$oMObjectClassList.ContainsKey($values[1])) { Write-Host "[ERROR] : oMObjectClass specified is invalid. Please correct the value and try again!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : oMObjectClass specified is invalid. Please correct the value and try again!" $global:errorCount += 1 Continue } Write-Host "oMObjectClass is.....:" $oMObjectClassList[$values[1]] $tempStr = "# oMObjectClass is:" + $oMObjectClassList[$values[1]] Add-Content -path $outputLDIFFile -value $tempStr $objectClass = $values[1] } "objectclass" { $values[1] = $values[1].TrimStart() If($values[1].CompareTo("classSchema") -eq 0) { If($schemaUpdateNowFlag.CompareTo("TRUE") -ne 0 -and $startedAddingClassesFlag.CompareTo("TRUE") -eq 0) { Write-Host "[ERROR] : schemaUpdateNow needs to be inserted before adding new classes!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : schemaUpdateNow needs to be inserted before adding new classes!" $startedAddingClassesFlag = "TRUE" $global:errorCount += 1 } } } "admindescription" { $values[1] = $values[1].TrimStart() If ($values[1].length -le 1) { Add-Content -path $outputLDIFFile -value "### ERROR : Adequate description has not been specified. Please correct this!" Write-Host "[ERROR] : Adequate description has not been specified. Please correct this!" -Foregroundcolor Red $global:errorCount += 1 } } "systemonly" { $values[1] = $values[1].TrimStart() $systemOnlyCheck = $values[1].CompareTo("TRUE") If ($systemOnlyCheck -eq 0) { # Object Has Been Flagged As systemOnly. Needs To Be Checked Further. Write-Host "[ERROR] : This object has been marked as systemOnly. This needs approval from the MSFT Active Directory schema team!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : This object has been marked as systemOnly. This needs approval from the MSFT Active Directory schema team!" $global:errorCount += 1 } } "attributeid" { $values[1] = $values[1].TrimStart() checkUniquenessOfattributeIDCurrentSchema($values[1]) checkUniquenessOfattributeIDNewExtensions($values[1]) } "governsid" { $values[1] = $values[1].TrimStart() checkUniquenessOfgovernsIDCurrentSchema($values[1]) checkUniquenessOfgovernsIDNewExtensions($values[1]) } "changetype" { $changetypeValue = $values[1].TrimStart() # When you come across a "changeType" that means an attempt is being made to add /modify an existing object $schemaUpdateNowFlag = "FALSE" } "cn" { $cnValue = $values[1].TrimStart() If ($changetypeValue -like "add" -or $changetypeValue -like "ntdsSchemaAdd") { # add operation is being performed for this attribute/class. Make sure that the object doesn't already exist in the schema. checkExistenceCurrentSchema($cnValue) checkExistenceCNNewExtensions($cnValue) } $cnValue = $null #$changetypeValue = "" } "ldapdisplayname" { $lDAPDisplayNameValue = $values[1].TrimStart() If ($changetypeValue -like "add" -or $changetypeValue -like "ntdsSchemaAdd") { # add operation is being performed for this attribute/class. Make sure that the object doesn't already exist in the schema. checkExistenceCurrentSchema($lDAPDisplayNameValue) checkExistenceLDAPDisplayNameNewExtensions($lDAPDisplayNameValue) } $lDAPDisplayNameValue = $null #$changetypeValue = "" } "linkid" { $values[1] = $values[1].TrimStart() If ($values[1] -eq "1.2.840.113556.1.2.50") { $linkedAttrsDisplayNameList.Add($ldapDisplayNameValue, 0); $linkedAttrsOIDList.Add($oid, 0); } Else { If(!$linkedAttrsDisplayNameList.ContainsKey($values[1])) { If(!$linkedAttrsOIDList.ContainsKey($values[1])) { Write-Host "[ERROR] : An attempt is being made to access a forward link that doesn't seem to exist. If this is a hard coded linkID that is not valid. Please follow the guidelines for obtaining a linkID!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : An attempt is being made to access a forward link that doesn't seem to exist. If this is a hard coded linkID that is not valid. Please follow the guidelines for obtaining a linkID!" $global:errorCount += 1 } Else { $linkedAttrsOIDList[$values[1]] = $linkedAttrsOIDList[$values[1]] + 1; } } Else { $linkedAttrsDisplayNameList[$values[1]] = $linkedAttrsDisplayNameList[$values[1]] + 1; Write-Host "A forward link does exist for this back link. Usage is valid!" Add-Content -path $outputLDIFFile -value "### A forward link does exist for this back link. Usage is valid!" } If(($consolidatedLDIFFileContent | Select-String -Pattern "^(?i)linkID:.*$($values[1])$" | Measure).Count -gt 1) { Write-Host "[ERROR] : The linkID value is already used in the extension. Make sure the linkID is unique!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The linkID value is already used in the extension. Make sure the linkID is unique!" $global:errorCount += 1 } } } "mapiid" { $values[1] = $values[1].TrimStart() checkUniquenessOfmAPIIDCurrentSchema($values[1]) checkUniquenessOfmAPIIDNewExtensions($values[1]) } # systemMayContain, systemMustContain,systemAuxiliaryClass, systemPossSuperiors # mayContain, mustContain # subClassOf , # auxiliaryClass # possSuperiors # ConfirmExistenceofReferencedAttributesOrClasses($attrClassList) "systemmaycontain" { If ($changetypeValue -like "modify" -or $changetypeValue -like "ntdsSchemaModify") { Write-Host "[ERROR] : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" $global:errorCount += 1 } Else { Write-Host $changeTypeValue " operation, so system attribute addition is permitted!" ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } } "systemmustcontain" { If ($changetypeValue -like "modify" -or $changetypeValue -like "ntdsSchemaModify") { Write-Host "[ERROR] : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" $global:errorCount += 1 } Else { Write-Host $changeTypeValue " operation, so system attribute addition is permitted!" ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } } "systemauxiliaryclass" { If ($changetypeValue -like "modify" -or $changetypeValue -like "ntdsSchemaModify") { Write-Host "[ERROR] : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" $global:errorCount += 1 } Else { Write-Host $changeTypeValue " operation, so system attribute addition is permitted!" ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } } "systemposssuperiors" { If ($changetypeValue -like "modify" -or $changetypeValue -like "ntdsSchemaModify") { Write-Host "[ERROR] : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : A systemOnly attribute is being added to an existing object. This operation is not allowed. Addition of systemOnly attributes while creating new objects is permitted!" $global:errorCount += 1 } Else { Write-Host $changeTypeValue " operation, so system attribute addition is permitted!" ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } } "m*contain" { $values[1] = $values[1].TrimStart() ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } "subclassof" { $values[1] = $values[1].TrimStart() ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } "*auxiliaryclass" { $values[1] = $values[1].TrimStart() ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } "posssuperiors" { $values[1] = $values[1].TrimStart() ConfirmExistenceofReferencedAttributesOrClasses($values[1]) } "ismemberofpartialattributeset" { $values[1] = $values[1].ToLower() If($values[1].CompareTo("true") -eq 0) { Write-Host "[WARNING] : Attribute has been marked as a member of the partial attribute set. Please confirm this requirement!" -Foregroundcolor Yellow Add-Content -path $outputLDIFFile -value "### WARNING : Attribute has been marked as a member of the partial attribute set. Please confirm this requirement!" $global:warningCount += 1 } } "schemaupgradeinprogress" { $values[1] = $values[1].ToLower() If($values[1].CompareTo("true") -eq 0) { Write-Host "[ERROR] : Invalid element. Please consider removing it!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : Invalid element. Please consider removing it!" $global:errorCount += 1 } } "schemaupdatenow" { $values[1] = $values[1].TrimStart() If($values[1].CompareTo("1") -eq 0) { $schemaUpdateNowFlag = "TRUE" } } "oid" { $oid = $values[1].TrimStart(); } "objectclasscategory" { $values[1] = $values[1].TrimStart() switch ($values[1]) { "structural" { Add-Content -path $outputLDIFFile -value "###ObjectClassCategory : Structural" } "auxiliary" { Add-Content -path $outputLDIFFile -value "###ObjectClassCategory : Auxiliary" } "abstract" { Add-Content -path $outputLDIFFile -value "###ObjectClassCategory : Abstract" } "88" { Add-Content -path $outputLDIFFile -value "###ObjectClassCategory : 88" } default { Write-Host "[ERROR] : The value provided for objectClassCategory " $values[1] " is not valid. Please correct it and try again. Accepted values are Structural, Auxiliary, Abstract, 88!" -Foregroundcolor Red Add-Content -path $outputLDIFFile -value "### ERROR : The value provided for objectClassCategory " $values[1] " is not valid. Please correct it and try again. Accepted values are Structural, Auxiliary, Abstract, 88!" $global:errorCount += 1 } } } "rdnattid" { $values[1] = $values[1].TrimStart() checkSyntaxOfRdnattidAttribute($values[1]) } "changetype" { $values[1]=$values[1].ToLower() switch($values[1]) { "add" { Write-Host "Allowed changeType" } "ntdsschemaadd" { Write-Host "Allowed changeType" } "modify" { Write-Host "Allowed changeType" } "ntdsschemamodify" { Write-Host "Allowed changeType" } "delete" { Write-Host "Allowed changeType" } "ntdsschemadelete" { Write-Host "Allowed changeType" } "ntdsschemamodrdn" { Write-Host "Allowed changeType" } default { Write-Host "[ERROR] : ChangeType " $values[1] " is invalid. Allowed changeType values are - add, ntdsschemaadd, modify, ntdsschemamodify, delete, ntdsschemadelete, ntdsschemamodrdn." Add-Content -path $outputLDIFFile -value "### ERROR : ChangeType value is invalid. Allowed changeType values are - add, ntdsschemaadd, modify, ntdsschemamodify, delete, ntdsschemadelete, ntdsschemamodrdn." $global:errorCount += 1 } } } } Write-Host "*******************************************************************************************************************" -Foregroundcolor DarkCyan } } Write-Host "`n +++ SUMMARY +++" -Foregroundcolor Cyan Write-Host " Errors....: " $errorCount -Foregroundcolor Red Write-Host " Warnings..: " $warningCount -Foregroundcolor Yellow Write-Host "REMARK: This script just helps you to validate new schema extensions and give you more confidence about the new" -Foregroundcolor Red Write-Host "extensions. However, it is in no way a full replacement of designing, validating, testing and implementing the" -Foregroundcolor Red Write-Host "extension. YOU remain responsible for the end-to-end process of correctly implementing any new extension into" -Foregroundcolor Red Write-Host "the schema!" -Foregroundcolor Red # Now that the whole file has been checked go through the $linkedAttrsOIDList and $linkedAttrsDisplayNameList to make sure the reference counts are > 0 # A count of zero means that a forward link was created but a back link was never created for it } # Main Program Function global:ExtensionChecker() { Write-Host "`nValidating Extensions...`n" # Get The Script Path $scriptFolderPath = (Get-Location).Path # Process All Specified Input Files And Get The Combined Content $consolidatedLDIFFileContent = $null $consolidatedLDIFFile = "combinedSchemaExtensions.ldif" Clear-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) ForEach ($inputLDIFFile in $inputLDIFFileList) { Write-Host "`nInput File To Validate..........:" $inputLDIFFile If(!(Test-Path $inputLDIFFile)) { Write-Host "`nInput file provided " $inputLDIFFile " does not exist. Please check the path and try again.`n" -Foregroundcolor Red Exit } $inputLDIFFileContent = Get-Content $inputLDIFFile Add-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) "############################################################################################################" Add-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) "### INPUT FILE: $inputLDIFFile" Add-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) "###---------------------------------------------------------------------------------------------------------" Add-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) $inputLDIFFileContent } Write-Host "`nConsolidated Input File Used....:" $($scriptFolderPath + "\" + $consolidatedLDIFFile) $consolidatedLDIFFileContent = Get-Content $($scriptFolderPath + "\" + $consolidatedLDIFFile) # Create A New Output File For The Results If ($outputLDIFFile -notmatch ":\\") { $outputLDIFFile = $scriptFolderPath + "\" + $outputLDIFFile } Write-Host "`nResults File With Corrections...:" $outputLDIFFile New-Item -ItemType file $outputLDIFFile -force | Out-Null # Export The Schema If Not Schema File Was Specified If (!$currentSchemaLDIFFile) { $ThisADForest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $fqdnSchemaFSMO = $ThisADForest.SchemaRoleOwner.Name Write-Host "`nNo current schema file specified. Attempting to grab the schema from the Schema FSMO ($fqdnSchemaFSMO)..." $currentSchemaLDIFFile = $scriptFolderPath + "\currentSchemaLDIFFile.ldif" CMD.EXE /C "START /Wait LDIFDE.EXE -s $fqdnSchemaFSMO -f $currentSchemaLDIFFile -d #SchemaNamingContext -c #DefaultNamingContext DC=X" } # Get The Content From The Current Schema File Write-Host "`nCurrent Schema File.............:" $currentSchemaLDIFFile Write-Host "" If(!(Test-Path $currentSchemaLDIFFile)) { Write-Host "`nSchema file " $currentSchemaLDIFFile " does not exist. Please check the path and try again.`n" -Foregroundcolor Red Exit } Else { $currentSchemaLDIFFileContent = Get-Content $currentSchemaLDIFFile } # Go For It And Validate The Extensions ValidateExtensions } # Execute Main Program ExtensionChecker($args)

-

Below you will see the example output of the script. On purpose I created many conflicts, so that you are able to see what happens when you find any.

image

Figure 1: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 2: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 3: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 4: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 5: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 6: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 7: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 8: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 9: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 10: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 11: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 12: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 13: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 14: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 15: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 16: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

image

Figure 17: Sample/Example Out Of The "AD Schema Extension Conflict Analyzer" PowerShell Script

-

Cheers,

Jorge

———————————————————————————————

* This posting is provided "AS IS" with no warranties and confers no rights!

* Always evaluate/test yourself before using/implementing this!

* DISCLAIMER: https://jorgequestforknowledge.wordpress.com/disclaimer/

———————————————————————————————

############### Jorge’s Quest For Knowledge #############

######### http://JorgeQuestForKnowledge.wordpress.com/ ########

———————————————————————————————

Posted in Active Directory Domain Services (ADDS), PowerShell, Schema, Tooling/Scripting | Leave a Comment »

(2014-07-17) Exporting And Importing Password Settings Objects

Posted by Jorge on 2014-07-17


If you need to export and import your Password Settings Object from one AD domain to the other, you can use the following PowerShell commands:

# SOURCE ENVIRONMENT Import-Module ActiveDirectory Get-ADFineGrainedPasswordPolicy -Filter * -Properties * | Export-CliXml C:\TEMP\AllPSOs.xml # TARGET ENVIRONMENT Import-Module ActiveDirectory Import-CliXml C:\TEMP\AllPSOs.xml | %{ New-ADFineGrainedPasswordPolicy -Name $_.Name ` -ComplexityEnabled $_.ComplexityEnabled ` -Description $_.Description ` -DisplayName $_.DisplayName ` -LockoutDuration $_.LockoutDuration ` -LockoutObservationWindow $_.LockoutObservationWindow ` -LockoutThreshold $_.LockoutThreshold ` -MaxPasswordAge $_.MaxPasswordAge ` -MinPasswordAge $_.MinPasswordAge ` -MinPasswordLength $_.MinPasswordLength ` -PasswordHistoryCount $_.PasswordHistoryCount ` -Precedence $_.Precedence ` -ReversibleEncryptionEnabled $_.ReversibleEncryptionEnabled Add-ADFineGrainedPasswordPolicySubject ` -Identity $_.Name ` -Subjects $_.AppliesTo }

-

Cheers,

Jorge

———————————————————————————————

* This posting is provided "AS IS" with no warranties and confers no rights!

* Always evaluate/test yourself before using/implementing this!

* DISCLAIMER: https://jorgequestforknowledge.wordpress.com/disclaimer/

———————————————————————————————

############### Jorge’s Quest For Knowledge #############

######### http://JorgeQuestForKnowledge.wordpress.com/ ########

———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Fine Grained Password Policies, PowerShell, Tooling/Scripting | Leave a Comment »

(2014-07-16) Active Directory Schema Deactivations (Defunct)

Posted by Jorge on 2014-07-16


When thinking about adjusting the AD schema (also applies to the ADLDS schema) you can categorize those adjustments into the following categories:

Schema Extensions

The most common schema adjustment is a schema extension. With a schema extension, new attribute and object class definitions are added to the schema, to be able to store data with specific requirements. A schema extension is easier and has a lower impact as it does not affect existing applications. However, it does have higher risk, as schema extensions cannot be undone. It must be done correctly the first time. TEST!

-

Schema Modifications

Schema modifications are less common than schema extensions. Changing, the schema definition of either object classes or attributes can adversely affect the application(s) using those definitions. For example, if an application expects to find specific information about all users in the enterprise by querying a GC, the application will be negatively impacted if the corresponding attributes are suddenly configured not to replicate to GCs. However, depending on the type of change, it can positively improve the functioning of application(s). For example, if one or more applications are using one or more specific attributes in queries and those attributes are not (yet) indexed, configuring those attributes to be indexed will improve the performance of those applications. Any change of any kind to existing schema definitions must carefully be evaluated and tested to fully understand all the consequences! TEST!

-

Schema Deactivations

It is not possible to delete schema definitions. However, when running at least W2K3 DCs, it is possible to deactivate schema definitions (attributes and object classes) if those are not required anymore. Are you even running at least forest functional level "Windows Server 2003", then you will be able to reuse the object identifier (governsId and attributeId values), the ldapDisplayName, and the schemaIdGUID that were associated with the defunct attribute or object class. This allows you to redefine the schema definition if it was mistakenly added to the schema.

Only custom schema definitions (non-default AD) can be deactivated. It is not possible to deactivate default AD schema definitions. To be able to deactivate an attribute, the attribute must not be defined on any active object class in any way (mayContain, mustContain, systemMayContain, systemMustContain, rDNAttId). To be able to deactivate an object class, the object class must not be defined on any other active object class in any way (auxiliaryClass, possSuperiors, systemAuxiliaryClass, systemPossSuperiors, subClassOf).

When an attribute or object class is deactivated, the corresponding data stored in AD is not removed automatically. Although data can be deleted afterwards, it is preferred to remove it manually prior to the deactivation as it cannot be edited (except deletion) anymore afterwards. Therefore, before deactivating an attribute, make sure to clear all attribute values on any object using that attribute. When deactivating an abstract or auxiliary object class, make sure to also clear all attribute values on any object using that abstract or auxiliary object class, and when also those attributes are not used in another object class in any way. When deactivating an structural object class, make sure to delete all instances of that object class from the directory service.

-

For additional information about adjusting the AD schema, please see:

-

In this blog post I’m going to focus on schema deactivations, and the effects when querying for information.

-

In any of three articles, available through the links above, you will read something similar to:

Effects of deactivating a schema object on schema updates

After a class salesUser is deactivated, any subsequent addition or modification of instances of salesUser fails as if salesUser has been deleted from the system; the same error codes are returned as if salesUser never existed at all. For example, creating a new instance of salesUser fails, and trying to modify or rename an existing instance of salesUser fails. Similarly, if an attribute hasPager is deactivated, hasPager is treated as nonexistent for new object creations, and attempting to modify (add or replace) the value of hasPager in an existing object fails.

However, you can still search for and delete existing instances of deactivated schema objects. For example, you can still search for all existing instances of salesUser and delete them, if necessary. Note that what you are doing is searching for and deleting objects that were created in the directory using the deactivated class, salesUser, not deleting the actual class definition from the schema. Similarly, you can search for all instances that have a value for the attribute hasPager and delete hasPager from the existing objects. This facilitates cleanup after a schema object is deactivated. If you decide that a class is not needed anymore, you can deactivate it so that no one can use it for any modifications. You can then clean up the existing instances of the class by searching for all instances and deleting them. Active Directory does not perform any automatic cleanup of data instances after a schema object is deactivated.

Similarly, you can deactivate an attribute and clean up all its instances. Note that, for a deactivated attribute, you can delete only the entire attribute from an object, not certain values of the attribute. For example, if hasPager is a multivalue attribute and an object has more than one value for hasPager, you cannot delete only one value of hasPager from the object.

-

I do not fully agree with the statements above! And….I’m going to explain WHY! Keep reading as I’m going to use examples.

-

I extend the AD schema with:

  • An attribute called "xTRAOccupation" and configured it on the user objectClass
  • An objectClass called "car" and configured it with attributes already existing in AD

-

See the schema definitions below…

image

Figure 1: AD Schema Extended With New Attribute Called "xTRAOccupation"

-

image

Figure 2: AD Schema Extended With New Object Class Called "car"

-

Querying AD for all instances of the "user" objectClass with any value in the new attribute definition, results in….

image

Figure 3: All Instances Of The "user" Object Class With Any Value In The New Attribute Called "xTRAOccupation" (8 Objects)

-

Querying AD for all instances of the "car" objectClass, results in….

image

Figure 4: All Instances Of The "car" Object Class (2 Objects)

-

Now lets start and deactivate the newly created "car" objectClass, by setting isDefunct to TRUE, refresh/reload the AD schema and then perform the same query as shown in figure 4.

Now, querying AD for all instances of the "car" objectClass, results in….

image

Figure 5: All Instances Of The "car" Object Class After Deactivation (0 Objects)

-

The objectClass has been deactivated, so you cannot query AD anymore for its name. The only way to query for those instances is to not use the the lDAPDisplayName, but rather use the OID defined in the "governsID" attribute (see figure 2). Combining ADFIND with ADMOD to delete the instances found will succeed. Of course you can use any other tool/script to perform the same actions

image

Figure 6: All Instances Of The "car" Object Class After Deactivation (Using The OID Instead Of The lDAPDisplayName) (2 Objects)

-

Now lets start and deactivate the newly created "xTRAOccupation" attribute, by setting isDefunct to TRUE, refresh/reload the AD schema and then perform the same query as shown in figure 3.

Now, querying AD for all instances of the "user" objectClass with any value in the "xTRAOccupation" attribute definition, results in….

image

Figure 7: All Instances Of The "user" Object Class With Any Value In The Deactivated "xTRAOccupation" Attribute (0 Objects)

-

Although the existing instances of the "user" objectClass have the data/values in the attribute "xTRAOccupation", you cannot query AD for that attribute because it has been unassociated from the "user" objectClass, which was a requirement to be able to deactivate the attribute.

-

So, the moral of the story here is:

  • Clean up your data of attributes to be deactivated BEFORE deactivating the corresponding attributes!!!
  • Clean up your data of objectClasses to be deactivated BEFORE deactivating the corresponding objectClasses by using the lDAPDisplayName of the objectClass in the query OR clean up your data of objectClasses to be deactivated AFTER deactivating the corresponding objectClasses by using the OID of the deactivated objectClass in the AD query!!!

-

image

Figure 8: Deleting All Instances Of The "car" Object Class Using The OID Of The Deactivated Object Class IN The AD Query

-

Querying for the deleted objects that have a deactivated objectClass….

image

Figure 9: Querying All Deleted Instances Of The "car" Object Class Using The OID Of The Deactivated Object Class In The AD Query

-

Now….lets say I want to reanimate the deleted object instances of the deactivated objectClass. Is that possible? No, because the objectClass is deactivated it cannot be reused again! See below!

image

Figure 10: Reanimating All Instances Of The "car" Object Class Using The OID Of The Deactivated Object Class IN The AD Query

-

After reactivating the objectClass called "car" by configuring the attribute isDefunct to <NOT SET>, refreshing/reloading the AD schema and then trying the undelete, results in…

image

Figure 11: Reanimating All Instances Of The "car" Object Class Using The lDAPDisplayName Of The Reactivated Object Class In The AD Query

-

NOTE: You could also have used the OID of the reactivated objectClass in the AD query!

-

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Deactivate/Defunct, Schema | Leave a Comment »

 
%d bloggers like this: