Driver.Firmware.Servicing.psm1

#Region './Private/Invoke-GetRequest.ps1' 0
function Invoke-GetRequest {
    <#
    .SYNOPSIS
        Performs Get Requests with Pagination.
    .DESCRIPTION
        Performs Get Requests with Pagination. Without the logic in this function, all results would not be returned.
    .NOTES
        Tested on PowerShell 5 and 7 on Windows.
    .EXAMPLE
        Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/me"
        Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/me" -All
    .PARAMETER Uri
        The URI to perform the Get Request on. This is a mandatory parameter.
    .PARAMETER All
        This switch will determine if paginated requests will be run. This is an optional parameter.
    #>

    [CmdletBinding()]
    param (
        # Parameter help description
        [Parameter(Mandatory = $true)]
        [string]
        $Uri,
        # This switch will determine if paginated requests will be run
        [switch]
        $All
    )
    process {
        switch ($All) {
            true {
                $getRequestParameters = @{
                    Method = "GET"
                    URI    = $Uri
                }
                $getRequest = Invoke-MgGraphRequest @getRequestParameters -ErrorAction Stop
                $requestArray = @()
                $requestArray += IF ($getRequest.value) { $getRequest.value }else { $getRequest }
                while ($getRequest.'@odata.nextLink') {
                    $getRequest_NextLink = @{
                        Method = "GET"
                        URI    = $getRequest.'@odata.nextLink'
                    }
                    $getRequest = Invoke-MgGraphRequest @getRequest_NextLink -ErrorAction Stop
                    $requestArray += IF ($getRequest.value) { $getRequest.value }else { $getRequest }
                }
                $return = $requestArray
            }
            false {
                $getRequestParameters = @{
                    Method = "GET"
                    URI    = $Uri
                }
                $return = (Invoke-MgGraphRequest @getRequestParameters -ErrorAction Stop).value
            }
        }
    }
    end {
        return $return
    }
}
#EndRegion './Private/Invoke-GetRequest.ps1' 60
#Region './Public/Add-DeploymentAudienceMember.ps1' 0
function Add-DeploymentAudienceMember {
    <#
    .SYNOPSIS
        Add members to a deployment audience for Windows Updates for Business
    .DESCRIPTION
        This function will check if the deployments audiences have the devices as members, and if not they will be added to the audience.
    .EXAMPLE
        Add-DeploymentAudienceMember -azureDeviceIDs ("ID1","ID2") -audienceID <AudienceID>
    .PARAMETER azureDeviceIDs
        The Azure Device IDs to add to the audience.
    .PARAMETER audienceID
        The Update Audience ID to add the members to.
    .PARAMETER policyID
        The Update Policy ID to add the members to.
    .NOTES
        You can specify either the audienceID or policyID parameter, if both are specified the audienceID will be used.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [array]
        $azureDeviceIDs,
        # The Update Audience ID
        [Parameter(Mandatory = $false)]
        [string]
        $audienceID,
        [Parameter(Mandatory = $false)]
        [string]
        $policyID
    )
    begin {
        if ([String]::IsNullOrEmpty($audienceID) -and ([String]::IsNullOrEmpty($policyID))) {
            throw "You must specify either the audienceID or policyID parameter."
        }
        # Create the param body base
        $paramBody = @{
            addMembers = @(
            )
        }
    }
    process {
        IF (-Not([String]::IsNullOrEmpty($audienceID))) {
            $updateAudienceMembers = Get-DeploymentAudienceMember -audienceID $audienceID
        }
        elseif (([String]::IsNullOrEmpty($policyID))) {
            $updateAudienceMembers = Get-DeploymentAudienceMember -policyID $policyID
        }
        foreach ($id in $azureDeviceIDs) {
            IF (-Not($updateAudienceMembers.id -contains $id)) {
                $memberObject = @{
                    "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                    id            = $id
                }
                $paramBody.addMembers += $memberObject
            }
        }
    }
    end {
        IF ($paramBody.addMembers.Count -ge 1) {
            Invoke-MgGraphRequest `
                -Method POST `
                -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$audienceID')/updateAudience" `
                -Body $paramBody
        }
    }
}
#EndRegion './Public/Add-DeploymentAudienceMember.ps1' 67
#Region './Public/Add-DriverUpdateApproval.ps1' 0
function Add-DriverUpdateApproval {
    <#
    .SYNOPSIS
        Add a driver update approval to a policy for Windows Updates for Business.
    .DESCRIPTION
        This function will add a driver update approval to a policy for Windows Updates for Business.
        Note: The catalog entry must be applicable to the policy, this will be checked before adding the approval.
    .EXAMPLE
        Add-DriverUpdateApproval -policyIDs ("ID1","ID2") -catalogEntryID <CatalogEntryID>
    .PARAMETER policyIDs
        The policy IDs to add the approval to.
    .PARAMETER catalogEntryID
        The catalog entry ID to add to the policy.
    .PARAMETER deferDays
        The days to defer the deployment of the driver update.
    .PARAMETER return
        Return the response from the API.
    #>

    [CmdletBinding()]
    [OutputType("System.Array", ParameterSetName = "return")]
    param (
        [Parameter(Mandatory = $true)]
        [array]
        $policyIDs,
        # The catalog entry ID, use Get-DriverUpdatePolicyApplicableContent to get the ID.
        [Parameter(Mandatory = $true)]
        [string]
        $catalogEntryID,
        # The days to defer the deployment of the driver update.
        [Parameter()]
        [int]
        $deferDays = 0,
        [parameter(ParameterSetName = "return", Mandatory = $false, dontShow = $true)]
        [array]
        $return = @()
    )
    begin {
        # Create the param body base
        $paramBody = @{
            "@odata.type" = "#microsoft.graph.windowsUpdates.contentApproval"
            content = @{
                "@odata.type" = "#microsoft.graph.windowsUpdates.catalogContent"
                catalogEntry = @{
                    "@odata.type" = "#microsoft.graph.windowsUpdates.driverUpdateCatalogEntry"
                    id = $catalogEntryID
                }
            }
            deploymentSettings = @{
                "@odata.type" = "microsoft.graph.windowsUpdates.deploymentSettings"
                schedule = @{
                    startDateTime = ""
                }
            }
        }
    }
    process {
        foreach ($policyID in $policyIDs) {
            $applicableConent = Get-DriverUpdatePolicyApplicableContent -policyID $policyID
            if ($applicableConent.catalogEntry.id -contains $catalogEntryID) {
                $startDate = (Get-Date).AddDays($deferDays).ToString("yyyy-MM-ddT00:00:00Z")
                try {
                    $paramBody.deploymentSettings.schedule.startDateTime = $startDate
                    $responce = Invoke-MgGraphRequest `
                        -Method POST `
                        -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies('$policyID')/complianceChanges" `
                        -Body $paramBody
                    $return += $responce
                }
                catch {
                    throw "Error adding the catalog entry ID to the policy ID: $policyID"
                }

            } else {
                Write-Warning "The catalog entry ID is not applicable to the policy ID: $policyID"
            }

        }
    }
    end {
        $return
    }
}
#EndRegion './Public/Add-DriverUpdateApproval.ps1' 83
#Region './Public/Get-DeploymentAudience.ps1' 0
function Get-DeploymentAudience {
    <#
    .SYNOPSIS
        This function get Update Deployment Audiences.
    .DESCRIPTION
        This function get Update Deployment Audiences.
    .NOTES
        This has only been tested for the commercial driver and firmware updates.
    .EXAMPLE
        Get-DeploymentAudience
        Get-DeploymentAudience -audienceID <audienceID>
    .PARAMETER audienceID
        The audience ID to get the deployment audience for.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $audienceID
    )
    process {
        try {
            IF ([string]::IsNullOrEmpty($audienceID)) {
                $DriverUpdateDeploymentAudience = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences" -All
            }
            elseif (-Not([string]::IsNullOrEmpty($audienceID))) {
                $DriverUpdateDeploymentAudience = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences?`$filter=id eq '$audienceID'"
            }
        }
        catch {
            throw "Unable to get the deployment audiences. $($_.Exception.Message)"
        }
    }
    end {
        return $DriverUpdateDeploymentAudience
    }
}
#EndRegion './Public/Get-DeploymentAudience.ps1' 38
#Region './Public/Get-DeploymentAudienceMember.ps1' 0
function Get-DeploymentAudienceMember {
    <#
.SYNOPSIS
    This function will get the members of a deployment audience for Windows Updates for Business.
.DESCRIPTION
    This function will get the members of a deployment audience for Windows Updates for Business.
.NOTES
    This has only been tested for the commercial driver and firmware updates.
.EXAMPLE
    Get-DeploymentAudienceMember -policyID <PolicyID>
.PARAMETER policyID
    The policy ID to get the members for.
.PARAMETER audienceID
    The audience ID to get the members for.
.NOTES
    You can specify either the audienceID or policyID parameter, if both are specified the audienceID will be used.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $policyID,
        [Parameter(Mandatory = $false)]
        [string]
        $audienceID
    )
    begin {
        if ([String]::IsNullOrEmpty($audienceID) -and ([String]::IsNullOrEmpty($policyID))) {
            throw "You must specify either the audienceID or policyID parameter."
        }
    }
    process {
        try {
            if(-Not([String]::IsNullOrEmpty($audienceID))) {
                $members = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$($audienceID)')/members" -All
            }
            ELSEIF (-Not([String]::IsNullOrEmpty($policyID))) {
                $policy = Get-DriverUpdatePolicy -policyID $policyID
                $members = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$($policy.audience.id)')/members" -All
            }
        }
        catch {
            throw "Unable to get the members of the deployment audience. $($_.Exception.Message)"
        }
    }
    end {
        return $members
    }
}
#EndRegion './Public/Get-DeploymentAudienceMember.ps1' 50
#Region './Public/Get-DriverUpdatePolicy.ps1' 0
function Get-DriverUpdatePolicy{
    <#
.SYNOPSIS
    This function gets all update policies for Windows Updates for Business for Driver and Firmware Servicing.
.DESCRIPTION
    This function gets all update policies for Windows Updates for Business for Driver and Firmware Servicing.
.NOTES
    This has only been tested for the commercial driver and firmware updates.
    https://learn.microsoft.com/en-us/graph/api/adminwindowsupdates-list-updatepolicies?view=graph-rest-beta&tabs=http
.EXAMPLE
    Get-DriverUpdatePolicy
.PARAMETER policyID
    The policy ID to get. If not specified, all policies will be returned.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $policyID
    )
    process {
        try {
            if([string]::IsNullOrEmpty($policyID)){
                $policy = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies" -All
            } else {
                $policy = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies?`$filter=id eq '$policyID'"
            }
        }
        catch {
            throw "Unable to get the policies. $($_.Exception.Message)"
        }
    }
    end {
        return $policy
    }
}
#EndRegion './Public/Get-DriverUpdatePolicy.ps1' 37
#Region './Public/Get-DriverUpdatePolicyApplicableContent.ps1' 0
function Get-DriverUpdatePolicyApplicableContent {
    <#
.SYNOPSIS
    This function will get the deployable content of a deployment audience based on a PolicyID for Windows Updates for Business.
.DESCRIPTION
    This function will get the deployable content of a deployment audience based on a PolicyID for Windows Updates for Business.
.NOTES
    This has only been tested for the commercial driver and firmware updates.
.EXAMPLE
    Get-DriverUpdatePolicyApplicableContent -policyID <PolicyID>
.PARAMETER policyID
    The policy ID to get the applicable content for.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $policyID
    )
    process {
        try {
            $policy = Get-DriverUpdatePolicy -policyID $policyID
            $applicableConent = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$($policy.audience.id)')/applicableContent?`$expand=catalogEntry" -All
        }
        catch {
            throw "Unable to get the applicable content of the deployment audience. $($_.Exception.Message)"
        }
    }
    end {
        return $applicableConent
    }
}
#EndRegion './Public/Get-DriverUpdatePolicyApplicableContent.ps1' 33
#Region './Public/Get-DriverUpdatePolicyComplianceChange.ps1' 0
function Get-DriverUpdatePolicyComplianceChange {
    <#
.SYNOPSIS
    This function will get the compliance changes for an update policy for Windows Updates for Business.
.DESCRIPTION
    This function will get the compliance changes for an update policy for Windows Updates for Business. This include the deployment schedule for the Driver Updates if Deferrals are configured.
.NOTES
    This has only been tested for the commercial driver and firmware updates.
.EXAMPLE
    Get-DriverUpdatePolicyComplianceChange -policyID <PolicyID>
.PARAMETER policyID
    The policy ID to get the applicable content for.
.PARAMETER catalogentryID
    The update catalog entry ID to get the compliance changes for.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $policyID,
        [Parameter()]
        [string]
        $catalogentryID
    )
    process {
        try {
            Write-Verbose "Getting compliance changes of the update policy: $policyID"
            $complainceChanges = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies('$($policyID)')/complianceChanges" -All
            if ([string]::IsNullOrEmpty($catalogentryID)) {
                Write-Verbose "Returning all compliance changes of the update policy: $policyID"
                $return = $complainceChanges
            }
            else {
                Write-Verbose "Returning compliance changes of the update policy: $policyID - for catalog entry ID: $catalogentryID"
                $return = $complainceChanges | Where-Object { $_.content.catalogEntry.id -eq $catalogentryID }
            }
        }
        catch {
            throw "Unable to get compliance changes of the update policy. $($_.Exception.Message)"
        }
    }
    end {
        return $return
    }
}
#EndRegion './Public/Get-DriverUpdatePolicyComplianceChange.ps1' 46
#Region './Public/Get-UpdatableAsset.ps1' 0
function Get-UpdatableAsset {
    <#
    .SYNOPSIS
        This function get all updateable assets for Windows Updates for Business.
    .DESCRIPTION
        This function get all updateable assets for Windows Updates for Business.
    .NOTES
        This has only been tested for the commercial driver and firmware updates.
    .EXAMPLE
        Get-UpdatableAsset
        Get-UpdatableAsset -AzureADDeviceID <AzureADDeviceID>
    .PARAMETER AzureADDeviceID
        The Azure AD Device ID to get the updatable asset for.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $AzureADDeviceID
    )
    process {
        try {
            IF ([string]::IsNullOrEmpty($AzureADDeviceID)) {
                $updatableAsset = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets" -All
            }
            elseif (-Not([string]::IsNullOrEmpty($AzureADDeviceID))) {
                $updatableAsset = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets?`$filter=id eq '$AzureADDeviceID'"
            }
        }
        catch {
            throw "Unable to get the updatable assets. $($_.Exception.Message)"
        }
    }
    end {
        return $updatableAsset
    }
}
#EndRegion './Public/Get-UpdatableAsset.ps1' 38
#Region './Public/New-DeploymentAudience.ps1' 0
function New-DeploymentAudience {
    <#
    .SYNOPSIS
        Creates a WUfBDS Deployment Audience
    .DESCRIPTION
        Creates a WUfBDS Deployment Audience for Policy Assignment.
    .EXAMPLE
        New-DeploymentAudience
    .PARAMETER daAudienceParams
        The parameters to create the deployment audience.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        $daAudienceParams = @{}
    )
    process {

        if ($pscmdlet.ShouldProcess(
            'Creating a WUfBDS Deployment Audience',
            'Warning: Creating a WUfBDS Deployment Audience',
            'Question: Are you sure you want to do continue?'))
        {
            $daAudience = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences" -Method POST -Body $daAudienceParams
        }
    }
    end {
        return $daAudience
    }
}
#EndRegion './Public/New-DeploymentAudience.ps1' 30
#Region './Public/New-DriverUpdatePolicy.ps1' 0
function New-DriverUpdatePolicy {
    <#
    .SYNOPSIS
        This function is to be used to create a Driver Policy for WUfBDS
    .DESCRIPTION
        Based on the inputs, will depend on the policy creation type of Manual or Automatic.
    .EXAMPLE
        New-DriverUpdatePolicy -audienceID $daAudience.id -policyType Automatic -deferralTime PT1D
    .PARAMETER audienceID
        The Deployment Audience ID. This can be obtained from the Get-DeploymentAudience function.
    .PARAMETER policyType
        The type of policy to create. Manual or Automatic.
    .PARAMETER deferralTime
        The deferral time for the policy. This is only required for Automatic policies.
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        # The Deployment Audience ID
        [Parameter(Mandatory = $true)]
        [string]
        $audienceID,
        # Manual or Automatic Publishing Policy
        [Parameter(Mandatory = $true)]
        [ValidateSet("Manual", "Automatic")]
        [string]
        $policyType,
        # ISO8601 Timeformat for Deferral
        [Parameter()]
        [string]
        $deferralTime = "PT0S"
    )
    begin {
        #Initialise the base object body.
        $paramBody = @{
            "@odata.type"                  = "#microsoft.graph.windowsUpdates.updatePolicy"
            audience                       = @{
                id = $audienceID
            }
            autoEnrollmentUpdateCategories = @(
                "driver"
            )
        }
    }
    process {
        switch ($policyType) {
            Manual {
                $paramBody.deploymentSettings = @{
                    schedule             = $null
                    monitoring           = $null
                    contentApplicability = $null
                    userExperience       = $null
                    expedite             = $null
                }
            }
            Automatic {

                $paramBody.deploymentSettings = @{
                    schedule             = $null
                    monitoring           = $null
                    contentApplicability = @{
                        offerWhileRecommendedBy = @(
                            "microsoft"
                        )
                        safeguard               = $null
                    }
                    userExperience       = $null
                    expedite             = $null
                }
                $paramBody.complianceChangeRules = @(
                    @{
                        "@odata.type"                 = "#microsoft.graph.windowsUpdates.contentApprovalRule"
                        durationBeforeDeploymentStart = $deferralTime
                        contentFilter                 = @{
                            "@odata.type" = "#microsoft.graph.windowsUpdates.driverUpdateFilter"
                        }
                    }
                )
            }
        }
    }
    end {
        if ($PSCmdlet.ShouldProcess(
                'Creating a WUfBDS Driver Update Policy',
                'Warning: Creating a WUfBDS Driver Update Policy',
                'Question: Are you sure you want to do continue?')) {
            Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies"`
                -Method POST `
                -Body $paramBody `
                -ContentType 'application/json'
        }
    }
}
#EndRegion './Public/New-DriverUpdatePolicy.ps1' 93
#Region './Public/Push-EnrollUpdatableAsset.ps1' 0
function Push-EnrollUpdatableAsset {
    <#
    .SYNOPSIS
        This script is to be used to push enrol assets as without it, the delay could be significant.
    .DESCRIPTION
        This script is to be used to push enrol assets as without it, the delay could be significant.
    .NOTES
        This has only been tested for the commercial driver and firmware updates.
    .EXAMPLE
        Push-EnrollUpdatableAsset -azureDeviceIDs ('ID1','ID2')
    .PARAMETER azureDeviceIDs
        The Azure AD Device IDs to enrol as updatable assets.
    .NOTES
        This script is to be used to push enrol assets as without it, the delay could be significant. This is also a recommended step in the documentation from Microsoft.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [array]
        $azureDeviceIDs
    )
    begin {
        # Create the param body base
        $paramBody = @{
            updateCategory = "driver"
            assets         = @(
            )
        }
    }
    process {
        if($PSCmdlet.ShouldProcess("Enroll Assets", "Enroll Assets")) {
            $updateAudienceMembers = Get-DeploymentAudienceMember -policyID $updateAudienceID
            foreach ($id in $azureDeviceIDs) {
                IF (-Not($updateAudienceMembers.id -contains $id)) {
                    $memberObject = @{
                        "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                        id            = $id
                    }
                    $paramBody.addMembers += $memberObject
                }
            }
        }
        $updatableAssets = Get-UpdatableAsset
        foreach ($id in $azureDeviceIDs) {
            IF (-Not($updatableAssets | Where-Object { $_.id -match $id }).enrollments.updateCategory -notcontains "driver") {
                $memberObject = @{
                    "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                    id            = $id
                }
                $paramBody.assets += $memberObject
            }
        }
    }
    end {
        IF ($paramBody.assets.Count -ge 1) {
            Invoke-MgGraphRequest `
                -Method POST `
                -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets/enrollAssets" `
                -Body $paramBody
        }
    }
}
#EndRegion './Public/Push-EnrollUpdatableAsset.ps1' 63
#Region './Public/Remove-DeploymentAudience.ps1' 0
function Remove-DeploymentAudience {
    <#
    .SYNOPSIS
        Deletes a Deployment Audience for Windows Updates for Business.
    .DESCRIPTION
        This function will delete a Deployment Audience for Windows Updates for Business.
    .EXAMPLE
        Remove-DeploymentAudience -audienceID 00000000-0000-0000-0000-000000000000
    .PARAMETER audienceID
        The Update Policy ID to delete.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([string])]
    param (
        # The Deployment Audience ID
        [Parameter(Mandatory = $true)]
        [string]
        $audienceID
    )
    process {
        if ($PSCmdlet.ShouldProcess("Deletes the Update Audience with ID $audienceID",
            "Delete Update Audience",
            "Do you want to delete the Update Audience with ID $audienceID?"
            )
        ) {
            try {
                Invoke-MgGraphRequest `
                    -Method DELETE `
                    -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$audienceID')"
            }
            catch {
                throw "Unable to delete the update audience. $($_.Exception.Message)"
            }
        }
        else {
            return
        }
    }
    end {
        return "Successfully deleted the Update Audience with ID $audienceID"
    }
}
#EndRegion './Public/Remove-DeploymentAudience.ps1' 43
#Region './Public/Remove-DeploymentAudienceMember.ps1' 0
function Remove-DeploymentAudienceMember {
    <#
    .SYNOPSIS
        Remove members from a deployment audience for Windows Updates for Business
    .DESCRIPTION
        This function will check if the deployments audiences have the devices as members, and if so they will be removed from the audience.
    .EXAMPLE
        Remove-DeploymentAudienceMember -azureDeviceIDs ("ID1","ID2") -updateAudienceID <AudienceID>
    .PARAMETER azureDeviceIDs
        The Azure Device IDs to add to the audience.
    .PARAMETER policyID
        The Update Policy ID to get the audience from.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [array]
        $azureDeviceIDs,
        # The Update Audience ID
        [Parameter(Mandatory = $true)]
        [string]
        $policyID
    )
    begin {
        # Create the param body base
        $paramBody = @{
            removeMembers = @(
            )
        }
    }
    process {
        if ($PSCmdlet.ShouldProcess("Remove members from the deployment audience")) {
            Write-Verbose "Removing members from the deployment audience"
        }
        else {
            return
        }
        $updateAudienceID = (Get-DriverUpdatePolicy -policyID $policyID).audience.id
        $updateAudienceMembers = Get-DeploymentAudienceMember -policyID $policyID
        foreach ($id in $azureDeviceIDs) {
            IF ($updateAudienceMembers.id -contains $id) {
                $memberObject = @{
                    "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                    id            = $id
                }
                $paramBody.removeMembers += $memberObject
            }
        }
    }
    end {
        IF ($paramBody.removeMembers.Count -ge 1) {
            Invoke-MgGraphRequest `
                -Method Post `
                -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$updateAudienceID')/updateAudience" `
                -Body $paramBody
        }
    }
}
#EndRegion './Public/Remove-DeploymentAudienceMember.ps1' 59
#Region './Public/Remove-DriverUpdatePolicy.ps1' 0
function Remove-DriverUpdatePolicy {
    <#
    .SYNOPSIS
        Deletes a Driver Update Policy for Windows Updates for Business.
    .DESCRIPTION
        This function will delete a Driver Update Policy for Windows Updates for Business.
    .EXAMPLE
        Remove-DriverUpdatePolicy -policyID 00000000-0000-0000-0000-000000000000
    .PARAMETER policyID
        The Update Policy ID to delete.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([string])]
    param (
        # The Update Audience ID
        [Parameter(Mandatory = $true)]
        [string]
        $policyID
    )
    process {
        if ($PSCmdlet.ShouldProcess("Deletes the Driver Update Policy with ID $policyID",
            "Delete Driver Update Policy",
            "Do you want to delete the Driver Update Policy with ID $policyID?"
            )
        ) {
            try {
                #Get the Update Audience ID
                $audienceID = (Get-DriverUpdatePolicy -policyID $policyID).audience.id
                #Remove the deployment audience
                Remove-DeploymentAudience -audienceID $audienceID
                #Remove the update policy
                Invoke-MgGraphRequest `
                    -Method DELETE `
                    -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies('$policyID')"
            }
            catch {
                throw "Unable to delete the update policy. $($_.Exception.Message)"
            }
        }
        else {
            return
        }
    }
    end {
        return "Successfully deleted the Driver Update Policy with ID $policyID"
    }
}
#EndRegion './Public/Remove-DriverUpdatePolicy.ps1' 48
#Region './Public/Revoke-DriverUpdateApproval.ps1' 0
function Revoke-DriverUpdateApproval {
    <#
    .SYNOPSIS
        Revoke a driver update approval from a policy for Windows Updates for Business.
    .DESCRIPTION
        This function will revoke a driver update approval from a policy for Windows Updates for Business.

        This function will find the compliance change ID for the update and revoke it based on the catalogEntry ID.
    .EXAMPLE
        Revoke-DriverUpdateApproval -policyIDs ("ID1","ID2") -catalogEntryID <catalogEntryID>
    .PARAMETER policyIDs
        The policy IDs to revoke the approval from.
    .PARAMETER catalogEntryID
        The update catalog entry ID.
    .PARAMETER return
        Return the response from the API.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType("System.Array", ParameterSetName = "return")]
    param (
        [Parameter(Mandatory = $true)]
        [array]
        $policyIDs,
        # The catalog entry ID, use Get-DriverUpdatePolicyApplicableContent to get the ID.
        [Parameter(Mandatory = $true)]
        [string]
        $catalogEntryID,
        [Parameter(ParameterSetName = "return", Mandatory = $false, dontShow = $true)]
        [array]
        $return = @()
    )
    begin {
        # Create the param body base
        $paramBody = @{
            "@odata.type" = "#microsoft.graph.windowsUpdates.contentApproval"
            isRevoked = $true
        }
    }
    process {
        if ($PSCmdlet.ShouldProcess("Revokes a driver update approval from a policy for Windows Updates for Business",
            "Revokes a driver update approval from a policy for Windows Updates for Business",
            "Do you want to revoke the driver update approval from a policy for Windows Updates for Business?"
            )
        ) {
            try {
                foreach ($policyID in $policyIDs)
                {
                    Write-Verbose "Getting the compliance change ID for the update for policy $policyID"
                    $policyComplianceChanges = Get-DriverUpdatePolicyComplianceChange -policyID $policyID -catalogEntryID $catalogEntryID
                    foreach ($complianceChange in $policyComplianceChanges)
                    {
                        if ($complianceChange.isRevoked) {
                            Write-Verbose "The driver update approval for policy $policyID is already revoked"
                            continue
                        } else {
                            Write-Verbose "Revoking the driver update approval for policy $policyID"
                            $response = Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies('$policyID')/complianceChanges/$($complianceChange.id)" -Body $paramBody -ContentType "application/json"
                            Write-Verbose "Successfully revoked the driver update approval for policy $policyID, with compliance change ID $($complianceChange.id)"
                            $return += $response# Action when all if and elseif conditions are false #>
                        }
                    }
                }
            }
            catch {
                throw "Unable to revoke the driver update approval for policy $policyID. Error: $($_.Exception.Message)"
            }
        }
        else {
            return
        }
    }
    end {
        return $return
    }
}
#EndRegion './Public/Revoke-DriverUpdateApproval.ps1' 76
#Region './Public/Update-DriverUpdatePatchDeferral.ps1' 0
function Update-DriverUpdatePatchDeferral {
    <#
    .SYNOPSIS
        This function is to be used to update the Patch Deferral on Update Policies.
    .DESCRIPTION
        This function is to be used to update the Patch Deferral on Update Policies.
    .NOTES
        This has only been tested for the commercial driver and firmware updates.
    .EXAMPLE
        Update-DriverUpdatePatchDeferral -policyID <id> -deferralTime P1D
        Explanation of the function or its result. You can include multiple The deferral time must be in the ISO8601 format.
    .PARAMETER policyID
        The Update Policy ID. You can get this from the Get-DriverUpdatePolicy function.
    .PARAMETER deferralTime
        The deferral time must be in the ISO8601 format.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        # The Update Policy ID
        [Parameter(Mandatory = $true)]
        [string]
        $policyID,
        # ISO8601 Timeformat for Deferral
        [Parameter(Mandatory = $true)]
        [string]
        $deferralTime
    )
    begin {
        #The Base Object for the post Body
        $paramBody = @{
            "@odata.type"         = "#microsoft.graph.windowsUpdates.updatePolicy"
            complianceChangeRules = @()
        }
        # Create the param body base
        $complianceChangeRules = (Get-DriverUpdatePolicy -policyID $policyID).complianceChangeRules
    }
    process {
        $paramBody.complianceChangeRules += $complianceChangeRules
        $paramBody.complianceChangeRules | foreach-object {
            $_.durationBeforeDeploymentStart = $deferralTime
        }
        $deferralTime | Out-Null #Adding in this line to stop the output of the deferral time
    }
    end {
        Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatePolicies/$policyID" -Method PATCH -Body $paramBody
    }
}
#EndRegion './Public/Update-DriverUpdatePatchDeferral.ps1' 48