Tests/Unit/MSFT_ADManagedServiceAccount.Tests.ps1

Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\ActiveDirectoryDsc.TestHelper.psm1')

if (-not (Test-RunForCITestCategory -Type 'Unit' -Category 'Tests'))
{
    return
}

$script:dscModuleName = 'ActiveDirectoryDsc'
$script:dscResourceName = 'MSFT_ADManagedServiceAccount'

#region HEADER

# Unit Test Template Version: 1.2.4
$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or `
    (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) )
{
    & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests'))
}

Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force

$TestEnvironment = Initialize-TestEnvironment `
    -DSCModuleName $script:dscModuleName `
    -DSCResourceName $script:dscResourceName `
    -ResourceType 'Mof' `
    -TestType Unit

#endregion HEADER

function Invoke-TestSetup
{
}

function Invoke-TestCleanup
{
    Restore-TestEnvironment -TestEnvironment $TestEnvironment
}

# Begin Testing
try
{
    Invoke-TestSetup

    InModuleScope $script:dscResourceName {
        # Load stub cmdlets and classes.
        Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\ActiveDirectory_2019.psm1') -Force

        # Need to do a deep copy of the Array of objects that compare returns
        function Copy-ArrayObjects
        {
            param
            (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [System.Array]
                $DeepCopyObject
            )

            $memStream = New-Object -TypeName 'IO.MemoryStream'
            $formatter = New-Object -TypeName 'Runtime.Serialization.Formatters.Binary.BinaryFormatter'
            $formatter.Serialize($memStream,$DeepCopyObject)
            $memStream.Position=0
            $formatter.Deserialize($memStream)
        }

        $mockPath               = 'OU=Fake,DC=contoso,DC=com'
        $mockDomainController   = 'MockDC'

        $mockCredentials        = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @(
            'DummyUser',
            (ConvertTo-SecureString -String 'DummyPassword' -AsPlainText -Force)
        )

        $mockADUSer = @{
            SamAccountName    = 'User1'
            DistinguishedName = 'CN=User1,OU=Fake,DC=contoso,DC=com'
            Enabled           = $true
            SID               = 'S-1-5-21-1409167834-891301383-2860967316-1142'
            ObjectGUID        = '91bffe90-4c84-4026-b1fc-d03671ff56ab'
            GivenName         = ''
            Name              = 'User1'
        }

        $mockADComputer = @{
            SamAccountName    = 'Node1$'
            DistinguishedName = 'CN=Node1,OU=Fake,DC=contoso,DC=com'
            Enabled           = $true
            SID               = 'S-1-5-21-1409167834-891301383-2860967316-1143'
            ObjectClass       = 'computer'
            ObjectGUID        = '91bffe90-4c84-4026-b1fc-d03671ff56ac'
            DnsHostName       = 'Node1.fake.contoso.com'
        }

        $mockSingleServiceAccount = @{
            Name              = 'TestSMSA'
            DistinguishedName = "CN={0},{1}" -f ('TestSMSA', $mockPath)
            Description       = 'Dummy single service account for unit testing'
            DisplayName       = ''
            ObjectClass       = 'msDS-ManagedServiceAccount'
            Enabled           = $true
            SamAccountName    = 'TestSMSA'
            SID               = 'S-1-5-21-1409167834-891301383-2860967316-1144'
            ObjectGUID        = '91bffe90-4c84-4026-b1fc-d03671ff56ad'
        }

        $mockGroupServiceAccount = @{
            Name              = 'TestGMSA'
            DistinguishedName = "CN={0},{1}" -f ('TestGMSA', $mockPath)
            Description       = 'Dummy group service account for unit testing'
            DisplayName       = ''
            ObjectClass       = 'msDS-GroupManagedServiceAccount'
            Enabled           = $true
            SID               = 'S-1-5-21-1409167834-891301383-2860967316-1145'
            ObjectGUID        = '91bffe90-4c84-4026-b1fc-d03671ff56ae'
            PrincipalsAllowedToRetrieveManagedPassword = @($mockADUSer.SamAccountName, $mockADComputer.SamAccountName)
        }

        $mockGetSingleServiceAccount = @{
            ServiceAccountName  = $mockSingleServiceAccount.Name
            DistinguishedName   = $mockSingleServiceAccount.DistinguishedName
            Path                = $mockPath
            Description         = $mockSingleServiceAccount.Description
            DisplayName         = $mockSingleServiceAccount.DisplayName
            AccountType         = 'Single'
            AccountTypeForce    = $false
            Ensure              = 'Present'
            Enabled             = $true
            Members             = @()
            MembershipAttribute = 'sAMAccountName'
            Credential          = $mockCredentials
            DomainController    = $mockDomainController
        }

        $mockGetGroupServiceAccount = @{
            ServiceAccountName  = $mockGroupServiceAccount.Name
            DistinguishedName   = $mockGroupServiceAccount.DistinguishedName
            Path                = $mockPath
            Description         = $mockGroupServiceAccount.Description
            DisplayName         = $mockGroupServiceAccount.DisplayName
            AccountType         = 'Group'
            AccountTypeForce    = $false
            Ensure              = 'Present'
            Enabled             = $true
            Members             = $mockGroupServiceAccount.PrincipalsAllowedToRetrieveManagedPassword
            MembershipAttribute = 'sAMAccountName'
            Credential          = $mockCredentials
            DomainController    = $mockDomainController
        }

        $mockCompareSingleServiceAccount = @(
            [pscustomobject] @{
                Parameter = 'ServiceAccountName'
                Expected  = $mockGetSingleServiceAccount.ServiceAccountName
                Actual    = $mockGetSingleServiceAccount.ServiceAccountName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'AccountType'
                Expected  = $mockGetSingleServiceAccount.AccountType
                Actual    = $mockGetSingleServiceAccount.AccountType
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'AccountTypeForce'
                Expected  = $mockGetSingleServiceAccount.AccountTypeForce
                Actual    = $mockGetSingleServiceAccount.AccountTypeForce
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Path'
                Expected  = $mockGetSingleServiceAccount.Path
                Actual    = $mockGetSingleServiceAccount.Path
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Ensure'
                Expected  = $mockGetSingleServiceAccount.Ensure
                Actual    = $mockGetSingleServiceAccount.Ensure
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Enabled'
                Expected  = $mockGetSingleServiceAccount.Enabled
                Actual    = $mockGetSingleServiceAccount.Enabled
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Description'
                Expected  = $mockGetSingleServiceAccount.Description
                Actual    = $mockGetSingleServiceAccount.Description
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DisplayName'
                Expected  = $mockGetSingleServiceAccount.DisplayName
                Actual    = $mockGetSingleServiceAccount.DisplayName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DistinguishedName'
                Expected  = $mockGetSingleServiceAccount.DistinguishedName
                Actual    = $mockGetSingleServiceAccount.DistinguishedName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'MembershipAttribute'
                Expected  = $mockGetSingleServiceAccount.MembershipAttribute
                Actual    = $mockGetSingleServiceAccount.MembershipAttribute
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Credential'
                Expected  = $mockGetSingleServiceAccount.Credential
                Actual    = $mockGetSingleServiceAccount.Credential
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DomainController'
                Expected  = $mockGetSingleServiceAccount.DomainController
                Actual    = $mockGetSingleServiceAccount.DomainController
                Pass      = $true
            }
        )

        $mockCompareGroupServiceAccount = @(
            [pscustomobject] @{
                Parameter = 'ServiceAccountName'
                Expected  = $mockGetGroupServiceAccount.ServiceAccountName
                Actual    = $mockGetGroupServiceAccount.ServiceAccountName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'AccountType'
                Expected  = $mockGetGroupServiceAccount.AccountType
                Actual    = $mockGetGroupServiceAccount.AccountType
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'AccountTypeForce'
                Expected  = $mockGetGroupServiceAccount.AccountTypeForce
                Actual    = $mockGetGroupServiceAccount.AccountTypeForce
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Path'
                Expected  = $mockGetGroupServiceAccount.Path
                Actual    = $mockGetGroupServiceAccount.Path
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Ensure'
                Expected  = $mockGetGroupServiceAccount.Ensure
                Actual    = $mockGetGroupServiceAccount.Ensure
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Enabled'
                Expected  = $mockGetGroupServiceAccount.Enabled
                Actual    = $mockGetGroupServiceAccount.Enabled
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Description'
                Expected  = $mockGetGroupServiceAccount.Description
                Actual    = $mockGetGroupServiceAccount.Description
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DisplayName'
                Expected  = $mockGetGroupServiceAccount.DisplayName
                Actual    = $mockGetGroupServiceAccount.DisplayName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Members'
                Expected  = $mockGetGroupServiceAccount.Members
                Actual    = $mockGetGroupServiceAccount.Members
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Credential'
                Expected  = $mockGetGroupServiceAccount.MembershipAttribute
                Actual    = $mockGetGroupServiceAccount.MembershipAttribute
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DistinguishedName'
                Expected  = $mockGetGroupServiceAccount.DistinguishedName
                Actual    = $mockGetGroupServiceAccount.DistinguishedName
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'Credential'
                Expected  = $mockGetGroupServiceAccount.Credential
                Actual    = $mockGetGroupServiceAccount.Credential
                Pass      = $true
            }
            [pscustomobject] @{
                Parameter = 'DomainController'
                Expected  = $mockGetGroupServiceAccount.DomainController
                Actual    = $mockGetGroupServiceAccount.DomainController
                Pass      = $true
            }
        )

        #region Function Get-TargetResource
        Describe -Name 'MSFT_ADManagedServiceAccount\Get-TargetResource' -Tag 'Get' {
            BeforeAll {
                Mock -CommandName Assert-Module -ParameterFilter {
                    $ModuleName -eq 'ActiveDirectory'
                }

                Mock -CommandName Get-ADObjectParentDN -MockWith {
                    return $mockPath
                }
            }

            Context 'When the system uses specific parameters' {
                Mock -CommandName Get-ADServiceAccount -MockWith {
                    return $mockSingleServiceAccount
                }

                It 'Should call "Assert-Module" to check AD module is installed' {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                    }

                    { Get-TargetResource @testResourceParametersSingle } | Should -Not -Throw

                    Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                        $ModuleName -eq 'ActiveDirectory'
                    } -Scope It -Exactly -Times 1
                }

                It 'Should call "Get-ADServiceAccount" with "Server" parameter when "DomainController" specified' {
                    $testResourceParametersWithServer = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        DomainController   = $mockDomainController
                    }

                    { Get-TargetResource @testResourceParametersWithServer } | Should -Not -Throw

                    Assert-MockCalled -CommandName Get-ADServiceAccount -ParameterFilter {
                        $Server -eq $mockDomainController
                    } -Scope It -Exactly -Times 1
                }

                It 'Should call "Get-ADServiceAccount" with "Credential" parameter when specified' {
                    $testResourceParametersWithCredentials = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Credential         = $mockCredentials
                    }

                    { Get-TargetResource @testResourceParametersWithCredentials } | Should -Not -Throw

                    Assert-MockCalled -CommandName Get-ADServiceAccount -ParameterFilter {
                        $Credential -eq $mockCredentials
                    } -Scope It -Exactly -Times 1
                }
            }

            Context 'When system cannot connect to domain or other errors' {
                Mock -CommandName Get-ADServiceAccount -MockWith {
                    throw 'Microsoft.ActiveDirectory.Management.ADServerDownException'
                }

                It 'Should call "Get-ADServiceAccount" and throw an error when catching any other errors besides "Account Not Found"'{
                    $getTargetResourceParameters = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                    }

                    { Get-TargetResource  @getTargetResourceParameters -ErrorAction 'SilentlyContinue' } |
                        Should -Throw ($script:localizedData.RetrievingServiceAccountError -f $getTargetResourceParameters.ServiceAccountName)
                }
            }

            Context 'When the system is in desired state (sMSA)' {
                Mock -CommandName Get-ADServiceAccount -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $Identity
                } -MockWith {
                    Write-Verbose "Call Get-ADServiceAccount with $($mockSingleServiceAccount.Name)"
                    return $mockSingleServiceAccount
                }

                It 'Should mock call to Get-ADServiceAccount return identical information' {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                    }

                    $getTargetResourceResult = Get-TargetResource @testResourceParametersSingle

                    $getTargetResourceResult.ServiceAccountName | Should -Be $mockSingleServiceAccount.Name
                    $getTargetResourceResult.Ensure | Should -Be 'Present'
                    $getTargetResourceResult.AccountType | Should -Be 'Single'
                    $getTargetResourceResult.Description | Should -Be $mockSingleServiceAccount.Description
                    $getTargetResourceResult.DisplayName | Should -Be $mockSingleServiceAccount.DisplayName
                    $getTargetResourceResult.Members | Should -Be @()
                    $getTargetResourceResult.Path | Should -Be $mockPath
                }
            }

            Context 'When the system is in desired state (gMSA)' {
                Mock -CommandName Get-ADServiceAccount -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $Identity
                } -MockWith {
                    Write-Verbose "Call Get-ADServiceAccount with $($mockGroupServiceAccount.Name)"
                    return $mockGroupServiceAccount
                }

                Mock -CommandName Get-ADObject -ParameterFilter {
                    $mockADComputer.SamAccountName -eq $Identity
                } -MockWith {
                    Write-Verbose "Call Get-ADObject with $($mockADComputer.SamAccountName)"
                    return $mockADComputer
                }

                Mock -CommandName Get-ADObject -ParameterFilter {
                    $mockADUSer.SamAccountName -eq $Identity
                } -MockWith {
                    Write-Verbose "Call Get-ADObject with $($mockADUser.SamAccountName)"
                    return $mockADUser
                }

                It 'Should mock call to Get-ADServiceAccount return identical information' {
                    $testResourceParametersGroup = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SamAccountName'
                    }

                    $getTargetResourceResult = Get-TargetResource @testResourceParametersGroup

                    $getTargetResourceResult.ServiceAccountName | Should -Be $mockGroupServiceAccount.Name
                    $getTargetResourceResult.Ensure | Should -Be 'Present'
                    $getTargetResourceResult.AccountType | Should -Be 'Group'
                    $getTargetResourceResult.Description | Should -Be $mockGroupServiceAccount.Description
                    $getTargetResourceResult.DisplayName | Should -Be $mockGroupServiceAccount.DisplayName
                    $getTargetResourceResult.Members | Should -Be `
                        @($mockADUSer.($testResourceParametersGroup.MembershipAttribute), `
                          $mockADComputer.($testResourceParametersGroup.MembershipAttribute))
                    $getTargetResourceResult.Path | Should -Be $mockPath
                }
            }

            Context -Name 'When the system is NOT in the desired state (Both)' {
                Mock -CommandName Get-ADServiceAccount -MockWith {
                    throw New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException
                }

                $testResourceParametersSingle = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                }

                $getTargetResourceResult = Get-TargetResource @testResourceParametersSingle

                It "Should return 'Ensure' is 'Absent'" {
                    $getTargetResourceResult.Ensure | Should -Be 'Absent'
                }

                It "Should return 'ServiceAccountName' when 'Absent'" {
                    $getTargetResourceResult.ServiceAccountName | Should -Not -BeNullOrEmpty
                    $getTargetResourceResult.ServiceAccountName | Should -BeExactly $testResourceParametersSingle.ServiceAccountName
                }
            }
        }
        #endregion Function Get-TargetResource

        #region Function Compare-TargetResourceState
        Describe -Name 'MSFT_ADManagedServiceAccount\Compare-TargetResourceState' -Tag 'Compare' {
            Context -Name 'When the system is in the desired state (sMSA)' {
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Get-TargetResource with $($mockSingleServiceAccount.Name)"
                    return $mockGetSingleServiceAccount
                }

                $testResourceParametersSingle = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                    AccountType        = 'Single'
                    Path               = $mockPath
                    Description        = $mockSingleServiceAccount.Description
                    Ensure             = 'Present'
                    DisplayName        = $mockSingleServiceAccount.DisplayName
                }

                $getTargetResourceResult = Compare-TargetResourceState @testResourceParametersSingle
                $testCases = @()
                $getTargetResourceResult | ForEach-Object {
                    $testCases += @{
                        Parameter = $_.Parameter
                        Expected  = $_.Expected
                        Actual    = $_.Actual
                        Pass      = $_.Pass
                    }
                }

                It "Should return identical information for <Parameter>" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Expected,

                        [Parameter()]
                        $Actual,

                        [Parameter()]
                        $Pass
                    )

                    $Expected | Should -BeExactly $Actual
                    $Pass | Should -BeTrue
                }

            }

            Context -Name 'When the system is in the desired state (gMSA)' {
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'SamAccountName'
                } -MockWith {
                    Write-Verbose 'Group MSA using sAMAccountName'
                    return $mockGetGroupServiceAccount
                }

                $mockGetGroupServiceAccountDN = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountDN['MembershipAttribute'] = 'DistinguishedName'
                $mockGetGroupServiceAccountDN['Members'] = @($mockADUSer.DistinguishedName, $mockADComputer.DistinguishedName)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'DistinguishedName'
                } -MockWith {
                    Write-Verbose 'Group MSA using DistinguishedName'
                    return $mockGetGroupServiceAccountDN
                }

                $mockGetGroupServiceAccountSID = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountSID['MembershipAttribute'] = 'SID'
                $mockGetGroupServiceAccountSID['Members'] = @($mockADUSer.SID, $mockADComputer.SID)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'SID'
                } -MockWith {
                    Write-Verbose 'Group MSA using SID'
                    return $mockGetGroupServiceAccountSID
                }

                $mockGetGroupServiceAccountOID = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountOID['MembershipAttribute'] = 'ObjectGUID'
                $mockGetGroupServiceAccountOID['Members'] = @($mockADUSer.ObjectGUID, $mockADComputer.ObjectGUID)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'ObjectGUID'
                } -MockWith {
                    Write-Verbose 'Group MSA using ObjectGUID'
                    return $mockGetGroupServiceAccountOID
                }

                $testResourceParametersGroup = @{
                    ServiceAccountName  = $mockGroupServiceAccount.Name
                    MembershipAttribute = 'SamAccountName'
                    AccountType         = 'Group'
                    Path                = $mockPath
                    Description         = $mockGroupServiceAccount.Description
                    Ensure              = 'Present'
                    Members             = 'Node1$', 'User1'
                    DisplayName         = $mockGroupServiceAccount.DisplayName
                }

                $getTargetResourceResult = Compare-TargetResourceState @testResourceParametersGroup
                $testCases = @()
                $getTargetResourceResult | ForEach-Object {
                    $testCases += @{
                        Parameter = $_.Parameter
                        Expected  = $_.Expected
                        Actual    = $_.Actual
                        Pass      = $_.Pass
                    }
                }

                It "Should return identical information for <Parameter>" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Expected,

                        [Parameter()]
                        $Actual,

                        [Parameter()]
                        $Pass
                    )

                    $Expected | Should -BeExactly $Actual
                    $Pass | Should -BeTrue
                }

                It "Should return identical information for 'Members' when using 'SamAccountName'" {
                    $testResourceParametersGroupSAM = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SamAccountName'
                        Members             = 'Node1$', 'User1'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultSAM  = Compare-TargetResourceState @testResourceParametersGroupSAM

                    $getTargetResourceResultSAM.Expected | Should -BeExactly $getTargetResourceResultSAM.Actual
                    $getTargetResourceResultSAM.Pass | Should -BeTrue
                }

                It "Should return identical information for 'Members' when using 'DistinguishedName'" {
                    $testResourceParametersGroupDN = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'DistinguishedName'
                        Members             = 'CN=Node1,OU=Fake,DC=contoso,DC=com', 'CN=User1,OU=Fake,DC=contoso,DC=com'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultDN  = Compare-TargetResourceState @testResourceParametersGroupDN

                    $getTargetResourceResultDN.Expected | Should -BeExactly $getTargetResourceResultDN.Actual
                    $getTargetResourceResultDN.Pass | Should -BeTrue
                }

                It "Should return identical information for 'Members' when using 'SID'" {
                    $testResourceParametersGroupSID = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SID'
                        Members             = 'S-1-5-21-1409167834-891301383-2860967316-1143', 'S-1-5-21-1409167834-891301383-2860967316-1142'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultSID  = Compare-TargetResourceState @testResourceParametersGroupSID

                    $getTargetResourceResultSID.Expected | Should -BeExactly $getTargetResourceResultSID.Actual
                    $getTargetResourceResultSID.Pass | Should -BeTrue
                }

                It "Should return identical information for 'Members' when using 'ObjectGUID'" {
                    $testResourceParametersGroupGUID = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'ObjectGUID'
                        Members             = '91bffe90-4c84-4026-b1fc-d03671ff56ac', '91bffe90-4c84-4026-b1fc-d03671ff56ab'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultGUID  = Compare-TargetResourceState @testResourceParametersGroupGUID

                    $getTargetResourceResultGUID.Expected | Should -BeExactly $getTargetResourceResultGUID.Actual
                    $getTargetResourceResultGUID.Pass | Should -BeTrue
                }
            }

            Context -Name 'When the system is NOT in the desired state (sMSA)' {
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    return $mockGetSingleServiceAccount
                }

                $testResourceParametersSingleNotCompliant = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                    AccountType        = 'Group'
                    Path               = 'OU=FakeWrong,DC=contoso,DC=com'
                    Description        = 'Test MSA description Wrong'
                    Ensure             = 'Absent'
                    DisplayName        = 'WrongDisplayName'
                }

                $getTargetResourceResult = Compare-TargetResourceState @testResourceParametersSingleNotCompliant
                $testCases = @()
                # Need to remove parameters that will always be true
                $getTargetResourceResult = $getTargetResourceResult | Where-Object -FilterScript {
                    $_.Parameter -ne 'ServiceAccountName' -and
                    $_.Parameter -ne 'DistinguishedName' -and
                    $_.Parameter -ne 'MembershipAttribute'
                }

                $getTargetResourceResult | ForEach-Object {
                    $testCases += @{
                        Parameter = $_.Parameter
                        Expected  = $_.Expected
                        Actual    = $_.Actual
                        Pass      = $_.Pass
                    }
                }

                It "Should return false for <Parameter>" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Expected,

                        [Parameter()]
                        $Actual,

                        [Parameter()]
                        $Pass
                    )

                    $Expected | Should -Not -Be $Actual
                    $Pass | Should -BeFalse
                }

            }

            Context -Name 'When the system is NOT in the desired state (gMSA)' {
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'SamAccountName'
                } -MockWith {
                    Write-Verbose 'Group MSA using sAMAccountName'
                    return $mockGetGroupServiceAccount
                }

                $mockGetGroupServiceAccountDN = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountDN['MembershipAttribute'] = 'DistinguishedName'
                $mockGetGroupServiceAccountDN['Members'] = @($mockADUSer.DistinguishedName, $mockADComputer.DistinguishedName)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'DistinguishedName'
                } -MockWith {
                    Write-Verbose 'Group MSA using DistinguishedName'
                    return $mockGetGroupServiceAccountDN
                }

                $mockGetGroupServiceAccountSID = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountSID['MembershipAttribute'] = 'SID'
                $mockGetGroupServiceAccountSID['Members'] = @($mockADUSer.SID, $mockADComputer.SID)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'SID'
                } -MockWith {
                    Write-Verbose 'Group MSA using SID'
                    return $mockGetGroupServiceAccountSID
                }

                $mockGetGroupServiceAccountOID = $mockGetGroupServiceAccount.Clone()
                $mockGetGroupServiceAccountOID['MembershipAttribute'] = 'ObjectGUID'
                $mockGetGroupServiceAccountOID['Members'] = @($mockADUSer.ObjectGUID, $mockADComputer.ObjectGUID)
                Mock -CommandName Get-TargetResource -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $MembershipAttribute -eq 'ObjectGUID'
                } -MockWith {
                    Write-Verbose 'Group MSA using ObjectGUID'
                    return $mockGetGroupServiceAccountOID
                }

                $testResourceParametersGroup = @{
                    ServiceAccountName   = $mockGroupServiceAccount.Name
                    AccountType          = 'Single'
                    Path                 = 'OU=FakeWrong,DC=contoso,DC=com'
                    Description          = 'Test MSA description Wrong'
                    Ensure               = 'Absent'
                    DisplayName          = 'WrongDisplayName'
                    MembershipAttribute = 'SamAccountName'
                }

                $getTargetResourceResult = Compare-TargetResourceState @testResourceParametersGroup
                $testCases = @()
                # Need to remove parameters that will always be true
                $getTargetResourceResult = $getTargetResourceResult | Where-Object -FilterScript {
                    $_.Parameter -ne 'ServiceAccountName' -and
                    $_.Parameter -ne 'DistinguishedName' -and
                    $_.Parameter -ne 'MembershipAttribute'
                }
                $getTargetResourceResult | ForEach-Object {
                    $testCases += @{
                        Parameter = $_.Parameter
                        Expected  = $_.Expected
                        Actual    = $_.Actual
                        Pass      = $_.Pass
                    }
                }

                It "Should return false for <Parameter>" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Expected,

                        [Parameter()]
                        $Actual,

                        [Parameter()]
                        $Pass
                    )

                    $Expected | Should -Not -Be $Actual
                    $Pass | Should -BeFalse
                }

                It "Should return false for 'Members' when using 'SamAccountName'" {
                    $testResourceParametersGroupSAM = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SamAccountName'
                        Members             = 'Node1$'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultSAM  = Compare-TargetResourceState @testResourceParametersGroupSAM

                    $membersState = $getTargetResourceResultSAM | Where-Object -FilterScript {$_.Parameter -eq 'Members'}
                    $membersState.Expected | Should -Not -BeExactly $membersState.Actual
                    $membersState.Pass | Should -BeFalse
                }

                It "Should return false for 'Members' when using 'DistinguishedName'" {
                    $testResourceParametersGroupDN = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'DistinguishedName'
                        Members             = 'CN=Node1,OU=Fake,DC=contoso,DC=com'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultDN  = Compare-TargetResourceState @testResourceParametersGroupDN

                    $membersState = $getTargetResourceResultDN | Where-Object -FilterScript {$_.Parameter -eq 'Members'}
                    $membersState.Expected | Should -Not -BeExactly $membersState.Actual
                    $membersState.Pass | Should -BeFalse
                }

                It "Should return false for 'Members' when using 'SID'" {
                    $testResourceParametersGroupSID = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SID'
                        Members             = 'S-1-5-21-1409167834-891301383-2860967316-1143'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultSID  = Compare-TargetResourceState @testResourceParametersGroupSID

                    $membersState = $getTargetResourceResultSID | Where-Object -FilterScript {$_.Parameter -eq 'Members'}
                    $membersState.Expected | Should -Not -BeExactly $membersState.Actual
                    $membersState.Pass | Should -BeFalse
                }

                It "Should return false for 'Members' when using 'ObjectGUID'" {
                    $testResourceParametersGroupGUID = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'ObjectGUID'
                        Members             = '91bffe90-4c84-4026-b1fc-d03671ff56ac'
                        AccountType         = 'Group'
                    }

                    $getTargetResourceResultGUID  = Compare-TargetResourceState @testResourceParametersGroupGUID

                    $membersState = $getTargetResourceResultGUID | Where-Object -FilterScript {$_.Parameter -eq 'Members'}
                    $membersState.Expected | Should -Not -BeExactly $membersState.Actual
                    $membersState.Pass | Should -BeFalse
                }
            }
        }
        #endregion Function Compare-TargetResourceState

        #region Function Test-TargetResource
        Describe -Name 'MSFT_ADManagedServiceAccount\Test-TargetResource' -Tag 'Test' {
            Context -Name "When the system is in the desired state and 'Ensure' is 'Present' (sMSA)" {
                It "Should pass when the Parameters are properly set" {
                    Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                        $mockSingleServiceAccount.Name -eq $ServiceAccountName
                    } -MockWith {
                        Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                        return $mockCompareSingleServiceAccount
                    }

                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        AccountType        = 'Single'
                        Path               = $mockPath
                        Description        = $mockSingleServiceAccount.Description
                        Ensure             = 'Present'
                        DisplayName        = ''
                    }

                    Test-TargetResource @testResourceParametersSingle | Should -BeTrue
                }
            }

            Context -Name "When the system is in the desired state and 'Ensure' is 'Present' (gMSA)" {
                It "Should pass when the Parameters are properly set" {
                    Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                        $mockGroupServiceAccount.Name -eq $ServiceAccountName
                    } -MockWith {
                        Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name)"
                        return $mockCompareGroupServiceAccount
                    }

                    $testResourceParametersGroup = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SamAccountName'
                        AccountType         = 'Group'
                        Path                = $mockPath
                        Description         = $mockGroupServiceAccount.Description
                        Ensure              = 'Present'
                        Members             = 'Node1$', 'User1'
                        DisplayName         = ''
                    }

                    Test-TargetResource @testResourceParametersGroup | Should -BeTrue
                }
            }

            Context -Name "When the system is in the desired state and 'Ensure' is 'Absent' (Both)" {
                It "Should pass when 'Ensure' is set to 'Absent" {
                    $mockCompareSingleServiceAccountEnsureAbsent = $mockCompareSingleServiceAccount.Clone()
                    $objectEnsure = $mockCompareSingleServiceAccountEnsureAbsent | Where-Object -FilterScript {$_.Parameter -eq 'Ensure'}
                    $objectEnsure.Actual = 'Absent'
                    $objectEnsure.Pass = $true

                    Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                        $mockSingleServiceAccount.Name -eq $ServiceAccountName
                    } -MockWith {
                        Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                        return $mockCompareSingleServiceAccountEnsureAbsent
                    }

                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Ensure             = 'Absent'
                    }

                    Test-TargetResource @testResourceParametersSingle | Should -BeTrue
                }
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (sMSA)" {
                $mockCompareSingleServiceAccountNotCompliant = Copy-ArrayObjects $mockCompareSingleServiceAccount

                $testIncorrectParameters = @{
                    AccountType = 'Group'
                    Path        = 'WrongPath'
                    Description = 'WrongDescription'
                    Ensure      = 'Absent'
                    DisplayName = 'DisplayNameWrong'
                }

                $testCases = @()
                foreach($incorrectParameter in $testIncorrectParameters.GetEnumerator())
                {
                    $objectParameter = $mockCompareSingleServiceAccountNotCompliant | Where-Object -FilterScript { $_.Parameter -eq $incorrectParameter.Name }
                    $objectParameter.Expected = $incorrectParameter.Value
                    $objectParameter.Pass = $false

                    $testCases += @{
                        Parameter = $incorrectParameter.Name
                        Value = $incorrectParameter.Value
                    }
                }

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                    return $mockCompareSingleServiceAccountNotCompliant
                }

                It "Should return $false when <Parameter> is incorrect" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Value
                    )

                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        AccountType        = 'Single'
                        Path               = $mockPath
                        Description        = $mockSingleServiceAccount.Description
                        Ensure             = 'Present'
                        DisplayName        = ''
                    }

                    $testResourceParametersSingle[$Parameter] = $value
                    Test-TargetResource @testResourceParametersSingle | Should -BeFalse
                }
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (gMSA)" {
                $mockCompareGroupServiceAccountNotCompliant = Copy-ArrayObjects $mockCompareGroupServiceAccount

                $testIncorrectParameters = @{
                    AccountType = 'Single'
                    Path        = 'WrongPath'
                    Description = 'WrongDescription'
                    Ensure      = 'Absent'
                    Members     = ''
                    DisplayName = 'DisplayNameWrong'
                }

                $testCases = @()
                foreach($incorrectParameter in $testIncorrectParameters.GetEnumerator())
                {
                    $objectParameter = $mockCompareGroupServiceAccountNotCompliant | Where-Object -FilterScript { $_.Parameter -eq $incorrectParameter.Name }
                    $objectParameter.Expected = $incorrectParameter.Value
                    $objectParameter.Pass = $false

                    $testCases += @{
                        Parameter = $incorrectParameter.Name
                        Value = $incorrectParameter.Value
                    }
                }

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name)"
                    return $mockCompareGroupServiceAccountNotCompliant
                }

                It "Should return $false when <Parameter> is incorrect" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Value
                    )

                    $testResourceParametersGroup = @{
                        ServiceAccountName  = $mockGroupServiceAccount.Name
                        MembershipAttribute = 'SamAccountName'
                        AccountType         = 'Group'
                        Path                = $mockPath
                        Description         = $mockGroupServiceAccount.Description
                        Ensure              = 'Present'
                        Members             = 'Node1$', 'User1'
                        DisplayName         = ''
                    }

                    $testResourceParametersGroup[$Parameter] = $value
                    Test-TargetResource @testResourceParametersGroup | Should -BeFalse
                }
            }
        }
        #endregion Function Test-TargetResource

        Describe -Name 'MSFT_ADManagedServiceAccount\New-ADServiceAccountHelper' {
            BeforeAll {
                Mock -CommandName New-ADServiceAccount
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (sMSA)" {
                $testResourceParametersSingle = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                    AccountType        = 'Single'
                    Path               = $mockPath
                    Description        = $mockSingleServiceAccount.Description
                    Ensure             = 'Present'
                    DisplayName        = 'NewDisplayName'
                }

                It 'Should call New-ADServiceAccount' {
                    New-ADServiceAccountHelper @testResourceParametersSingle
                    Assert-MockCalled -CommandName New-ADServiceAccount -Scope It -Exactly -Times 1
                }
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (gMSA)" {
                $testResourceParametersGroup = @{
                    ServiceAccountName  = $mockGroupServiceAccount.Name
                    MembershipAttribute = 'SamAccountName'
                    AccountType         = 'Group'
                    Path                = $mockPath
                    Description         = $mockGroupServiceAccount.Description
                    Ensure              = 'Present'
                    Members             = 'Node1$', 'User1'
                    DisplayName         = ''
                }

                It 'Should call New-ADServiceAccount' {
                    New-ADServiceAccountHelper @testResourceParametersGroup
                    Assert-MockCalled -CommandName New-ADServiceAccount -Scope It -Exactly -Times 1
                }
            }
        }

        #region Function Set-TargetResource
        Describe -Name 'MSFT_ADManagedServiceAccount\Set-TargetResource' -Tag 'Set' {
            BeforeAll {
                Mock -CommandName New-ADServiceAccountHelper
                Mock -CommandName Remove-ADServiceAccount
                Mock -CommandName Move-ADObject
                Mock -CommandName Set-ADServiceAccount
            }

            Context -Name "When the system is in the desired state and 'Ensure' is 'Present' (sMSA)" {
                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                    return $mockCompareSingleServiceAccount
                }

                $testResourceParametersSingle = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                    AccountType        = 'Single'
                    Path               = $mockPath
                    Description        = $mockSingleServiceAccount.Description
                    Ensure             = 'Present'
                    DisplayName        = ''
                }

                It 'Should NOT take any action when all parameters are correct' {
                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }
            }

            Context -Name "When the system is in the desired state and 'Ensure' is 'Present' (gMSA)" {
                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name)"
                    return $mockCompareGroupServiceAccount
                }

                $testResourceParametersGroup = @{
                    ServiceAccountName  = $mockGroupServiceAccount.Name
                    MembershipAttribute = 'SamAccountName'
                    AccountType         = 'Group'
                    Path                = $mockPath
                    Description         = $mockGroupServiceAccount.Description
                    Ensure              = 'Present'
                    Members             = 'Node1$', 'User1'
                    DisplayName         = ''
                }

                It 'Should NOT take any action when all parameters are correct' {
                    Set-TargetResource @testResourceParametersGroup

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }
            }

            Context -Name "When the system is in the desired state and 'Ensure' is 'Absent' (Both)" {
                $mockCompareSingleServiceAccountEnsureAbsent = $mockCompareSingleServiceAccount.Clone()
                $objectEnsure = $mockCompareSingleServiceAccountEnsureAbsent | Where-Object -FilterScript {$_.Parameter -eq 'Ensure'}
                $objectEnsure.Actual = 'Absent'
                $objectEnsure.Pass = $true

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                    return $mockCompareSingleServiceAccountEnsureAbsent
                }

                $testResourceParametersSingle = @{
                    ServiceAccountName = $mockSingleServiceAccount.Name
                    Ensure             = 'Absent'
                }

                It "Should pass when 'Ensure' is set to 'Absent" {
                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (sMSA)" {
                $mockCompareSingleServiceAccountNotCompliantPath = Copy-ArrayObjects $mockCompareSingleServiceAccount
                $mockCompareSingleServiceAccountNotCompliantOtherParameters = Copy-ArrayObjects $mockCompareSingleServiceAccount
                $mockCompareSingleServiceAccountNotCompliantAccountType = Copy-ArrayObjects $mockCompareSingleServiceAccount
                $mockCompareSingleServiceAccountNotCompliantEnsure = Copy-ArrayObjects $mockCompareSingleServiceAccount

                #region Incorrect Path setup
                $objectPath = $mockCompareSingleServiceAccountNotCompliantPath | Where-Object -FilterScript {$_.Parameter -eq 'Path'}
                $objectPath.Expected = 'WrongPath'
                $objectPath.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName -and $Path -eq $objectPath.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name) and Path '$($objectPath.Expected)'"
                    return $mockCompareSingleServiceAccountNotCompliantPath
                }
                #endregion Incorrect Path setup

                It "Should call 'Move-ADObject' when 'Path' is incorrect" {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Path               = $objectPath.Expected
                    }

                    Set-TargetResource @testResourceParametersSingle
                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                #region Incorrect parameter test setup
                $testIncorrectParameters = @{
                    Description = 'WrongDescription'
                    DisplayName = 'WrongDisplayName'
                }

                $testCases = @()
                foreach($incorrectParameter in $testIncorrectParameters.GetEnumerator())
                {
                    $objectParameter = $mockCompareSingleServiceAccountNotCompliantOtherParameters |
                                            Where-Object -FilterScript { $_.Parameter -eq $incorrectParameter.Name }
                    $objectParameter.Expected = $incorrectParameter.Value
                    $objectParameter.Pass = $false

                    $testCases += @{
                        Parameter = $incorrectParameter.Name
                        Value = $incorrectParameter.Value
                    }
                }

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName -and (
                        $Description -eq $testIncorrectParameters.Description -or
                        $DisplayName -eq $testIncorrectParameters.DisplayName
                    )
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name) and incorrect parameters"
                    return $mockCompareSingleServiceAccountNotCompliantOtherParameters
                }
                #endregion Incorrect parameter test setup

                It "Should call 'Set-ADServiceAccount' when '<Parameter>' is incorrect" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Value
                    )

                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                    }
                    $testResourceParametersSingle[$Parameter] = $Value

                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 1
                }

                #region Incorrect Account type setup
                $objectAccountType = $mockCompareSingleServiceAccountNotCompliantAccountType | Where-Object -FilterScript {$_.Parameter -eq 'AccountType'}
                $objectAccountType.Expected = 'Group'
                $objectAccountType.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName -and $AccountType -eq $objectAccountType.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name) and AccountType '$($objectAccountType.Expected)'"
                    return $mockCompareSingleServiceAccountNotCompliantAccountType
                }
                #endregion Incorrect Account type setup

                It "Should NOT call 'Remove-ADServiceAccount, New-ADServiceAccountHelper' when 'AccountType' is incorrect and 'AccountTypeForce' is false" {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        AccountType        = $objectAccountType.Expected
                        AccountTypeForce   = $false
                    }

                    # Check if Warning is returned
                    Set-TargetResource @testResourceParametersSingle 3>&1 | Should -Not -Be $null

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                It "Should call 'Remove-ADServiceAccount, New-ADServiceAccountHelper' when 'AccountType' is incorrect and 'AccountTypeForce' is true" {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        AccountType        = 'Group'
                        AccountTypeForce   = $true
                    }

                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 1
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                #region Incorrect Ensure setup
                $objectEnsure = $mockCompareSingleServiceAccountNotCompliantEnsure | Where-Object -FilterScript {$_.Parameter -eq 'Ensure'}
                $objectEnsure.Expected = 'Absent'
                $objectEnsure.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName -and $Ensure -eq $objectEnsure.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name) and Ensure '$($objectEnsure.Expected)'"
                    return $mockCompareSingleServiceAccountNotCompliantEnsure
                }
                #endregion Incorrect Ensure type setup

                It "Should call 'Remove-ADServiceAccount' when 'Ensure' is set to 'Absent'" {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Ensure             = $objectEnsure.Expected
                    }

                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (gMSA)" {
                $mockCompareGroupServiceAccountNotCompliantPath = Copy-ArrayObjects $mockCompareGroupServiceAccount
                $mockCompareGroupServiceAccountNotCompliantOtherParameters = Copy-ArrayObjects $mockCompareGroupServiceAccount
                $mockCompareGroupServiceAccountNotCompliantAccountType = Copy-ArrayObjects $mockCompareGroupServiceAccount
                $mockCompareGroupServiceAccountNotCompliantEnsure = Copy-ArrayObjects $mockCompareGroupServiceAccount

                #region Incorrect Path setup
                $objectPath = $mockCompareGroupServiceAccountNotCompliantPath | Where-Object -FilterScript {$_.Parameter -eq 'Path'}
                $objectPath.Expected = 'WrongPath'
                $objectPath.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $Path -eq $objectPath.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name) and Path '$($objectPath.Expected)'"
                    return $mockCompareGroupServiceAccountNotCompliantPath
                }
                #endregion Incorrect Path setup

                It "Should call 'Move-ADObject' when 'Path' is incorrect" {
                    $testResourceParametersGroup = @{
                        ServiceAccountName = $mockGroupServiceAccount.Name
                        AccountType        = 'Group'
                        Path               = $objectPath.Expected
                    }

                    Set-TargetResource @testResourceParametersGroup
                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                #region Incorrect parameter test setup
                $testIncorrectParameters = @{
                    Description = 'WrongDescription'
                    DisplayName = 'WrongDisplayName'
                    Members     = 'WrongUser'
                }

                $testCases = @()
                foreach($incorrectParameter in $testIncorrectParameters.GetEnumerator())
                {
                    $objectParameter = $mockCompareGroupServiceAccountNotCompliantOtherParameters |
                                            Where-Object -FilterScript { $_.Parameter -eq $incorrectParameter.Name }
                    $objectParameter.Expected = $incorrectParameter.Value
                    $objectParameter.Pass = $false

                    $testCases += @{
                        Parameter = $incorrectParameter.Name
                        Value = $incorrectParameter.Value
                    }
                }

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and (
                        $Description -eq $testIncorrectParameters.Description -or
                        $DisplayName -eq $testIncorrectParameters.DisplayName -or
                        $Members -eq $testIncorrectParameters.Members
                    )
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name) and incorrect parameters"
                    return $mockCompareGroupServiceAccountNotCompliantOtherParameters
                }
                #endregion Incorrect parameter test setup

                It "Should call 'Set-ADServiceAccount' when '<Parameter>' is incorrect" -TestCases $testCases {
                    param
                    (
                        [Parameter()]
                        $Parameter,

                        [Parameter()]
                        $Value
                    )

                    $testResourceParametersGroup = @{
                        ServiceAccountName = $mockGroupServiceAccount.Name
                        AccountType        = 'Group'
                    }
                    $testResourceParametersGroup[$Parameter] = $Value

                    Set-TargetResource @testResourceParametersGroup
                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 1
                }

                #region Incorrect Account type setup
                $objectAccountType = $mockCompareGroupServiceAccountNotCompliantAccountType | Where-Object -FilterScript {$_.Parameter -eq 'AccountType'}
                $objectAccountType.Expected = 'Single'
                $objectAccountType.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $AccountType -eq $objectAccountType.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name) and AccountType '$($objectAccountType.Expected)'"
                    return $mockCompareGroupServiceAccountNotCompliantAccountType
                }
                #endregion Incorrect Account type setup

                It "Should NOT call 'Remove-ADServiceAccount, New-ADServiceAccountHelper' when 'AccountType' is incorrect and 'AccountTypeForce' is false" {
                    $testResourceParametersGroup = @{
                        ServiceAccountName = $mockGroupServiceAccount.Name
                        AccountType        = $objectAccountType.Expected
                        AccountTypeForce   = $false
                    }

                    # Check if Warning is returned
                    Set-TargetResource @testResourceParametersGroup 3>&1 | Should -Not -Be $null

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                It "Should call 'Remove-ADServiceAccount, New-ADServiceAccountHelper' when 'AccountType' is incorrect and 'AccountTypeForce' is true" {
                    $testResourceParametersGroup = @{
                        ServiceAccountName = $mockGroupServiceAccount.Name
                        AccountType        = 'Single'
                        AccountTypeForce   = $true
                    }

                    Set-TargetResource @testResourceParametersGroup
                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 1
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }

                #region Incorrect Ensure setup
                $objectEnsure = $mockCompareGroupServiceAccountNotCompliantEnsure | Where-Object -FilterScript {$_.Parameter -eq 'Ensure'}
                $objectEnsure.Expected = 'Absent'
                $objectEnsure.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockGroupServiceAccount.Name -eq $ServiceAccountName -and $Ensure -eq $objectEnsure.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockGroupServiceAccount.Name) and Ensure '$($objectEnsure.Expected)'"
                    return $mockCompareGroupServiceAccountNotCompliantEnsure
                }
                #endregion Incorrect Ensure type setup

                It "Should call 'Remove-ADServiceAccount' when 'Ensure' is set to 'Absent'" {
                    $testResourceParametersGroup = @{
                        ServiceAccountName = $mockGroupServiceAccount.Name
                        AccountType        = 'Group'
                        Ensure             = $objectEnsure.Expected
                    }

                    Set-TargetResource @testResourceParametersGroup

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 0
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 1
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }
            }

            Context -Name "When the system is NOT in the desired state and 'Ensure' is 'Present' (Both)" {
                $mockCompareSingleServiceAccountNotEnsure = Copy-ArrayObjects $mockCompareSingleServiceAccount

                #region Incorrect Ensure setup
                $objectEnsure = $mockCompareSingleServiceAccountNotEnsure | Where-Object -FilterScript {$_.Parameter -eq 'Ensure'}
                $objectEnsure.Expected = 'Present'
                $objectEnsure.Actual = 'Absent'
                $objectEnsure.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name)"
                    return $mockCompareSingleServiceAccountNotEnsure
                }
                #endregion Incorrect Ensure setup

                It "Should call 'New-AdServiceAccount' when 'Ensure' is set to 'Present" {
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Ensure             = $objectEnsure.Expected
                    }

                    Set-TargetResource @testResourceParametersSingle

                    Assert-MockCalled -CommandName Compare-TargetResourceState -Scope It -Times 1
                    Assert-MockCalled -CommandName New-ADServiceAccountHelper -Scope It -Times 1
                    Assert-MockCalled -CommandName Remove-ADServiceAccount -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Move-ADObject -Scope It -Exactly -Times 0
                    Assert-MockCalled -CommandName Set-ADServiceAccount -Scope It -Exactly -Times 0
                }
            }

            Context 'When system cannot connect to domain or other errors' {
                Mock -CommandName Move-ADObject -MockWith {
                    Write-Verbose "Calling 'Move-ADObject' and throwing an error"
                    throw 'Microsoft.ActiveDirectory.Management.ADServerDownException'
                }

                $mockCompareSingleServiceAccountNotCompliantPath = Copy-ArrayObjects $mockCompareSingleServiceAccount

                #region Incorrect Path setup
                $objectPath = $mockCompareSingleServiceAccountNotCompliantPath | Where-Object -FilterScript {$_.Parameter -eq 'Path'}
                $objectPath.Expected = 'WrongPath'
                $objectPath.Pass = $false

                Mock -CommandName Compare-TargetResourceState -ParameterFilter {
                    $mockSingleServiceAccount.Name -eq $ServiceAccountName -and $Path -eq $objectPath.Expected
                } -MockWith {
                    Write-Verbose "Calling Compare-TargetResourceState with $($mockSingleServiceAccount.Name) and Path '$($objectPath.Expected)'"
                    return $mockCompareSingleServiceAccountNotCompliantPath
                }
                #endregion Incorrect Path setup

                It 'Should call "Move-ADObject" and throw an error when catching any other errors besides "Account Not Found"'{
                    $testResourceParametersSingle = @{
                        ServiceAccountName = $mockSingleServiceAccount.Name
                        Path               = $objectPath.Expected
                    }
                    { Set-TargetResource  @testResourceParametersSingle -ErrorAction 'SilentlyContinue' } |
                        Should -Throw ($script:localizedData.AddingManagedServiceAccountError -f $testResourceParametersSingle.ServiceAccountName)
                }
            }
        }
        #endregion Function Set-TargetResource
    }
}
finally
{
    Invoke-TestCleanup
}