#You need to execute this script on Read-Write domain controller with AD account which is member of Schema Admins group.
#SecureMFA OTP provider when operates in AD mode ("auth_mode": "AD") it requires custom Active Directory (AD) attributes to be created to store OTP data for the user.
#This action cannot be undone and needs to be tested in your TEST domain first before moving into PRODUCTION.
#New AD Schema Attributes will be added into custom SecureMFA Auxiliary Class and that Class will be added into Existing User Class as AD Schema best practices suggest.
#OID numbers for custom attributes are from SecureMFA Private Enterprise range assigned by . Which do not overlap with other vendors OIDs numbers used to create custom AD attributes.

function ADDADSchemaAttributetoClass($SchemaAttribute)
    [bool]$reset = $true
            $reset = $true
            Write-Host "Adding $SchemaAttribute into SecureMFA Auxiliary Class.($attempts)" -ForegroundColor Green

            #Get AD Schema details
            $dse =  Get-ADRootDSE
            $schemaPath = $dse.schemaNamingContext       
            $type = 'attributeSchema'  

            #ADD AD Schema Attribute To SecureMFA Class
            $Schema = Get-ADObject -SearchBase $schemaPath -Filter "name -eq `'SecureMFA`'"
            $Schema | Set-ADObject -Add @{mayContain = $SchemaAttribute}  -ErrorAction stop             
        catch [Exception]
            Write-Host "Waiting for AD schema attribute refresh ... Please note this action can take a few minutes to complete." -ForegroundColor Yellow
            $reset = $false

        if ($attempts -gt 0 -and $reset -eq $false) { sleep $sleepInSeconds } 
    } until ($attempts -le 0 -or $reset -eq $true)
    return $reset    

function ADDADAttribute{
        [Boolean]$IsSingleValued = $True 
    #Attribute values by type
    switch ($AttributeType) {
                'String'            {$attributeSyntax = '';  $omSyntax = 20}
                'Int'               {$attributeSyntax = '';  $omSyntax = 2}
                'Time'              {$attributeSyntax = ''; $omSyntax = 24}
                'Boolean'           {$attributeSyntax = '';  $omSyntax = 1}
                Default {}
    #AD atribute params-------
    $attributes = @{
    lDAPDisplayName = $Name;
    attributeId = $AttributeID;
    oMSyntax = $omSyntax;
    attributeSyntax = $attributeSyntax;
    isSingleValued = $IsSingleValued;
    adminDescription = $Description;
    searchflags = 0
    #If AD Attribute doesn't exist create one.
    if(!(Get-ADObject -SearchBase "$((Get-ADRootDSE).SchemaNamingContext)" -Filter {lDAPDisplayName -eq $Name})) {
    New-ADObject -Name $Name -Type $type -Path $schemapath -OtherAttributes $attributes
    Write-host "AD attribute $Name has been created." -ForegroundColor Green
    Else {Write-host "AD attribute $Name allready exist in AD." -ForegroundColor yellow}

#START Script

#Get AD Schema details
$dse =  Get-ADRootDSE
$schemaPath = $dse.schemaNamingContext       
$type = 'attributeSchema'

#Create a New AD Schema Auxiliary Class for SecureMFA
$Name = 'SecureMFA'
$attributes = @{
            governsId = ''
            adminDescription = 'SecureMFA Class to host OTP attributes'
            objectClass =  'classSchema'
            ldapDisplayName = $Name
            adminDisplayName =  $Name
            objectClassCategory = 3
            systemOnly =  $FALSE
            # subclassOf: top
            subclassOf = ""
            # rdnAttId: cn
            rdnAttId = ""

#If Schema Auxiliary Class for SecureMFA doesn't exist create one.
if(!(Get-ADObject -SearchBase (Get-ADRootDSE).SchemaNamingContext -Filter {name -like "SecureMFA"})) {
New-ADObject -Name $Name -Type 'classSchema' -Path $schemapath -OtherAttributes $attributes
Write-host "A New AD Schema Auxiliary Class SecureMFA has been created." -ForegroundColor Green
Else {Write-host "Auxiliary Class SecureMFA allready exist in AD." -ForegroundColor Yellow}

#Add AD Schema SecureMFA Auxiliary Class To default User Class
$auxClass = Get-ADObject -SearchBase $schemaPath -Filter "name -eq `'$Name`'" -Properties governsID
$classToAddTo  = Get-ADObject -SearchBase $schemaPath -Filter "name -eq `'user`'"
$classToAddTo | Set-ADObject -Add @{auxiliaryClass = $($auxClass.governsID)}

#START Creating AD attributes

ADDADAttribute -Name 'sMFA-OTP-secret' -Description 'User Secret attribute' -AttributeID '' -AttributeType String -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-logon' -Description 'User logon status attribute' -AttributeID '' -AttributeType Int -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-lastlogon' -Description 'User laslogon attribute' -AttributeID '' -AttributeType Time -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-logoncount' -Description 'User logoncount attribute' -AttributeID '' -AttributeType Int -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-failedlogoncount' -Description 'User failedlogoncount attribute' -AttributeID '' -AttributeType Int -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-failedlastlogon' -Description 'User failedlastlogon attribute' -AttributeID '' -AttributeType Time -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-failedcode' -Description 'User failedcode attribute' -AttributeID '' -AttributeType String -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-logonip' -Description 'User logonip attribute' -AttributeID '' -AttributeType String -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-useragent' -Description 'User useragent attribute' -AttributeID '' -AttributeType String -IsSingleValued $true
ADDADAttribute -Name 'sMFA-OTP-interval' -Description 'User interval attribute' -AttributeID '' -AttributeType Int -IsSingleValued $false

#Refresh AD schema
$dse.schemaUpdateNow = $true
Write-host "Refreshing AD Schema." -ForegroundColor Green

[bool]$reset = $false
#START Adding AD Schema Attributes to User Class:

@("sMFA-OTP-secret","sMFA-OTP-logon","sMFA-OTP-lastlogon","sMFA-OTP-logoncount","sMFA-OTP-failedlogoncount","sMFA-OTP-failedlastlogon","sMFA-OTP-failedcode","sMFA-OTP-logonip","sMFA-OTP-useragent","sMFA-OTP-interval") | foreach {    
    $SchemaAttribute = $_.trim()
    $reset = ADDADSchemaAttributetoClass $SchemaAttribute
    if($reset) {Write-host "AD attribute $SchemaAttribute have been added into SecureMFA Auxiliary Class." -ForegroundColor Green}
    else {Write-host "AD attribute $SchemaAttribute has failed to add into SecureMFA Auxiliary Class. Please try again later when AD replication for attributes is completed across your environment." -ForegroundColor red}    

#Refresh AD schema
$dse.schemaUpdateNow = $true

write-host "Custom sMFA attributes have been created. Please note that depending on AD replication policies it may take a while to show a new custom attribute for user object in AD when using GUI tools." -ForegroundColor Cyan