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!)

(2014-07-19) Understanding Claim Rule Language In AD FS 2.0 And Higher

Posted by Jorge on 2014-07-19


In addition to previous blog posts that I had found regarding the claims rule language, I found yet another great article about the same topic. Kudos and credits of course go to the writer/contributors of the wiki post/article.

-

SOURCE: Understanding Claim Rule Language in AD FS 2.0 & Higher

-

<QUOTE SOURCE=”Understanding Claim Rule Language in AD FS 2.0 & Higher”>

Introduction

Claims Rules follow a basic pipeline.  The rules define which claims are accepted, processed, and eventually sent to the relying party.  Claim Rules are defined as a property of the Claims Provider Trust (incoming claims) and the Relying Party Trust (outgoing claims).  Basic claim passing and transformations can be handled using the built in Claim Rule Templates.

-

Understanding Claim Sets

It is important to understand claim sets as part of the claims pipeline.  When claims come in, they are a part of the incoming claim set.  After claims are processed by claim rules, they become part of the outgoing claim set.  An important piece to understand is there is an incoming and outgoing claim set for the Claims Provider Trust and for the Relying Party Trust, so there are two places claims can be processed before leaving AD FS.

  1. Claims come into the Claims Provider Trust as the incoming claim set
  2. Claims are processed using claim rules and become part of the outgoing claim set
  3. The outgoing claim set is passed to the Relying Party Trust as the incoming claim set for the Relying Party Trust
  4. The set of claims are processed using claim rules and become part of the final outgoing claim set

Read more about the claims pipeline here

-

General Syntax of the Claim Rule Language

There are two parts to each rule:

  • Condition statement
  • Issuance statement

-

If the condition statement is true, the issuance statement will be executed.  If the condition statement is false, the engine will move on to the next rule.

Example: Simple Claims Rule Syntax

