DSCResources/MSFT_ADReadOnlyDomainControllerAccount/MSFT_ADReadOnlyDomainControllerAccount.psm1

$resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
$modulesFolderPath = Join-Path -Path $resourceModulePath -ChildPath 'Modules'

$aDCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'ActiveDirectoryDsc.Common'
Import-Module -Name $aDCommonModulePath

$dscResourceCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'DscResource.Common'
Import-Module -Name $dscResourceCommonModulePath

$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'

<#
    .SYNOPSIS
        Returns the current state of the read only domain controller account.
 
    .PARAMETER DomainControllerAccountName
        Provide the name of the Read Domain Controller Account which will be created.
 
    .PARAMETER DomainName
        Provide the FQDN of the domain the Read Domain Controller Account is being created in.
 
    .PARAMETER Credential
        Specifies the credential for the account used to add the read only domain controller account.
 
    .PARAMETER SiteName
        Provide the name of the site you want the Read Only Domain Controller Account to be added to.
 
    .PARAMETER InstallDns
        Specifies if the DNS Server service should be installed and configured on
        the read only domain controller. If this is not set the default value of the parameter
        InstallDns of the cmdlet Add-ADDSReadOnlyDomainControllerAccount is used.
        The parameter `InstallDns` is only used during the provisioning of a read only domain
        controller. The parameter cannot be used to install or uninstall the DNS
        server on an already provisioned read only domain controller.
 
        Not used in Get-TargetResource.
 
    .NOTES
        Used Functions:
            Name | Module
            ------------------------------------------------|--------------------------
            Get-ADDomain | ActiveDirectory
            Get-ADDomainControllerPasswordReplicationPolicy | ActiveDirectory
            Get-DomainControllerObject | ActiveDirectoryDsc.Common
            Assert-Module | DscResource.Common
            New-ObjectNotFoundException | DscResource.Common
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainControllerAccountName,

        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainName,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(Mandatory = $true)]
        [System.String]
        $SiteName,

        [Parameter()]
        [System.Boolean]
        $InstallDns
    )

    Assert-Module -ModuleName 'ActiveDirectory'

    Write-Verbose -Message ($script:localizedData.ResolveDomainName -f $DomainName)

    $Domain = Get-DomainObject -Identity $DomainName -Credential $Credential -ErrorOnUnexpectedExceptions -Verbose:$VerbosePreference

    if (-not $Domain)
    {
        $errorMessage = $script:localizedData.MissingDomain -f $DomainName
        New-ObjectNotFoundException -Message $errorMessage
    }

    Write-Verbose -Message ($script:localizedData.DomainPresent -f $DomainName)

    $domainControllerObject = Get-DomainControllerObject `
        -DomainName $DomainName -ComputerName $DomainControllerAccountName -Credential $Credential

    if ($domainControllerObject.IsReadOnly)
    {
        Write-Verbose -Message ($script:localizedData.IsReadOnlyDomainControllerAccount -f
            $domainControllerObject.Name, $domainControllerObject.Domain)

        # Retrieve any user or group that is a delegated administrator via the ManagedBy attribute
        $delegateAdministratorAccountName = $null
        $domainControllerComputerObject = $domainControllerObject.ComputerObjectDN |
            Get-ADComputer -Properties ManagedBy -Credential $Credential
        if ($domainControllerComputerObject.ManagedBy)
        {
            $domainControllerManagedByObject = $domainControllerComputerObject.ManagedBy |
                Get-ADObject -Properties objectSid -Credential $Credential

            $delegateAdministratorAccountName = Resolve-SamAccountName -ObjectSid $domainControllerManagedByObject.objectSid
        }

        $allowedPasswordReplicationAccountName = (
            Get-ADDomainControllerPasswordReplicationPolicy -Allowed -Identity $domainControllerObject |
            ForEach-Object -MemberName sAMAccountName)
        $deniedPasswordReplicationAccountName = (
            Get-ADDomainControllerPasswordReplicationPolicy -Denied -Identity $domainControllerObject |
            ForEach-Object -MemberName sAMAccountName)

        $targetResource = @{
            AllowPasswordReplicationAccountName = @($allowedPasswordReplicationAccountName)
            Credential                          = $Credential
            DelegatedAdministratorAccountName   = $delegateAdministratorAccountName
            DenyPasswordReplicationAccountName  = @($deniedPasswordReplicationAccountName)
            DomainControllerAccountName         = $domainControllerObject.Name
            DomainName                          = $domainControllerObject.Domain
            Ensure                              = $true
            InstallDns                          = $InstallDns
            IsGlobalCatalog                     = $domainControllerObject.IsGlobalCatalog
            SiteName                            = $domainControllerObject.Site
        }
    }
    else
    {
        Write-Verbose -Message ($script:localizedData.NotReadOnlyDomainControllerAccount -f
            $domainControllerObject.Name, $domainControllerObject.Domain)

        $targetResource = @{
            AllowPasswordReplicationAccountName = $null
            Credential                          = $Credential
            DelegatedAdministratorAccountName   = $null
            DenyPasswordReplicationAccountName  = $null
            DomainControllerAccountName         = $DomainControllerAccountName
            DomainName                          = $DomainName
            Ensure                              = $false
            InstallDns                          = $false
            IsGlobalCatalog                     = $false
            SiteName                            = $null
        }
    }

    return $targetResource
}

<#
    .SYNOPSIS
        Creates a read only domain controller account.
 
    .PARAMETER DomainControllerAccountName
        Provide the name of the Read Domain Controller Account which will be created.
 
    .PARAMETER DomainName
        Provide the FQDN of the domain the Read Domain Controller Account is being created in.
 
    .PARAMETER Credential
        Specifies the credential for the account used to add the read only domain controller account.
 
    .PARAMETER SiteName
        Provide the name of the site you want the Read Only Domain Controller Account to be added to.
 
    .PARAMETER IsGlobalCatalog
        Specifies if the read only domain controller will be a Global Catalog (GC).
 
    .PARAMETER DelegatedAdministratorAccountName
        Specifies the user or group that is the delegated administrator of this read only domain controller account.
 
    .PARAMETER AllowPasswordReplicationAccountName
        Provides a list of the users, computers, and groups to add to the password replication allowed list.
 
    .PARAMETER DenyPasswordReplicationAccountName
        Provides a list of the users, computers, and groups to add to the password replication denied list.
 
    .PARAMETER InstallDns
        Specifies if the DNS Server service should be installed and configured on
        the read only domain controller. If this is not set the default value of the parameter
        InstallDns of the cmdlet Add-ADDSReadOnlyDomainControllerAccount is used.
        The parameter `InstallDns` is only used during the provisioning of a read only domain
        controller. The parameter cannot be used to install or uninstall the DNS
        server on an already provisioned read only domain controller.
 
    .NOTES
        Used Functions:
            Name | Module
            ---------------------------------------------------|--------------------------
            Add-ADDSReadOnlyDomainControllerAccount | ActiveDirectory
            Set-ADObject | ActiveDirectory
            Move-ADDirectoryServer | ActiveDirectory
            Remove-ADDomainControllerPasswordReplicationPolicy | ActiveDirectory
            Add-ADDomainControllerPasswordReplicationPolicy | ActiveDirectory
            Get-DomainControllerObject | ActiveDirectoryDsc.Common
            Get-DomainObject | ActiveDirectoryDsc.Common
            New-InvalidOperationException | DscResource.Common
#>

function Set-TargetResource
{
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '',
        Justification = 'Read-Only Domain Controller (RODC) Account Creation support(AllowPasswordReplicationAccountName and DenyPasswordReplicationAccountName)')]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainControllerAccountName,

        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainName,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(Mandatory = $true)]
        [System.String]
        $SiteName,

        [Parameter()]
        [System.Boolean]
        $IsGlobalCatalog,

        [Parameter()]
        [System.String]
        $DelegatedAdministratorAccountName,

        [Parameter()]
        [System.String[]]
        $AllowPasswordReplicationAccountName,

        [Parameter()]
        [System.String[]]
        $DenyPasswordReplicationAccountName,

        [Parameter()]
        [System.Boolean]
        $InstallDns
    )

    $getTargetResourceParameters = @{
        DomainControllerAccountName   = $DomainControllerAccountName
        DomainName                    = $DomainName
        Credential                    = $Credential
        SiteName                      = $SiteName
    }

    $targetResource = Get-TargetResource @getTargetResourceParameters

    if ($targetResource.Ensure -eq $false)
    {
        Write-Verbose -Message ($script:localizedData.Adding -f $DomainControllerAccountName, $DomainName)

        # Read only domain controller is not created so we add it.
        $addADDSReadOnlyDomainControllerAccountParameters = @{
            DomainControllerAccountName   = $DomainControllerAccountName
            DomainName                    = $DomainName
            Credential                    = $Credential
            SiteName                      = $SiteName
            Force                         = $true
        }

        if ($PSBoundParameters.ContainsKey('DelegatedAdministratorAccountName'))
        {
            $addADDSReadOnlyDomainControllerAccountParameters.Add('DelegatedAdministratorAccountName',
                $DelegatedAdministratorAccountName)
        }

        if ($PSBoundParameters.ContainsKey('AllowPasswordReplicationAccountName'))
        {
            $addADDSReadOnlyDomainControllerAccountParameters.Add('AllowPasswordReplicationAccountName',
                $AllowPasswordReplicationAccountName)
        }

        if ($PSBoundParameters.ContainsKey('DenyPasswordReplicationAccountName'))
        {
            $addADDSReadOnlyDomainControllerAccountParameters.Add('DenyPasswordReplicationAccountName',
                $DenyPasswordReplicationAccountName)
        }

        if ($PSBoundParameters.ContainsKey('IsGlobalCatalog') -and $IsGlobalCatalog -eq $false)
        {
            $addADDSReadOnlyDomainControllerAccountParameters.Add('NoGlobalCatalog', $true)
        }

        if ($PSBoundParameters.ContainsKey('InstallDns'))
        {
            $addADDSReadOnlyDomainControllerAccountParameters.Add('InstallDns', $InstallDns)
        }

        Add-ADDSReadOnlyDomainControllerAccount @addADDSReadOnlyDomainControllerAccountParameters

        Write-Verbose -Message ($script:localizedData.Added -f $DomainControllerAccountName, $DomainName)
    }
    elseif ($targetResource.Ensure)
    {
        # Read only domain controller account already created. We check if other properties are in desired state

        Write-Verbose -Message ($script:localizedData.IsReadOnlyDomainControllerAccount -f $DomainControllerAccountName, $DomainName)

        $domainControllerObject = Get-DomainControllerObject `
            -DomainName $DomainName -ComputerName $DomainControllerAccountName -Credential $Credential

        # Check if Node Global Catalog state is correct
        if ($PSBoundParameters.ContainsKey('IsGlobalCatalog') -and
            $targetResource.IsGlobalCatalog -ne $IsGlobalCatalog)
        {
            # RODC is not in the expected Global Catalog state
            if ($IsGlobalCatalog)
            {
                $globalCatalogOptionValue = 1

                Write-Verbose -Message $script:localizedData.AddGlobalCatalog
            }
            else
            {
                $globalCatalogOptionValue = 0

                Write-Verbose -Message $script:localizedData.RemoveGlobalCatalog
            }

            Set-ADObject -Identity $domainControllerObject.NTDSSettingsObjectDN -Replace @{
                options = $globalCatalogOptionValue
            }
        }

        if ($targetResource.SiteName -ne $SiteName)
        {
            # RODC is not in correct site. Move it.
            Write-Verbose -Message ($script:localizedData.MovingDomainController -f
                $targetResource.SiteName, $SiteName)

            Move-ADDirectoryServer -Identity $DomainControllerAccountName -Site $SiteName -Credential $Credential
        }

        if ($PSBoundParameters.ContainsKey('DelegatedAdministratorAccountName') -and
            $targetResource.DelegatedAdministratorAccountName -ne $DelegatedAdministratorAccountName)
        {
            # Set the delegated administrator via the ManagedBy attribute
            Write-Verbose -Message ($script:localizedData.UpdatingDelegatedAdministratorAccountName -f
            $targetResource.DelegatedAdministratorAccountName, $DelegatedAdministratorAccountName)

            $delegateAdministratorAccountSecurityIdentifier = Resolve-SecurityIdentifier -SamAccountName $DelegatedAdministratorAccountName

            Set-ADComputer -Identity $domainControllerObject.ComputerObjectDN `
                -ManagedBy $delegateAdministratorAccountSecurityIdentifier -Credential $Credential
        }

        if ($PSBoundParameters.ContainsKey('AllowPasswordReplicationAccountName'))
        {
            $testMembersParameters = @{
                ExistingMembers = $targetResource.AllowPasswordReplicationAccountName
                Members         = $AllowPasswordReplicationAccountName
            }

            if (-not (Test-Members @testMembersParameters))
            {
                Write-Verbose -Message (
                    $script:localizedData.AllowedSyncAccountsMismatch -f
                    ($targetResource.AllowPasswordReplicationAccountName -join ';'),
                    ($AllowPasswordReplicationAccountName -join ';')
                )

                $getMembersToAddAndRemoveParameters = @{
                    DesiredMembers = $AllowPasswordReplicationAccountName
                    CurrentMembers = $targetResource.AllowPasswordReplicationAccountName
                }

                $getMembersToAddAndRemoveResult = Get-MembersToAddAndRemove @getMembersToAddAndRemoveParameters

                $adPrincipalsToRemove = $getMembersToAddAndRemoveResult.MembersToRemove
                $adPrincipalsToAdd = $getMembersToAddAndRemoveResult.MembersToAdd

                if ($null -ne $adPrincipalsToRemove)
                {
                    $removeADPasswordReplicationPolicy = @{
                        Identity    = $domainControllerObject
                        AllowedList = $adPrincipalsToRemove
                    }

                    Remove-ADDomainControllerPasswordReplicationPolicy @removeADPasswordReplicationPolicy `
                        -Confirm:$false
                }

                if ($null -ne $adPrincipalsToAdd)
                {
                    $addADPasswordReplicationPolicy = @{
                        Identity    = $domainControllerObject
                        AllowedList = $adPrincipalsToAdd
                    }

                    Add-ADDomainControllerPasswordReplicationPolicy @addADPasswordReplicationPolicy
                }
            }
        }

        if ($PSBoundParameters.ContainsKey('DenyPasswordReplicationAccountName'))
        {
            $testMembersParameters = @{
                ExistingMembers = $targetResource.DenyPasswordReplicationAccountName
                Members         = $DenyPasswordReplicationAccountName;
            }

            if (-not (Test-Members @testMembersParameters))
            {
                Write-Verbose -Message (
                    $script:localizedData.DenySyncAccountsMismatch -f
                    ($targetResource.DenyPasswordReplicationAccountName -join ';'),
                    ($DenyPasswordReplicationAccountName -join ';')
                )

                $getMembersToAddAndRemoveParameters = @{
                    DesiredMembers = $DenyPasswordReplicationAccountName
                    CurrentMembers = $targetResource.DenyPasswordReplicationAccountName
                }

                $getMembersToAddAndRemoveResult = Get-MembersToAddAndRemove @getMembersToAddAndRemoveParameters

                $adPrincipalsToRemove = $getMembersToAddAndRemoveResult.MembersToRemove
                $adPrincipalsToAdd = $getMembersToAddAndRemoveResult.MembersToAdd

                if ($null -ne $adPrincipalsToRemove)
                {
                    $removeADPasswordReplicationPolicy = @{
                        Identity   = $domainControllerObject
                        DeniedList = $adPrincipalsToRemove
                    }

                    Remove-ADDomainControllerPasswordReplicationPolicy @removeADPasswordReplicationPolicy `
                        -Confirm:$false
                }

                if ($null -ne $adPrincipalsToAdd)
                {
                    $addADPasswordReplicationPolicy = @{
                        Identity   = $domainControllerObject
                        DeniedList = $adPrincipalsToAdd
                    }

                    Add-ADDomainControllerPasswordReplicationPolicy @addADPasswordReplicationPolicy
                }

            }
        }
    }
}

<#
    .SYNOPSIS
        Determines if the read only domain controller account is in desired state.
 
    .PARAMETER DomainControllerAccountName
        Provide the name of the Read Domain Controller Account which will be created.
 
    .PARAMETER DomainName
        Provide the FQDN of the domain the Read Domain Controller Account is being created in.
 
    .PARAMETER Credential
        Specifies the credential for the account used to add the read only domain controller account.
 
    .PARAMETER SiteName
        Provide the name of the site you want the Read Only Domain Controller Account to be added to.
 
    .PARAMETER IsGlobalCatalog
        Specifies if the read only domain controller will be a Global Catalog (GC).
 
    .PARAMETER DelegatedAdministratorAccountName
        Specifies the user or group that is the delegated administrator of this read only domain controller account.
 
    .PARAMETER AllowPasswordReplicationAccountName
        Provides a list of the users, computers, and groups to add to the password replication allowed list.
 
    .PARAMETER DenyPasswordReplicationAccountName
        Provides a list of the users, computers, and groups to add to the password replication denied list.
 
    .PARAMETER InstallDns
        Specifies if the DNS Server service should be installed and configured on
        the read only domain controller. If this is not set the default value of the parameter
        InstallDns of the cmdlet Add-ADDSReadOnlyDomainControllerAccount is used.
        The parameter `InstallDns` is only used during the provisioning of a read only domain
        controller. The parameter cannot be used to install or uninstall the DNS
        server on an already provisioned read only domain controller.
 
        Not used in Test-TargetResource.
 
    .NOTES
        Used Functions:
            Name | Module
            ------------------------------|--------------------------
            Test-ADReplicationSite | ActiveDirectoryDsc.Common
            Test-Members | ActiveDirectoryDsc.Common
            New-InvalidOperationException | DscResource.Common
            New-ObjectNotFoundException | DscResource.Common
#>

function Test-TargetResource
{
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "",
        Justification = 'Read-Only Domain Controller (RODC) Account Creation support(AllowPasswordReplicationAccountName and DenyPasswordReplicationAccountName)')]
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainControllerAccountName,

        [Parameter(Mandatory = $true)]
        [System.String]
        $DomainName,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(Mandatory = $true)]
        [System.String]
        $SiteName,

        [Parameter()]
        [System.Boolean]
        $IsGlobalCatalog,

        [Parameter()]
        [System.String]
        $DelegatedAdministratorAccountName,

        [Parameter()]
        [System.String[]]
        $AllowPasswordReplicationAccountName,

        [Parameter()]
        [System.String[]]
        $DenyPasswordReplicationAccountName,

        [Parameter()]
        [System.Boolean]
        $InstallDns
    )

    Write-Verbose -Message ($script:localizedData.TestingConfiguration -f $DomainControllerAccountName, $DomainName)

    if (-not (Test-ADReplicationSite -SiteName $SiteName -DomainName $DomainName -Credential $Credential))
    {
        $errorMessage = $script:localizedData.FailedToFindSite -f $SiteName, $DomainName
        New-ObjectNotFoundException -Message $errorMessage
    }

    $getTargetResourceParameters = @{
        DomainControllerAccountName   = $DomainControllerAccountName
        DomainName                    = $DomainName
        Credential                    = $Credential
        SiteName                      = $SiteName
    }

    $existingResource = Get-TargetResource @getTargetResourceParameters

    $testTargetResourceReturnValue = $existingResource.Ensure

    if ($existingResource.SiteName -ne $SiteName)
    {
        Write-Verbose -Message ($script:localizedData.WrongSite -f $existingResource.SiteName, $SiteName)

        $testTargetResourceReturnValue = $false
    }

    # Check Global Catalog Config
    if ($PSBoundParameters.ContainsKey('IsGlobalCatalog') -and $existingResource.IsGlobalCatalog -ne $IsGlobalCatalog)
    {
        if ($IsGlobalCatalog)
        {
            Write-Verbose -Message ($script:localizedData.ExpectedGlobalCatalogEnabled)
        }
        else
        {
            Write-Verbose -Message ($script:localizedData.ExpectedGlobalCatalogDisabled)
        }

        $testTargetResourceReturnValue = $false
    }

    if ($PSBoundParameters.ContainsKey('DelegatedAdministratorAccountName') -and $existingResource.DelegatedAdministratorAccountName -ne $DelegatedAdministratorAccountName)
    {
        Write-Verbose -Message ($script:localizedData.DelegatedAdministratorAccountNameMismatch -f $existingResource.DelegatedAdministratorAccountName, $DelegatedAdministratorAccountName)

        $testTargetResourceReturnValue = $false
    }

    if ($PSBoundParameters.ContainsKey('AllowPasswordReplicationAccountName') -and
        $null -ne $existingResource.AllowPasswordReplicationAccountName)
    {
        $testMembersParameters = @{
            ExistingMembers = $existingResource.AllowPasswordReplicationAccountName
            Members         = $AllowPasswordReplicationAccountName
        }

        if (-not (Test-Members @testMembersParameters))
        {
            Write-Verbose -Message (
                $script:localizedData.AllowedSyncAccountsMismatch -f
                ($existingResource.AllowPasswordReplicationAccountName -join ';'),
                ($AllowPasswordReplicationAccountName -join ';')
            )

            $testTargetResourceReturnValue = $false
        }
    }

    if ($PSBoundParameters.ContainsKey('DenyPasswordReplicationAccountName') -and
        $null -ne $existingResource.DenyPasswordReplicationAccountName)
    {
        $testMembersParameters = @{
            ExistingMembers = $existingResource.DenyPasswordReplicationAccountName
            Members         = $DenyPasswordReplicationAccountName;
        }

        if (-not (Test-Members @testMembersParameters))
        {
            Write-Verbose -Message (
                $script:localizedData.DenySyncAccountsMismatch -f
                ($existingResource.DenyPasswordReplicationAccountName -join ';'),
                ($DenyPasswordReplicationAccountName -join ';')
            )

            $testTargetResourceReturnValue = $false
        }
    }

    return $testTargetResourceReturnValue
}

<#
    .SYNOPSIS
        Return a hashtable with members that are not present in CurrentMembers,
        and members that are present add should not be present.
 
    .PARAMETER DesiredMembers
        Specifies the list of desired members in the hashtable.
 
    .PARAMETER CurrentMembers
        Specifies the list of current members in the hashtable.
 
    .OUTPUTS
        Returns a hashtable with two properties. The property MembersToAdd contains the
        members as ADPrincipal objects that are not members in the collection
        provided in $CurrentMembers. The property MembersToRemove contains the
        unwanted members as ADPrincipal objects in the collection provided
        in $CurrentMembers.
#>

function Get-MembersToAddAndRemove
{
    param
    (
        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [System.String[]]
        $DesiredMembers,

        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [System.String[]]
        $CurrentMembers
    )

    $principalsToRemove = foreach ($memberName in $CurrentMembers)
    {
        if ($memberName -notin $DesiredMembers)
        {
            New-Object -TypeName Microsoft.ActiveDirectory.Management.ADPrincipal -ArgumentList $memberName
        }
    }

    $principalsToAdd = foreach ($memberName in $DesiredMembers)
    {
        if ($memberName -notin $CurrentMembers)
        {
            New-Object -TypeName Microsoft.ActiveDirectory.Management.ADPrincipal -ArgumentList $memberName
        }
    }

    return @{
        MembersToAdd    = [Microsoft.ActiveDirectory.Management.ADPrincipal[]] $principalsToAdd
        MembersToRemove = [Microsoft.ActiveDirectory.Management.ADPrincipal[]] $principalsToRemove
    }
}

Export-ModuleMember -Function *-TargetResource