functions/Invoke-AdmfItem.ps1

function Invoke-AdmfItem {
    <#
    .SYNOPSIS
        Apply individual changes found by Test-AdmfDc, Test-AdmfDomain or Test-AdmfForest.
     
    .DESCRIPTION
        Apply individual changes found by Test-AdmfDc, Test-AdmfDomain or Test-AdmfForest.
        This allows applying individual changes, irrespective of domain or type.
 
        While this command accepts from the pipeline, it groups results by server and executes during the end phase.
        This is done to rationalize the application of credential providers, context switching and connection management.
     
    .PARAMETER TestResult
        The test results to apply.
        Output objects of Test-AdmfDc, Test-AdmfDomain or Test-AdmfForest.
     
    .PARAMETER Credential
        The credentials to use for this operation.
     
    .PARAMETER CredentialProvider
        The credential provider to use to resolve the input credentials.
        See help on Register-AdmfCredentialProvider for details.
     
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
     
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
     
    .EXAMPLE
        PS C:\> Test-AdmfDomain -Server contoso.com | Where-Object ObjectType -in User, Group | Where-Object ObjectType -eq Create | Invoke-AdmfItem
 
        Apply all create actions for all users and groups in contoso.com.
    #>

    [Alias('iai')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
    [CmdletBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter(ValueFromPipeline = $true)]
        $TestResult,

        [PSCredential]
        $Credential,

        [string]
        $CredentialProvider = 'default'
    )
    
    begin {
        #region Functions
        function Invoke-TestResult {
            [CmdletBinding()]
            param(
                $TestResult,

                $Parameters
            )

            switch ($TestResult.ObjectType) {
                # DCManagement
                'Share' { $TestResult | Invoke-DCShare @Parameters }
                'FSAccessRule' { $TestResult | Invoke-DCAccessRule @Parameters }

                # DomainManagement
                'AccessRule' { $TestResult | Invoke-DMAccessRule @Parameters }
                'Acl' { $TestResult | Invoke-DMAcl @Parameters }
                'DomainLevel' { $TestResult | Invoke-DMDomainLevel @Parameters }
                'ExchangeVersion' { $TestResult | Invoke-DMExchange @Parameters }
                'GPLink' { $TestResult | Invoke-DMGPLink @Parameters }
                'GPOwner' { $TestResult | Invoke-DMGPOwner @Parameters }
                'GPPermission' { $TestResult | Invoke-DMGPPermission @Parameters }
                'GroupMembership' { $TestResult | Invoke-DMGroupMembership @Parameters }
                'GroupPolicy' { $TestResult | Invoke-DMGroupPolicy @Parameters }
                'Group' { $TestResult | Invoke-DMGroup @Parameters }
                'Object' { $TestResult | Invoke-DMObject @Parameters }
                'OrganizationalUnit' { $TestResult | Invoke-DMOrganizationalUnit @Parameters -Delete }
                'PSO' { $TestResult | Invoke-DMPasswordPolicy @Parameters }
                'ServiceAccount' { $TestResult | Invoke-DMServiceAccount @Parameters }
                'User' { $TestResult | Invoke-DMUser @Parameters }
                'WmiFilter' { $TestResult | Invoke-DMWmiFilter @parameters }

                # ForestManagement
                'Certificate' { $TestResult | Invoke-FMCertificate @Parameters }
                'ExchangeSchema' { $TestResult | Invoke-FMExchangeSchema @Parameters }
                'ForestLevel' { $TestResult | Invoke-FMForestLevel @Parameters }
                'NTAuthStore' { $TestResult | Invoke-FMNTAuthStore @Parameters }
                'Schema' { $TestResult | Invoke-FMSchema @Parameters }
                'SchemaDefaultPermission' { $TestResult | Invoke-FMSchemaDefaultPermission @Parameters }
                'SchemaLdif' { $TestResult | Invoke-FMSchemaLdif @Parameters }
                'Server' { $TestResult | Invoke-FMServer @Parameters }
                'SiteLink' { $TestResult | Invoke-FMSiteLink @Parameters }
                'Site' { $TestResult | Invoke-FMSite @Parameters }
                'Subnet' { $TestResult | Invoke-FMSubnet @Parameters }
            }
        }
        #endregion Functions

        $testResults = [System.Collections.Generic.List[object]]::new()
    }
    process {
        foreach ($result in $TestResult) {
            $hasName = $result.PSObject.TypeNames -match '^DomainManagement|^ForestManagement|^DCManagement'
            if (-not $hasName) {
                Write-PSFMessage -Level Warning -String 'Invoke-AdmfItem.Error.BadInput' -StringValues $result -Target $result
                continue
            }
            $testResults.Add($result)
        }
    }
    end {
        $resultGroups = $testResults | Group-Object Server
        foreach ($resultGroup in $resultGroups) {
            #region Prepare Credential Stuff
            Reset-DomainControllerCache

            $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Credential
            $parameters.Server = $resultGroup.Name

            try {
                $originalArgument = Invoke-PreCredentialProvider @parameters -ProviderName $CredentialProvider -Parameter $parameters
                Invoke-PSFCallback -Data $parameters -EnableException $true
                Set-AdmfContext @parameters -Interactive -ReUse -EnableException
            }
            catch {
                Write-PSFMessage -Level Warning -String 'Invoke-AdmfItem.Error.PrepareContext' -StringValues $resultGroup.Name, $resultGroup.Count -Target $resultGroup -ErrorRecord $_ -EnableException $true -PSCmdlet $PSCmdlet
                if ($originalArgument) {
                    try { Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential }
                    catch { Write-PSFMessage -Level Warning -String 'Invoke-AdmfItem.Error.PostCredentialProvider' -StringValues $CredentialProvider, $resultGroup.Name, $resultGroup.Count -ErrorRecord $_ -Target $resultGroup -PSCmdlet $PSCmdlet }
                }
                continue
            }
            $parameters += $PSBoundParameters | ConvertTo-PSFHashtable -Include WhatIf, Confirm, Verbose, Debug
            $parameters.Server = $resultGroup.Name
            #endregion Prepare Credential Stuff

            #region Execute Test Results
            try {
                foreach ($resultItem in $resultGroup.Group) {
                    if (-not (Test-PSFShouldProcess -Target $resultItem -ActionString 'Invoke-AdmfItem.Processing.ShouldProcess' -ActionStringValues $resultItem.Server, $resultItem.ObjectType, $resultItem.Type, $resultItem.Identity -PSCmdlet $PSCmdlet)) {
                        continue
                    }
                    Write-PSFMessage -Level Host -String 'Invoke-AdmfItem.Processing' -Target $resultItem -StringValues $resultItem.Server, $resultItem.ObjectType, $resultItem.Type, $resultItem.Identity -PSCmdlet $PSCmdlet
                    Invoke-TestResult -TestResult $resultItem -Parameters $parameters
                }
            }
            #endregion Execute Test Results

            #region Post Processing
            catch {
                Write-PSFMessage -Level Warning -String 'Invoke-AdmfItem.Error.Execute' -StringValues $resultGroup.Name, $resultGroup.Count -Target $resultGroup -ErrorRecord $_ -EnableException $true -PSCmdlet $PSCmdlet
            }
            finally {
                try {
                    Disable-PSFConsoleInterrupt
                    Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential
                }
                catch {
                    Enable-PSFConsoleInterrupt
                    Write-PSFMessage -Level Warning -String 'Invoke-AdmfItem.Error.PostCredentialProvider' -StringValues $CredentialProvider, $resultGroup.Name, $resultGroup.Count -ErrorRecord $_ -Target $resultGroup
                }
            }
            #endregion Post Processing
        }
    }
}