Shared/Remove-SupplementalSigners.psm1

Function Remove-SupplementalSigners {
    <#
.SYNOPSIS
    Removes the entire SupplementalPolicySigners block
    and any Signer in Signers node that have the same ID as the SignerIds of the SupplementalPolicySigner(s) in <SupplementalPolicySigners>...</SupplementalPolicySigners> node
    from a CI policy XML file
.NOTES
    It doesn't do anything if the input policy file has no SupplementalPolicySigners block.
    It will also always check if the Signers node is not empty, like
   <Signers>
   </Signers>
 
   if it is then it will close it: <Signers />
 
   The function can run infinite number of times on the same file.
.PARAMETER Path
    The path to the CI policy XML file
.INPUTS
    System.IO.FileInfo
.OUTPUTS
    System.Void
#>

    [CmdletBinding()]
    [OutputType([System.Void])]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [System.IO.FileInfo]$Path
    )
    Begin {
        # Make sure the input file is compliant with the CI policy schema
        if (![WDACConfig.CiPolicyTest]::TestCiPolicy($Path, $null)) {

            throw 'The XML file is not compliant with the CI policy schema'
        }

        # Get the XML content from the file
        [System.Xml.XmlDocument]$XMLContent = Get-Content -Path $Path
    }
    Process {

        # Get the SiPolicy node
        [System.Xml.XmlElement]$SiPolicyNode = $XMLContent.SiPolicy

        # Declare the namespace manager and add the default namespace with a prefix
        [System.Xml.XmlNamespaceManager]$NameSpace = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $XMLContent.NameTable
        $NameSpace.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy')

        # Check if the SupplementalPolicySigners node exists and has child nodes
        if ($SiPolicyNode.SupplementalPolicySigners -and $SiPolicyNode.SupplementalPolicySigners.HasChildNodes) {

            [WDACConfig.VerboseLogger]::Write('Removing the SupplementalPolicySigners block and their corresponding Signers')

            # Select the SupplementalPolicySigners node using XPath and the namespace manager
            [System.Xml.XmlElement[]]$NodesToRemove_SupplementalPolicySigners = $SiPolicyNode.SelectNodes('//ns:SupplementalPolicySigners', $NameSpace)

            # Get the SignerIds of the nodes inside of the SupplementalPolicySigners nodes - <SupplementalPolicySigners>...</SupplementalPolicySigners>
            [System.Xml.XmlElement[]]$SupplementalPolicySignerIDs = $SiPolicyNode.SupplementalPolicySigners.SelectNodes("//ns:SupplementalPolicySigner[starts-with(@SignerId, 'ID_SIGNER_')]", $NameSpace)

            # Get the unique SignerIds
            [System.String[]]$SupplementalPolicySignerIDs = $SupplementalPolicySignerIDs.SignerId | Select-Object -Unique

            # Select all the Signer nodes in <Signers>...</Signers> that have the same ID as the SignerIds of the SupplementalPolicySigners nodes
            $NodesToRemove_Signers = foreach ($SignerID in $SupplementalPolicySignerIDs) {
                $SiPolicyNode.Signers.SelectNodes("//ns:Signer[@ID='$SignerID']", $NameSpace)
            }

            # Loop through the Signer nodes to remove
            foreach ($SignerNode in $NodesToRemove_Signers) {
                # Remove the Signer from the Signers node
                [System.Void]$SiPolicyNode.Signers.RemoveChild($SignerNode)
            }

            # Loop through the <SupplementalPolicySigners>..</SupplementalPolicySigners> nodes to remove, in case there are multiple!
            foreach ($Node in $NodesToRemove_SupplementalPolicySigners) {

                # Remove the <SupplementalPolicySigners> node from the parent node which is $SiPolicyNode
                [System.Void]$SiPolicyNode.RemoveChild($Node)
            }
        }

        # Check if the Signers node is empty, if it is then close it
        if (-NOT $SiPolicyNode.Signers.HasChildNodes) {

            # Create a new self-closing element with the same name and attributes as the old one
            [System.Xml.XmlElement]$NewSignersNode = $XMLContent.CreateElement('Signers', 'urn:schemas-microsoft-com:sipolicy')

            foreach ($Attribute in $SiPolicyNode.Signers.Attributes) {
                $NewSignersNode.SetAttribute($Attribute.Name, $Attribute.Value)
            }

            # Select the Signers node using XPath and the namespace manager
            [System.Xml.XmlElement]$OldSignersNode = $XMLContent.SelectSingleNode('//ns:Signers', $NameSpace)

            # Replace the old element with the new one
            [System.Void]$SiPolicyNode.ReplaceChild($NewSignersNode, $OldSignersNode)
        }
    }
    End {
        $XMLContent.Save($Path)
    }
}
Export-ModuleMember -Function 'Remove-SupplementalSigners'