Functions/Test-MimSvcTargetResource.ps1

function Test-MimSvcTargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [HashTable]
        $DscBoundParameters,

        [System.String]
        $ObjectType,

        [System.String]
        $KeyAttributeName
    )
    if ($DscBoundParameters['Verbose'] -eq $true)
    {
        $VerbosePreference = 'Continue'
    }
    Write-Verbose "PSBoundParameters:"
    Write-VerboseHashTable $DscBoundParameters
    
    ### Initialize our return value
    $returnValue = $false

    ##HACK
    if ($DscBoundParameters.ContainsKey('GroupScope'))
    {
        $DscBoundParameters.Add('Scope', $DscBoundParameters['GroupScope']) | Out-Null
        $DscBoundParameters.Remove('GroupScope') | Out-Null
    }

    $keyAttributeValue = $DscBoundParameters[$KeyAttributeName]
    Write-Verbose "MIM Attribute Key: $KeyAttributeName, $keyAttributeValue"

    ###
    ### Build an XPath Filter to find the target object
    ###
    if ($ObjectType -eq 'SearchScopeConfiguration')
    {
        $xpathFilter = "/$ObjectType[$KeyAttributeName='$keyAttributeValue' and Order='$($DscBoundParameters['Order'])']"
    }
    elseif ($ObjectType -eq 'BindingDescription')
    {
        $xpathFilter = "/$ObjectType[BoundAttributeType=/AttributeTypeDescription[Name='$($DscBoundParameters['BoundAttributeType'])'] and BoundObjectType=/ObjectTypeDescription[Name='$($DscBoundParameters['BoundObjectType'])']]"
    }
    else
    {
        $xpathFilter = "/$ObjectType[$KeyAttributeName=""$keyAttributeValue""]"
    }
    Write-Verbose "Searching using XPath: $xpathFilter"

    ###
    ### If a credential was supplied, then do the first search as that credentials
    ### the FimAutomation PS-Snapin caches the credential so we don't have to supply
    ### the credential again in this function.
    ###
    if ($DscBoundParameters['Credential'])
    {
        Write-Verbose "Searching as $($DscBoundParameters['Credential'].UserName)."
        $mimSvcObject = Get-MimSvcObjectByXPath -Filter $xpathFilter  -Credential $DscBoundParameters['Credential']
    }
    else
    {
        Write-Verbose "Searching as LocalSystem (NOTE: this will fail if LocalSystem has not been granted permissions)."
        $mimSvcObject = Get-MimSvcObjectByXPath -Filter $xpathFilter
    }
    
    ###
    ### Check the schema cache and update if necessary
    ### NOTE: it is important that this function gets called AFTER the first MIM Service search so that credentials get cached by the FimAutomation Snap-In
    ###
    Write-MimSvcSchemaCache
    
    if ($DscBoundParameters['Ensure'] -eq 'Present')
    {
        if ($mimSvcObject -eq $null)
        {
            Write-Verbose "$ObjectType '$keyAttributeValue' not found."
            $returnValue = $false
        }
        elseif ($mimSvcObject -is [array])
        {
            Write-Verbose "Mulitple $ObjectType objects found. This will be corrected by deleting the MPRs then creating a new one based on the desirable state."
            $returnValue = $false
        }
        else
        {
            Write-Verbose "$ObjectType found, diffing the properties: $($mimSvcObject.ObjectID)"
            $objectsAreTheSame = $true

            $fimAttributeTypes = Get-MimSvcSchemaCache -ObjectType $ObjectType | Convert-MimSvcExportToPSObject

            ### Note the syntax to label the loop, used in the switch statement to continue the for loop which to PowerShell is the outer loop
            :FimAttributeTypes foreach ($attributeType in $fimAttributeTypes)
            {
                if ($attributeType.Name -in 'Assistant','AuthNWFLockedOut','AuthNWFRegistered','AuthNLockoutRegistrationID','ComputedMember','ConnectedSystem','Dependency','DomainConfiguration','ObjectID','CreatedTime','Creator','ResourceTime','DeletedTime','ObjectType','Precedence','DetectedRulesList','ExpectedRulesList','ExpirationTime','MVObjectID','Temporal')
                {
                    Write-Verbose " Skipping system-owned attribute: $($attributeType.Name)"
                    continue
                }
                if ($attributeType.Name -in 'ExplicitMember','Manager','Assistant')
                {
                    Write-Verbose " Skipping user-owned attribute: $($attributeType.Name)"
                    continue
                }

                ### Process References before comparing
                if ($attributeType.DataType -eq 'Reference')
                {
                    $targetObjectType = ''
                    $searchAttribute = 'DisplayName'

                    switch ($attributeType.Name)
                    {
                        {$_ -in 'PrincipalSet','ResourceCurrentSet','ResourceFinalSet','AllowedMembershipReferences'} {$targetObjectType = 'Set'}
                        {$_ -in 'AuthenticationWorkflowDefinition','AuthorizationWorkflowDefinition','ActionWorkflowDefinition'} {$targetObjectType = 'WorkflowDefinition'}
                        {$_ -in 'BoundAttributeType','AllowedAttributes'} {$targetObjectType = 'AttributeTypeDescription'; $searchAttribute = 'Name'}
                        {$_ -eq 'BoundObjectType'} {$targetObjectType = 'ObjectTypeDescription'; $searchAttribute = 'Name'}
                        {$_ -in 'ExplicitMember','Manager','Owner','DisplayedOwner'} {$targetObjectType = 'Person'; $searchAttribute = 'AccountName'}
                        {$_ -eq 'TimeZone'} {$targetObjectType = 'TimeZoneConfiguration'; $searchAttribute = 'TimeZoneId'}
                        {$_ -eq 'SynchronizeObjectType'} {$targetObjectType = 'ObjectTypeDescription'; $searchAttribute = 'Name'}
                        {$_ -eq 'ManagementAgentID'} {$targetObjectType = 'ma-data';}
                        Default {Write-Warning "Skipping a reference attribute we don't know how to resolve: $($attributeType.Name).";continue FimAttributeTypes}
                    }

                    ### Is there a value on both the MIM object and DSC object? if yes then we need to convert the value from DSC to an ObjectID
                    if ($mimSvcObject.($attributeType.Name) -and $DscBoundParameters[$attributeType.Name])
                    {
                        if ($attributeType.Multivalued -eq 'True')
                        {
                            $mimSvcObjectIDs = $DscBoundParameters[$attributeType.Name] | 
                            ForEach-Object {
                                Write-Verbose " Resolving $($attributeType.Name) to a GUID: $_"
                                "urn:uuid:{0}" -F (Get-MimSvcObjectID -ObjectType $targetObjectType -AttributeName $searchAttribute -AttributeValue $_)
                            }
                            $DscBoundParameters[$attributeType.Name] = $mimSvcObjectIDs
                        }
                        else
                        {
                            Write-Verbose " Resolving $($attributeType.Name) to a GUID: $($DscBoundParameters[$attributeType.Name])"
                            $DscBoundParameters[$attributeType.Name] = "urn:uuid:{0}" -F (Get-MimSvcObjectID -ObjectType $targetObjectType -AttributeName $searchAttribute -AttributeValue $DscBoundParameters[$attributeType.Name])
                        }
                    }
                }

                ### Process Strings with References
                if ($attributeType.Name -in 'Filter','XOML')
                {
                    Write-Verbose " Processing references in the '$($attributeType.Name)' attribute"

                    ### Regex for finding JSON strings
                    $regexJsons = [regex]'\{ObjectType:".+?",AttributeName:".+?",AttributeValue:".+?"}'

                    ### Replace the JSON string with a GUID
                    foreach ($jsonString in $regexJsons.Matches($DscBoundParameters[$attributeType.Name]))
                    {
                        Write-Verbose " Converting '$jsonString' to a FIM ObjectID"
                        $jsonObject = ConvertFrom-Json -InputObject $jsonString

                        $mimSvcObjectID = Get-MimSvcObjectID -ObjectType $jsonObject.ObjectType -AttributeName $jsonObject.AttributeName -AttributeValue $jsonObject.AttributeValue

                        $DscBoundParameters[$attributeType.Name] = $DscBoundParameters[$attributeType.Name] -replace $jsonString, $mimSvcObjectID 
                    }
                }

                Write-Verbose " Comparing $($attributeType.Name)"
                if ($attributeType.Multivalued -eq 'True')
                {
                    Write-Verbose " From DSC: $($DscBoundParameters[$attributeType.Name] -join ',')"
                    Write-Verbose " From FIM: $($mimSvcObject.($attributeType.Name) -join ',')"

                    if ($DscBoundParameters[$attributeType.Name] -eq $null -and $mimSvcObject.($attributeType.Name) -eq $null)
                    {
                        ### do nothing. done.
                    }
                    elseif ($DscBoundParameters[$attributeType.Name] -eq $null -and $mimSvcObject.($attributeType.Name) -ne $null)
                    {
                        ### need to delete all attribute values in MIM
                        Write-Warning " '$($attributeType.Name)' property is not the same."
                        $objectsAreTheSame = $false
                    }
                    elseif ($DscBoundParameters[$attributeType.Name] -ne $null -and $mimSvcObject.($attributeType.Name) -eq $null)
                    {
                        ### need to add all attribute values in MIM
                        Write-Warning " '$($attributeType.Name)' property is not the same."
                        $objectsAreTheSame = $false
                    }
                    elseif (Compare-Object $DscBoundParameters[$attributeType.Name] $mimSvcObject.($attributeType.Name))
                    {
                        Write-Warning " '$($attributeType.Name)' property is not the same."
                        $objectsAreTheSame = $false
                    }
                }
                elseif ($attributeType.DataType -eq 'Boolean')
                {
                    Write-Verbose " From DSC: $($DscBoundParameters[$attributeType.Name])"
                    Write-Verbose " From FIM: $($mimSvcObject.($attributeType.Name))"
                    if ([Convert]::ToBoolean($DscBoundParameters[$attributeType.Name]) -ne [Convert]::ToBoolean($mimSvcObject.($attributeType.Name)))
                    {
                        Write-Warning " '$($attributeType.Name)' property is not the same."
                        $objectsAreTheSame = $false
                    }
                }
                else
                {
                    Write-Verbose " From DSC: $($DscBoundParameters[$attributeType.Name])"
                    Write-Verbose " From FIM: $($mimSvcObject.($attributeType.Name))"

                    if ($DscBoundParameters[$attributeType.Name] -ne $mimSvcObject.($attributeType.Name))
                    {
                        Write-Warning " '$($attributeType.Name)' property is not the same."
                        $objectsAreTheSame = $false
                    }
                }
            }

            $returnValue = $objectsAreTheSame 
        }
    }
    elseif($DscBoundParameters['Ensure'] -eq 'Absent')
    {
        if ($mimSvcObject -ne $null)
        {
            $returnValue = $false
        }
        else
        {
            $returnValue = $true
        }
    }
    else
    {
        Write-Error "Expected the 'Ensure' parameter to be 'Present' or 'Absent'"
    }

    ## Return the value
    return $returnValue
}