public/add-ADSchemaMod.ps1

<#
.Synopsis
   Adds predefined AD Schema modifications related to RBAC
.DESCRIPTION
   Implements predefined schema mods like sshpublickey, sudoroles, and LAPS.
   Must be run from a Domain Controller with Schema admin rights
.EXAMPLE
   Add-ADSchemaMod -name sshpublickey
     Adds 'sshPublicKey' attribute to user objects in AD. This allows you to define public key auth centrally and avoid trhe use of authorized_keys.
     This requires a correctly-configured sssd and sshd on the Linux clients, example below:
     sssd.conf:
        [sssd]
        services = nss, pam, sudo, ssh
        <...snip...>
        [domain/$DOMAIN]
        ldap_user_extra_attrs = sshPublicKey:sshPublicKey,phone:telephoneNumber,email:mail
        ldap_user_ssh_public_key = sshPublicKey
     sshd_config:
        <....snip...>
        AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
        AuthorizedKeysCommandUser nobody
.EXAMPLE
   Add-ADSchemaMod -name SudoRoles
      Adds 'sudoRole' object definition to the schema, allowing Linux hosts using a correctly-configured sssd to pull sudoers definitions via LDAP.
      Examples of this config can be found under the `misc` folder, but the key elements are a correctly-configured nsswitch.conf and sssd.conf:
      nsswitch.conf:
         <...snip...>
         sudoers: sss files
      sssd.conf: (replace variables)
         [sssd]
         services = nss, pam, sudo, ssh
         <...snip...>
         [domain/$DOMAIN]
         ldap_netgroup_search_base = OU=Netgroups,OU=LinuxFeatures,DC=$DOMAIN?subtree?
         ldap_sudo_search_base = OU=SudoRoles,OU=LinuxFeatures,DC=$DOMAIN?subtree?
 
.EXAMPLE
   Add-ADSchemaMod -name LAPS
 
.OUTPUTS
   $Null
.NOTES
   Requires Schema Admin, Enterprise Admin, and Domain Admin.
   This will briefly turn on the "Schema Updates Allowed" registry setting for NTDS
#>

function add-ADSchemaMod {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        # The name of the schema modification. Tab-completion will show available options
        [ValidateScript({[bool](get-RBACSchemaMods -name $_)})]
        [ArgumentCompleter( {@($(get-RBACSchemaMods).name)})]
        [string]$Name
    )
    begin {
        $DomainProperties = get-ADDomain
        $domainDN = $domainProperties.distinguishedName
        $DomainDNS = $domainProperties.DNSRoot
        $DomainSID = $domainProperties.DomainSID
        $SID_DomainAdmin = "$DomainSID-512"
        $SID_EnterpriseAdmin = "$DomainSID-519"
        $SID_SchemaAdmin = "$DomainSID-518"
        $GroupInfo = get-currentUserGroups

        $PreChecks = @{
            IsDomainAdmin = $groupInfo.sid.contains($SID_DomainAdmin)
            IsEnterpriseAdmin = $groupInfo.sid.contains($SID_EnterpriseAdmin)
            IsSchemaAdmin = $groupInfo.sid.contains($SID_SchemaAdmin)
            OnDomainController = [bool](get-wmiObject -query "Select * from Win32_OperatingSystem where ProductType='2'")
        }
        if ($PreChecks.values -contains $false) {
            write-loghandler -level "warning" -message "Please run this from a domain controller with Domain-, Enterprise-, and Schema-Admin rights."
            write-loghandler -level "warning" -message "See below for failing checks"
            foreach ($check in $PreChecks.getEnumerator()) {
                write-loghandler -level "warning" -message (" * {0,-24} : {1}" -f $check.name, $check.value)
            }
            Break
        }
        $schemaBaseDirectory = "\\$DomainDNS\SYSVOL\$DomainDNS"

        $registryMod = @{
            path = "Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NTDS\Parameters"
            Name = "Schema Update Allowed"
        }
        if ($PSCmdlet.ShouldProcess("$($registryMod.path) : $($registryMod.Name)","Enabling Schema modifications in registry")) {
            New-ItemProperty @RegistryMod -propertyType DWord -value 1 -confirm:$false
        }

    }
    Process {
        $schemaDef = get-RBACSchemaMods -name $name
        $modName = $schemaDef.name
        $LDIF_Document = $schemaDef.Value.Document
        $SchemaCommand = $schemaDef.Value.Command
        $schemaFileName = "$modName.schema.activeDirectory"
        $schemaFilePath = "$schemaBaseDirectory\$SchemaFileName"
        if ($LDIF_Document) {
            if ($PSCmdlet.ShouldProcess($SchemaFilePath, "Importing Schema file")) {
                $LDIF_Document | out-file -filePath $SchemaFilePath
                ldifde -i -f $schemaFilePath -c "CN=Schema,CN=Configuration,DC=X" "CN=Schema,CN=Configuration,$DomainDN" -j "C:\"
            }
        }
        if ($SchemaCommand) {
            if ($PSCmdlet.ShouldProcess($schemaCommand,"Running Schema Command")) {
                invoke-Expression -command $schemaCommand
            }
        }
    }

    End {
        if ($PSCmdlet.ShouldProcess("$($registryMod.path) : $($registryMod.Name)","Disabling Schema modifications in registry")) {
            Remove-ItemProperty @RegistryMod -confirm:$False
        }
    }
}