c:[Type == "http://contoso.com/department"%5D
=>issue(Type = “http://adatum.com/department”, Value = c.Value);

This example takes an incoming claim http://contoso.com/department and issues a new claim http://adatum.com/department with the same value as the incoming claim.  These claim types are URIs in the HTTP format but can also be in the URN format. URIs are not URLs and do not need to be actual pages on the Internet or intranet.

-

Condition Statements

Condition statements look at all incoming claims and determine if there is one that matches the condition. The following properties can be queried in an incoming claim:

  • Type
  • Value
  • Issuer
  • OriginalIssuer
  • ValueType

-

The format for querying an incoming claim is c:[query] where the variable c represents a claim in the incoming claim set.  The query can be more specific and check for more than one property.  See some of the examples below to get an idea of how the format works.  The two examples below are not complete syntax, as they are missing the issuance statement.

Example: Check for an incoming claim type http://contoso.com/department

c:[type == http://contoso.com/department]

-

Example: Check for an incoming claim type http://contoso.com/department with a value of sales

c:[type == "http://contoso.com/department&quot;, value == "sales"]

-

Condition statements are optional in the claims rule language.  By leaving the condition statement blank, the claim rule will always evaluate as true.

Example: Issue a claim http://contoso.com/partner with the value of adatum to all incoming claim sets

=>issue(Type = “http://contoso.com/partner”, Value = "adatum");

-

Issuance Statements

There are two types of issuance statements to use.

  • Add – adds the claim to the incoming claim set
  • Issue – adds the claim to the outgoing claim set

-

The ADD issuance statement is used to add additional claims to the incoming claim set so that subsequent claim rules can use them for processing. The ISSUE issuance statement is used to add claims to the outgoing claim.

Example: Issue a claim http://contoso.com/department to the outgoing claim set

=> issue(type = "http://contoso.com/department&quot;, value = "marketing");

-

Example: Add a claim http://contoso.com/partner to the incoming claim set

=> add(type = "http://contoso.com/partner&quot;, value = "adatum");

-

Example: Check for an incoming claim type http://contoso.com/email and if found, issue a claim http://contoso.com/role with the value of Exchange User

c:[type == "http://contoso.com/emailaddress"%5D
=> issue(type = "http://contoso.com/role&quot;, value = "Exchange User");

-

The entire incoming claim can be passed on or certain values inside the claim can be used in the outgoing claim. Use the variable c in the issuance statement to pass the entire claim or parts of the claim.

Example: Check for an incoming claim type http://contoso.com/role and if found, issue the exact same claim to the outgoing claim set

c:[type == "http://contoso.com/role"%5D
=> issue(claim = c);

-

Example: Check for an incoming claim type http://contoso.com/role and if found, issue a claim http://adatum.com/role with the same value of the incoming claim

c:[type == "http://contoso.com/role"%5D
=> issue(type = "http://adatum.com/role&quot;, value = c.Value);

-

Multiple Conditions

Another possibility is to have multiple conditions, and if all conditions evaluate to true, run the issuance statement.  Each condition is joined using the && special operator.  There is not a logical OR operator.  To accomplish an OR, create separate claim rules.

Example: Check for an incoming claim type http://contoso.com/role with a value of Editor and separate incoming claim type  http://contoso.com/role with a value of Manager. If both are found, issue a claim http://contoso.com/role with the value of Managing Editor

c1:[type == "http://contoso.com/role&quot;, value=="Editor"] &&
c2:[type == "http://contoso.com/role&quot;, value=="Manager"]
=> issue(type = "http://contoso.com/role&quot;, value = "Managing Editor");

-

Combining Values

The values of each individual incoming claim can be accessed and joined using the special operator + in the issuance statement.

Example: Check for an incoming claim type http://contoso.com/location and separate incoming claim type http://contoso.com/role. If both are found, issue a claim http://contoso.com/targetedrole combining the values of the incoming roles

c1:[type == "http://contoso.com/location"%5D &&
c2:[type == "http://contoso.com/role"%5D
=> issue(type = "http://contoso/targetedrole&quot;, value = c1.Value + " " c2.Value);

-

Example Incoming Claims:

"http://contoso.com/location&quot; is “Seattle”

“http://contoso.com/role” is “Editor”

-

Example Outgoing Claim:

“http://contoso.com/targetedrole” is “Seattle Editor”

-

Aggregate Functions

Typical claims rules will issue an output claim for each match it finds.  Aggregate functions will issue or add a single claim regardless of the number of matches. The EXISTS function serves this purpose.

-
EXISTS

Example: Claims rule without an Aggregate Function

c:[type == "http://contoso.com/emailaddress"%5D
=> issue(type = "http://contoso.com/role&quot;, value = "Exchange User");

-

This example would issue multiple http://contoso.com/role claims if the incoming claim set had multiple email addresses.  If that is not desired, use the EXISTS function as shown below.

Example: Check for any incoming claims with the type http://contoso.com/emailaddress and if any are found, issue a single claim type http://contoso.com/role with the value of Exchange User:

EXISTS([type == "http://contoso.com/emailaddress"%5D)
=> issue(type = "http://contoso/role&quot;, value = "Exchange User");

-

NOT EXISTS

There is also an option to use NOT EXISTS to issue claims if there is no incoming claim that matches the condition. This can be useful for subsequent rules that combine values.

Example: Claim rule that will only work if the incoming claim set has both claims

c1:[type == "http://contoso.com/location"%5D &&
c2:[type == "http://contoso.com/role"%5D
=> issue(type = "http://contoso/targetedrole&quot;, value = c1.Value + " " c2.Value);

-

This claim rule will only trigger if the incoming claim set has a http://contoso.com/location and a http://contoso.com/role incoming claim. If the location claim is not present, the outgoing set will not contain a http://contoso.com/targetedrole claim. If it is desired that all outgoing claim sets have this particular claim, the NOT EXISTS function can be used in a separate claim rule.

Example: Claim rule that uses the NOT EXISTS Aggregate Function

NOT EXISTS([type == "http://contoso.com/location"%5D)
=> add(type = "http://contoso/location&quot;, value = "Unknown");

-

Here is an example set of incoming and outgoing claims if the NOT EXISTS Aggregate function claim rule is included with the multiple condition claim rule.

Example Incoming Claims:

“http://contoso.com/role” is “Editor”

-

Example Outgoing Claim:

“http://contoso.com/targetedrole” is “Unknown Editor”

-

Another good use for the NOT EXISTS aggregate function is to restrict access to certain applications based on group membership.

Example: Issuance Authorization claim rule that uses the NOT EXISTS Aggregate Function

NOT EXISTS([type == "http://contoso.com/group&quot;,  Value =~ "^(?i)ADFSUser"])
=> issue(type = "http://schemas.microosft.com/authorization/claims/deny&quot;, value = "DenyUsersWithClaim");

This claim rule will deny users access to the relying party if they are not a member of a group that starts with ADFSUser.  It group name evaluation is not case sensitive. The syntax uses Regular Expressions (regex) which is explained in more detail in the next section.

-

COUNT

Another aggregate function available in AD FS 2.0 is the COUNT function.  The claim will only be issued if the condition statement is true.

Example: Claim Rule that uses the COUNT Aggregate Function

COUNT([type == http://contoso.com/proxyAddresses"%5D) >= 2
=> issue(type = "http://contoso.com/MultipleEmails&quot;, value = "True");

This claim rule will issue the claim if the user has two or more proxy address claims.

-

Using Regular Expressions

Regular Expressions (regex) can be used in the condition or issuance statements.  In a condition statement, regex allows similar matches to evaluate true.  In issuance statements, regex allows parts of the string values to be used in the outgoing claim. Regular Expressions use special characters to perform various tasks inside a string.

Character Description Examples
$ Matches the end of a string contoso.com$ matches a string that ends with "contoso.com"
bob@contoso.com would evaluate true
bob@contoso2.com would evaluate false
^ Matches the beginning of a string ^bob matches a string that starts with "bob"
bob.smith@contoso.com would evaluate true
bonny.smith@contoso.com would evaluate false

-

Example: Using the $ expression.  Matches strings that end in "contoso.com"

c:[type == "http://contoso.com/email&quot;, Value =~ "contoso.com$"]
=> issue (claim = c);

-

Example: Using the ^ expression.  Matches strings that start with "bob"

c:[type == "http://contoso.com/email&quot;, Value =~ "^bob"]
=> issue (claim = c);

-

Example: Matches strings that contain "bob"

c:[type == "http://contoso.com/email&quot;, Value =~ "bob"]
=> issue (claim = c);

-

The string matching in the above examples are case-sensitive.  To perform a string match that ignores case, use a pattern (?i) in front of the string.

Example: Matches strings that contain "bob" regardless of case

c:[type == "http://contoso.com/email&quot;, Value =~ "(?i)bob"]
=> issue (claim = c);

-

For more advanced RegEx examples, view this article: http://social.technet.microsoft.com/wiki/contents/articles/16161.ad-fs-2-0-using-regex-in-the-claims-rule-language.aspx

-

Querying Attribute Stores

Active Directory is the default store created when AD FS 2.0 is installed.  SQL attribute stores and LDAP attribute stores can also be defined.  The condition statement remains the same, but the issuance statement changes depending on which attribute store is used.

-

SQL Attribute Stores

If user data is located in a SQL database, the Claim Rule Language can query the database and generate claims based on the information in the database.

Example: Claim rule using a SQL Attribute Store

c:[type == "http://contoso.com/emailaddress"%5D
=> issue (store = "Custom SQL Store", types = ("http://contoso.com/age&quot;, "http://contoso.com/purchasinglimit&quot;), query = "SELECT age,purchasinglimit FROM users WHERE email={0}",param = c.value);

This rule looks for an incoming http://contoso.com/emailaddress claim, then queries the SQL store Custom SQL Store for the age and purchasing limit associated with the value of the claim (email address).  It then issues two claims, http://contoso.com/age and http://contoso.com/purchasinglimit with the values stored in the SQL database.
As the example shows, multiple claims can be issued from a single rule.  The query is a standard transact-SQL statement.  The {0} variable is associated with the first param value.  If there are multiple param values, they will be associated in order {0}, {1}, {2}, etc.

-

LDAP Attribute Stores
If user data is located in a LDAP store, the Claim Rule Language can query it and generate claims based on the information in the store.
Example: Claim rule using an LDAP Attribute Store

c:[type == "http://contoso.com/emailaddress"%5D
=> issue (store = "Custom LDAP Store", types = ("http://contoso.com/age&quot;, "http://contoso.com/purchasinglimit&quot;), query = "mail={0};age,purchasinglimit", param = c.value);

The example shown is similar to the SQL attribute example.  The difference is the query parameter.

Format of an LDAP query in a claim rule: QUERY = "<query_filter>;<attributes>"

Read more about Attribute Stores here.

-

Links to Additional Content

There are many good articles that supplement the data in this article.

-

Advanced Topics

Here are some articles that go over more advanced topics:

</QUOTE SOURCE=”Understanding Claim Rule Language in AD FS 2.0 & Higher”>

-

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 Federation Services (ADFS), Claims Rule Language | 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 »

(2014-06-25) Azure AD Premium – Trial Mode Now Available

Posted by Jorge on 2014-06-25


Since past weekend it is now possible try out Azure AD Premium without paying anything! For every directory listed in your subscription, you can get 100 Azure AD Premium licenses for free for 90 days!

image

Figure 1: Microsoft Azure Main Page

-

To enable the trial, logon to your Azure subscription (paid, trial, MSDN, etc.) with your Azure AD global admin. After clicking on your directory as shown in figure 1, navigate to the "Licenses" tab as shown in figure 2 below.

image

Figure 2: Microsoft Azure AD Directory Page

-

To enable the Trial, click on "Try Azure Active Directory Premium Now" as shown in figure 3.

image

Figure 3: Enabling The Trial For Azure AD Premium

-

You will then see:

image

Figure 4: Azure AD Premium Trial Notification

-

To be able to actually use the Azure AD Premium features, you must assign one license to the global admin of the directory and each assigned user must have a "usage location" specified in its profile.

-

Have fun!

-

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 Trial, Windows Azure Active Directory | Leave a Comment »

(2014-06-23) A Hotfix Rollup Package (Build 4.1.3559.0) Is Available for Forefront Identity Manager 2010 R2

Posted by Jorge on 2014-06-23


Microsoft released a new hotfix for FIM 2010 R2 with build 4.1.3559.0. What it fixes can be found in this blog post. For additional or detailed info see MS-KBQ2969673

Download link

-

Issues that are fixed or features that are added in this update

This update fixes the following issues or adds the following features that were not previously documented in the Microsoft Knowledge Base.

FIM add-ins and extensions

Issue 1

After the FIM Password Reset add-in is installed in Windows 7, the “Create a Password Reset Disk” feature is available when you press Ctrl+Alt+Del and then you click Change a Password.

-

FIM Certificate Management

Issue 1

When you try to remove a certification authority (CA) that is bound to Certificate Management (CM) by using the CLMutil -removeca command, you experience the following symptoms:

  • Actions such as enroll, renew or revoke that are performed on certificates that are bound to the removed CA cause an exception to be returned.
  • When you try to edit or remove a certificate template that was related to the removed CA, an exception is displayed on the CM portal.

Note.  These same symptoms occur when the CA is stopped or unavailable.

-

Changes to the symptoms after you apply this update:

  • Actions such as enroll, renew or revoke that are performed on certificates that are bound to the removed CA cause a well-defined error message to be displayed.
  • Removing a certificate template is possible. Removing the template for any removed CA is also possible.
  • Changing a certificate template that is related to the removed CA causes a well-defined error message to be displayed.

-

Changes to the clmutil.exe tool after you apply this update:

The following commands are added:

  • decommissionca

Syntax: [-force] -decomissionca CA_ID
Example 1: clmutil -force -decommissionca 1
Example 2: clmutil -decommissionca 1

Note.  You may receive the following warning message when you run the clmutil command:

There are outstanding certificates for CA 1, cannot decommission. Please use -force flag if you still want to decommission CA

  • recommisionca

Syntax: -recommissionca CA_ID
Example: clmutil -recommissionca 1

Note.  CA_ID can be obtained by using the following command:

  • listca

-

Changes in the Web Portal user interface after you apply this update

When you change a profile template that references a certificate template that was originally hosted by a CA and that is marked as decommissioned, the following conditions are true:

  • The CA is unavailable in the Add Certificate Template dialog box.
  • Users who enroll in the profile template receive the following warning message that is displayed at the top of the enrollment screen:
    • The Certification Authority CA2.proseware.com\FIM CM CA cannot be contacted as it is marked as decommissioned.

Note.  If the profile template includes certificate templates from a disabled or re-enabled CA, users will be unable to enroll in that profile template. This causes this same warning message to be displayed.

In the profile template properties, clicking a certificate template that was originally associated with a now-decommissioned CA displays all registered CAs that expose this certificate template. This lets the administrator select the appropriate CA.

Note.  If no registered CAs expose the certificate template, the list of CAs on the screen is blank.

-

FIM Synchronization Service

Feature 1

This update includes ECMA 2.3. In this version, it is now possible to add custom schema pages that will be listed in the Synchronization Service Manager user interface. An example of how this feature is used can be seen in the Generic SQL connector.

Please be aware that the Microsoft.MetadirectoryServicesEx.dll is updated in this update. See the “Known issues in this update” section for information about how to update the configuration files to reflect this change.

-

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 Forefront Identity Manager (FIM) bHold, Forefront Identity Manager (FIM) Certificate Management, Forefront Identity Manager (FIM) Portal, Forefront Identity Manager (FIM) Sync, Updates, Updates, Updates, Updates | Leave a Comment »

(2014-06-22) A Hotfix Rollup Package (Build 4.1.3510.0) Is Available for Forefront Identity Manager 2010 R2

Posted by Jorge on 2014-06-22


Microsoft released a new hotfix for FIM 2010 R2 with build 4.1.3510.0. What it fixes can be found in this blog post. For additional or detailed info see MS-KBQ2934816

Download link

-

Issues that are fixed or features that are added in this update

This update fixes the following issues or adds the following features that were not previously documented in the Microsoft Knowledge Base.

FIM Service and Portal

Issue 1

If a FIMService instance loses connection to the FIMService database, it can may stop processing FIM Service MA export requests. This results in failed FIM Service MA exports with a run status of "stopped-server." Additionally, the following exception is logged in the Forefront Identity Manager event log:

System.Data: System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.

-

Issue 2

You use a multivalue attribute in a dynamic set. This dynamic set is used in a Transition Out management policy rule. If two or more elements are removed from the attribute in a single request, and if of the elements triggers the Transition-Out MPR, the request fails, and you receive the following exception:

Microsoft.ResourceManagement.WebServices.Exceptions.UnwillingToPerformException: Other —> System.Data.SqlClient.SqlException: Reraised Error 2627, Level 14, State 1, Procedure DoEvaluateRequestInner, Line 1073, Message: Violation of PRIMARY KEY constraint ‘PK__#1B54B73__5330D0771D3CFFB1′. Cannot insert duplicate key in object ‘dbo.@transitionOutApplicableRuleBuffer’.

-

Issue 3

When an export run in the FIM Service MA includes updates to the Filter attribute of multiple dynamic groups, a "failed-modification-via-web-services" exception can be returned. When you review the details of the exception that is returned, you see that an SQL Deadlock occurred.

-

FIM Synchronization Service

Issue 1

In the Active Directory management agent, changes to a multivalue attribute such as proxyAddresses are not synchronized to the metaverse in the following scenario:

  • A change to proxyAddresses is exported to the Active Directory for User1.
  • A second change is made to proxyAddresses outside the synchronization service.
  • A Delta Import run profile is run to confirm the exported changes.

-

Issue 2

If an exception is thrown by the management agent’s password extension during password synchronization, the password interface at which the exception was thrown is discarded. This can cause high processor usage on the computer that is hosting the FIM Synchronization Service when the computer processes password synchronization to multiple management agents.
After you apply this update, exceptions of type PasswordPolicyException and PasswordIllFormedException no longer discard the password interface. This enables the interface to be reused for another password operation to the connected data source.

-

BHOLD

Issue 1
If a regular expression policy rule is applied for an ABA role, all applied ABA roles are stuck in the pending state for the users and are never assigned.

-

Issue 2

If a user has an ABA role, and if you try to change a user attribute that is not related to the ABA role, all ABA roles are again marked for policy validation. Additionally, assigned permissions are removed and assigned back.

-

Issue 3

When you have more than 500 permissions in BHOLD and search permissions on the Supervised Permissions tab of Default Supervisor Role, no results are returned, and you are returned to the previous page.

-

Issue 4

When you configure an attribute-based role assignment for a role and then you try to click the Show Impact link in the policies section of a role, you receive the following error message:

Object reference not set to an instance of an object

-

Issue 5

The SP1 build does not let you re-create a permission that was removed from BHOLD earlier.

-

Issue 6

When you try to change and save a user without changing the end date, you receive the following error message:

Invalid date format

-

Issue 7

When you try to move an organization unit in the BHOLD Core Portal, you receive the following warning message:

Session ID missing: The Session ID is not found in URL. You can continue working using the menu at the left

-

Issue 8

The "User by Role" report cannot be generated after the limit of 50,000 users is reached. Additionally, you receive an "Out of memory" exception.

-

Issue 9

In the BHOLD Self-Service Portal, the role information screen under the Role Requests-Current Roles tab displays no role descriptions or permission details.

-

Issue 10

When you log on as a typical end-user in the BHOLD Service Portal, the "My Roles" screen is displayed as an empty page even though the user is assigned with both "active" and "proposed" roles.

-

Issue 11

The BHOLD – Access Management agent cannot perform full imports because of an SQL time-out issue that occurs when there is a load of more than 50,000 to 100,000 users.

-

Issue 12

BHOLD cannot add permissions to a user by using the BHOLD Connector after these permissions are denied.

-

Issue 13

When a steward in the BHOLD Attestation portal has multiple resources to attest and is working on approving or denying permissions for one user, other permissions for a different user are changed in the user interface.

-

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 Forefront Identity Manager (FIM) bHold, Forefront Identity Manager (FIM) Certificate Management, Forefront Identity Manager (FIM) Portal, Forefront Identity Manager (FIM) Sync, Updates, Updates, Updates, Updates | Leave a Comment »

(2014-06-21) Installing PES v3.2 On W2K12(R2)

Posted by Jorge on 2014-06-21


To download PES see this blog post.

If in addition to migration objects (users, groups, computers, etc.) you also need to migrate passwords, then you also need to install the Password Export Service (PES) on a(ny) writable DC in the source AD domain. PES cannot be installed on a read-only domain controller (RODC). The default behavior of ADMT, when migrating passwords, is to configure every target user account with "change password at next logon", unless "password never expires" (most likely service accounts) or "smartcard is required for interactive logon" on the source user account. After the password migration, it is also possible to revert the setting of "change password at next logon" by using PowerShell, ADMOD or any other LDAP modification tool.

Assuming the OU "OU=Migrated-Users,DC=ADCORP,DC=LAB" contains all migrated user accounts…

  • PowerShell –> Get-ADUser -SearchBase "OU=Migrated-Users,DC=ADCORP,DC=LAB" -Filter * | %{Set-ADUser $_.SamAccountName -ChangePasswordAtLogon $false}
  • ADFIND/ADMOD –> ADFIND -b "OU=Migrated-Users,DC=ADCORP,DC=LAB" -f "(&(objectCategory=person)(objectClass=user))" -adcsv | ADMOD pwdLastSet::-1

-

PES has a very tight relation with ADMT. Because of that you must first create a so called encryption key on the server where ADMT is installed before even starting the installation of PES!

-

To create the encryption key on the server with ADMT:

  • Open a command prompt window and navigate to the folder "C:\Windows\ADMT"
  • ADMT key /option:create /sourcedomain:ADCORP.LAB /keyfile:C:\Windows\ADMT\ADMTPESEncryptionKeyFile.pes /keypassword:*

image

Figure 1: Creating The Encryption File For PES On The Server With ADMT

-

Securely transfer the encryption file to the RWDC that will host the PES service. You can now start the installation of PES.

image

Figure 1: Selecting The Encryption File For PES

-

image

Figure 2: Specifying The Password Securing The Encryption File

-

The "Password Export Server (PES)" can be configured to run with a service account. This enhancement removes the dependency on the "pre-Windows 2000 compatible access" group that PREVIOUS should contain the well-known security principals "Everyone" and "Anonymous Logon" (in W2K only "Everyone" as that by default already contained "Anonymous Logon"). THEREFORE, preferably use a service account instead of the Local System account.

image

Figure 3: Specifying The Service Account That Will Be Used By The Password Export Service

-

The PES service account will be granted the "logon as a service" user right. After the installing you must reboot the RWDC.

-

For additional info see the ADMT Migration Guide.

-

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 ADMT, Migration/Upgrade, PES | 4 Comments »

(2014-06-20) Installing ADMT v3.2 On W2K12(R2)

Posted by Jorge on 2014-06-20


To download ADMT see this blog post.

To be able to install the latest version of ADMT, it must be either a GUI based member server (preferred!) or a writable DC (RWDC). Server Core installations are not supported, nor is it possible to install on a read-only domain controller (RODC).

Remember that you require either SQL Express or SQL Server to be able to use ADMT. While using W2K12R2 I was not able to use the Windows Internal Database feature. For the free version of SQL I had to download and install SQL Express. The latest available version (at the time of writing) of SQL Express (Microsoft SQL Server 2012 Service Pack 1 (SP1) Express) can be downloaded through this link. During the installation of SQL Express will may be invited to download and install additional updates. After installing SQL (Express), you can start the ADMT install. It is not possible to upgrade current ADMT installations. You must uninstall old ADMT versions before installing the latest version. Assuming you chose to use SQL Express, the default instance name is .\SQLEXPRESS as you can see below in the picture.

image

Figure 1: The Default Connection String For SQL Express

-

image

Figure 2: ADMT Being Installed

-

image

Figure 3: The Possibility To Import Old ADMT Databases

-

image

Figure 4: The Installation Of ADMT Finishing Successfully

-

On W2K12(R2), after clicking the Start button, do not search "ADMT", but rather search for "Active Directory Migration Tool" or just "Migration".

-

image

Figure 5: The ADMT GUI

-

For additional info see the ADMT Migration Guide

-

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 ADMT, Migration/Upgrade, PES | Leave a Comment »

 
%d bloggers like this: