Skip to content

Instantly share code, notes, and snippets.

@IISResetMe
Last active January 18, 2021 13:50
Show Gist options
  • Save IISResetMe/a9bee01a652d9e7d56e267e7f6fedec3 to your computer and use it in GitHub Desktop.
Save IISResetMe/a9bee01a652d9e7d56e267e7f6fedec3 to your computer and use it in GitHub Desktop.
<#
.Synopsis
Extends the Active Directory Schema with linked account attributes
.DESCRIPTION
Extends the Active Directory Schema with linked account attributes for use
with Microsoft tiered admin account model and similar account segregation
paradigms.
Adds a linked attribute pair to the schema to reflect account ownership, and
an auxiliary class to associate them with the user object class.
.EXAMPLE
Extend the schema and return the new schema elements
PS C:\> Add-ADSchemaAdminAccountExtensions -OrgPrefix 'myCompany' -PassThru -Confirm:$false
attributeID : 1.2.840.113556.1.8000.666.840424748.300608405.1140883067.626181007.520.1
DistinguishedName : cn=myCompany-sourceAccount,CN=Schema,CN=Configuration,DC=corp,DC=mycompany,DC=tld
Name : myCompany-sourceAccount
ObjectClass : attributeSchema
ObjectGUID : bedc07a2-3de3-488b-bae1-606f8bc20f0e
attributeID : 1.2.840.113556.1.8000.666.840424748.300608405.1140883067.626181007.520.2
DistinguishedName : cn=myCompany-adminAccounts,CN=Schema,CN=Configuration,DC=corp,DC=mycompany,DC=tld
Name : myCompany-adminAccounts
ObjectClass : attributeSchema
ObjectGUID : 98fd4190-c8f4-4728-a89b-21504e54e71e
DistinguishedName : cn=myCompany-adminAccountExtensions,CN=Schema,CN=Configuration,DC=corp,DC=mycompany,DC=tld
governsID : 1.2.840.113556.1.8000.666.840424748.300608405.1140883067.626181007.520.3
Name : myCompany-adminAccountExtensions
ObjectClass : classSchema
ObjectGUID : 4d228e7f-f2bc-4d2b-960e-4531ef03130e
.EXAMPLE
In the following example, we extend the schema and use Set-ADUser to update existing admin accounts.
This change will be reflected on the target account automatically
PS C:\> Add-ADSchemaAdminAccountExtensions -OrgPrefix 'iRM' -Confirm:$false
PS C:\> Set-ADUser alice-admin -Replace @{ 'iRM-sourceAccount' = (Get-ADUser alice).distinguishedName }
PS C:\> Get-ADUser alice -Properties iRM-adminAccounts |% iRM-adminAccounts |Get-ADUser |select sAMAccountName
sAMAccountName
--------------
alice-admin
.NOTES
Requires membership of Schema Admins in the forest of the computer, as well as the RSAT ActiveDirectory module
#>
function Add-ADSchemaAdminAccountExtensions {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory = $true)]
[string]$orgPrefix,
[ValidatePattern('^1(?:\.\d+)+$')]
[string]$orgOID,
[switch]$Force,
[switch]$PassThru
)
$ErrorActionPreference = 'Stop'
if(($orgPrefix.Length -lt 2 -or $orgPrefix -cnotmatch '^\p{Ll}\p{L}+$') -and -not $Force){
throw 'Expected org prefix longer than one character, starting with a lowercase letter'
}
# Discover schema NC
$rootDSE = Get-ADRootDSE
$schemaNC = $rootDSE.schemaNamingContext
# Discover schema master
$schemaMaster = Get-ADObject $schemaNC -Properties fSMORoleOwner | Get-ADDomainController -Identity { $_.fSMORoleOwner }
# Re-bind against RootDSE on schema master
$rootDSE = [ADSI]::new("LDAP://$($schemaMaster.HostName)/RootDSE")
# Prepare the refresh the schema a couple of times!!!
$schemaRefresh = {
$rootDSE.Put("schemaUpdateNow", 1)
$rootDSE.SetInfo()
}
# Generate OID prefix if none is provided
if(-not $PSBoundParameters.ContainsKey('orgOID')){
# Slightly modified version of Microsoft's OID generation script
# https://gallery.technet.microsoft.com/scriptcenter/56b78004-40d0-41cf-b95e-6e795b2e8a06
$guidBytes = New-Guid |% ToByteArray
$orgOID = @(
"1.2.840.113556.1.8000.2554"
0..7 |%{
[BitConverter]::ToUInt16($guidBytes, $_ * 2)
}
) -join '.'
}
# Define defaults
$server = @{
Server = $schemaMaster.HostName
}
$newSchemaDefaults = @{
Path = $schemaNC
PassThru = $true
Confirm = $false
} + $server
# Actual extensions
if (-not ($Force -or $PSCmdlet.ShouldProcess("Tiered AD Admin Account Schema Extensions", "Add"))) {
return
}
# Forward-link attribute to indicate the owner of a non-primary account
$fwdAttrName = "${orgPrefix}-sourceAccount".Trim("-")
Write-Verbose "Creating attributeSchema '$fwdAttrName'"
$fwdAttributeSchema = New-ADObject -Name $fwdAttrName -Type attributeSchema -OtherAttributes @{
adminDisplayName = $fwdAttrName
lDAPDisplayName = $fwdAttrName
oMSyntax = 127
attributeSyntax = "2.5.5.1"
attributeID = "${orgOID}.1"
isSingleValued = $true
isMemberOfPartialAttributeSet = $true
adminDescription = "Account owner"
instanceType = 4
linkID = '1.2.840.113556.1.2.50'
showInAdvancedViewOnly = $false
} @newSchemaDefaults
& $schemaRefresh
$fwdAttributeSchema = $fwdAttributeSchema | Get-ADObject -Properties attributeID @server
# Backlink attribute reflecting the non-primary accounts referencing this user principal as source/owner
$bckAttrName = "${orgPrefix}-adminAccounts".Trim("-")
Write-Verbose "Creating attributeSchema '$bckAttrName'"
$bckAttributeSchema = New-ADObject -Name $bckAttrName -Type attributeSchema -OtherAttributes @{
adminDisplayName = $bckAttrName
lDAPDisplayName = $bckAttrName
oMSyntax = 127
attributeSyntax = "2.5.5.1"
attributeID = "${orgOID}.2"
isSingleValued = $false
isMemberOfPartialAttributeSet = $true
adminDescription = "Associated admin accounts"
instanceType = 4
showInAdvancedViewOnly = $false
linkID = $fwdAttributeSchema.attributeID
} @newSchemaDefaults
& $schemaRefresh
$bckAttributeSchema = $bckAttributeSchema | Get-ADObject -Properties attributeID @server
# Auxiliary class that may contain our attributes
$auxClassName = "${orgPrefix}-adminAccountExtensions".Trim("-")
Write-Verbose "Creating classSchema '$auxClassName'"
$auxClassSchema = New-ADObject -Name $auxClassName -Type classSchema -OtherAttributes @{
adminDisplayName = $auxClassName
lDAPDisplayName = $auxClassName
governsID = "${orgOID}.3"
mayContain = @(
$fwdAttributeSchema.attributeID
$bckAttributeSchema.attributeID
)
objectClassCategory = '3'
adminDescription = 'This class adds optional admin account relationship links to user account objects'
subClassOf = 'top'
} @newSchemaDefaults
& $schemaRefresh
$auxClassSchema = $auxClassSchema | Get-ADObject -Properties governsID @server
# Allow the 'user' object class to be extended with our new auxiliary class
Write-Verbose "Adding auxiliary class '$auxClassName' to the 'user' class"
Get-ADObject -SearchBase $schemaNC -Filter "Name -like 'user'" @server | Set-ADObject -Add @{ auxiliaryClass = $auxClassSchema.governsID } @server
& $schemaRefresh
if ($PassThru) {
$fwdAttributeSchema
$bckAttributeSchema
$auxClassSchema
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment