DSCResources/AddsDomainPrincipals/AddsDomainPrincipals.schema.psm1

configuration AddsDomainPrincipals
{
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $DomainDN,

        [Parameter()]
        [Hashtable[]]
        $Computers,

        [Parameter()]
        [Hashtable[]]
        $Users,

        [Parameter()]
        [Hashtable]
        $KDSKey,

        [Parameter()]
        [Hashtable[]]
        $ManagedServiceAccounts
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName ActiveDirectoryDsc

    function AddMemberOf
    {
        param (
            [Parameter()]
            [string]
            $ExecutionName,

            [Parameter()]
            [string]
            $ExecutionType,

            [Parameter()]
            [string]
            $AccountName,

            [Parameter()]
            [String[]]
            $MemberOf
        )

        if ( $null -ne $MemberOf -and $MemberOf.Count -gt 0 )
        {
            Script "$($ExecutionName)_MemberOf"
            {
                TestScript =
                {
                    # get current member groups in MemberOf
                    $currentGroups = Get-ADPrincipalGroupMembership -Identity $using:AccountName | `
                            Where-Object { $using:MemberOf -contains $_.SamAccountName } | `
                                Select-Object -ExpandProperty SamAccountName

                    Write-Verbose "ADPrincipal '$using:AccountName' is member of required groups: $($currentGroups -join ', ')"

                    $missingGroups = $using:MemberOf | Where-Object { -not ($currentGroups -contains $_) }

                    if ($missingGroups.Count -eq 0)
                    {
                        return $true
                    }

                    Write-Verbose "ADPrincipal '$using:AccountName' is not member of required groups: $($missingGroups -join ', ')"
                    return $false
                }
                SetScript  =
                {
                    Add-ADPrincipalGroupMembership -Identity $using:AccountName -MemberOf $using:MemberOf
                }
                GetScript  = { return 'NA' }
                DependsOn  = "[$ExecutionType]$ExecutionName"
            }
        }
    }

    if ( $null -ne $Computers )
    {
        foreach ($computer in $Computers)
        {
            # Remove Case Sensitivity of ordered Dictionary or Hashtables
            $computer = @{} + $computer

            # save group list
            $memberOf = $computer.MemberOf
            $computer.Remove( 'MemberOf' )

            $executionName = "adComputer_$($computer.ComputerName)"

            (Get-DscSplattedResource -ResourceName ADComputer -ExecutionName $executionName -Properties $computer -NoInvoke).Invoke($computer)

            AddMemberOf -ExecutionName $executionName -ExecutionType ADComputer -AccountName "$($computer.ComputerName)$" -MemberOf $memberOf
        }
    }

    if ( $null -ne $Users )
    {
        # convert DN to Fqdn
        $pattern = '(?i)DC=(?<name>\w+){1,}?\b'
        $domainName = ([RegEx]::Matches($DomainDN, $pattern) | ForEach-Object { $_.groups['name'] }) -join '.'

        foreach ($user in $Users)
        {
            # Remove Case Sensitivity of ordered Dictionary or Hashtables
            $user = @{} + $user

            if ([string]::IsNullOrWhiteSpace($user.DomainName))
            {
                $user.DomainName = $domainName
            }

            # save group list
            $memberOf = $user.MemberOf
            $user.Remove( 'MemberOf' )

            $executionName = "adUser_$($user.UserName)"

            (Get-DscSplattedResource -ResourceName ADUser -ExecutionName $executionName -Properties $user -NoInvoke).Invoke($user)

            AddMemberOf -ExecutionName $executionName -ExecutionType ADUser -AccountName $user.UserName -MemberOf $memberOf
        }
    }

    $dependsOnKdsKey = $null

    if ( $null -ne $KDSKey )
    {
        (Get-DscSplattedResource -ResourceName ADKDSKey -ExecutionName 'adKDSKey' -Properties $KDSKey -NoInvoke).Invoke($KDSKey)

        $dependsOnKdsKey = '[ADKDSKey]adKDSKey'
    }

    if ( $null -ne $ManagedServiceAccounts )
    {
        foreach ($svcAccount in $ManagedServiceAccounts)
        {
            # Remove Case Sensitivity of ordered Dictionary or Hashtables
            $svcAccount = @{} + $svcAccount

            # save group list and computer
            $memberOf = $svcAccount.MemberOf
            $computer = $svcAccount.Computer
            $svcAccount.Remove( 'MemberOf' )
            $svcAccount.Remove( 'Computer' )

            if ( $null -ne $dependsOnKdsKey )
            {
                $svcAccount.DependsOn = $dependsOnKdsKey
            }

            $executionName = "adMSA_$($svcAccount.ServiceAccountName)"

            (Get-DscSplattedResource -ResourceName ADManagedServiceAccount -ExecutionName $executionName -Properties $svcAccount -NoInvoke).Invoke($svcAccount)

            # assign a managed service account to AD computer
            if ( -not [string]::IsNullOrWhitespace($computer) )
            {
                if ( $svcAccount.AccountType -eq 'Standalone' )
                {
                    $svcAccountName = $svcAccount.ServiceAccountName

                    Script "$($executionName)_Computer"
                    {
                        TestScript =
                        {
                            Write-Verbose "Get managed service accounts hosted by AD computer '$using:computer'..."
                            $result = Get-ADComputerServiceAccount -Identity $using:computer -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $using:svcAccountName }

                            if ( $null -ne $result -and $result.Name -eq $using:svcAccountName )
                            {
                                Write-Verbose "OK - managed service account '$using:svcAccountName' is assigned to AD computer '$using:computer'."
                                return $true;
                            }

                            Write-Verbose "Managed service account '$using:svcAccountName' is NOT assigned to AD computer '$using:computer'."
                            return $false
                        }
                        SetScript  =
                        {
                            Write-Verbose "Assign managed service account '$using:svcAccountName' to AD computer '$using:computer'"
                            Add-ADComputerServiceAccount -Computer $using:computer -ServiceAccount $using:svcAccountName
                        }
                        GetScript  = { return 'NA' }
                        DependsOn  = "[ADManagedServiceAccount]$executionName"
                    }
                }
                else
                {
                    throw "ERROR: Only a standalone managed service account can be assigned to an AD computer account."
                }
            }

            # append $ to acoountname to identify it as MSA
            AddMemberOf -ExecutionName $executionName -ExecutionType ADManagedServiceAccount -AccountName "$($svcAccount.ServiceAccountName)$" -MemberOf $memberOf
        }
    }
}