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
                }
                $Call = Invoke-MgGraphRequest @getRequestParameters -ErrorAction Stop
                IF (-Not($Call.Value))
                {
                    $return = $Call
                }
                else
                {
                    $return = $Call.Value
                }
            }
        }
    }
    end {
        return $return
    }
}
#EndRegion './Private/Invoke-GetRequest.ps1' 68
#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
        Write-Verbose "Creating the param body base."
        $paramBody = @{
            addMembers = @(
            )
        }
    }
    process {
        Write-Verbose "Checking the input parameters."
        IF (-Not([String]::IsNullOrEmpty($audienceID))) {
            Write-Verbose "Getting the members of the audience using audienceID."
            $updateAudienceMembers = Get-DeploymentAudienceMember -audienceID $audienceID
            Write-Verbose "Audience member count: $($updateAudienceMembers.Count)"
        }
        elseif (-not([String]::IsNullOrEmpty($policyID))) {
            Write-Verbose "Getting the members of the audience using policyID."
            $audienceID = (Get-DriverUpdatePolicy -policyID $policyID).audience.id
            Write-Verbose "Audience ID: $audienceID"
            $updateAudienceMembers = Get-DeploymentAudienceMember -policyID $policyID
            Write-Verbose "Audience member count: $($updateAudienceMembers.Count)"
        }
        Write-Verbose "Checking if the devices are already members of the audience."
        foreach ($id in $azureDeviceIDs) {
            IF (-Not($updateAudienceMembers.id -contains $id)) {
                $memberObject = @{
                    "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                    id            = $id
                }
                $paramBody.addMembers += $memberObject
            }
        }
    }
    end {
        Write-Verbose "Checking if there are any members to add."
        IF ($paramBody.addMembers.Count -ge 1) {
            Write-Verbose "Adding $($paramBody.addMembers.Count) members to the audience: $audienceID."
            Invoke-MgGraphRequest `
                -Method POST `
                -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$audienceID')/updateAudience" `
                -Body $paramBody
        }
    }
}
#EndRegion './Public/Add-DeploymentAudienceMember.ps1' 78
#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 {
        Write-Verbose "Creating the param body for the request."
        # 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 {
        Write-Verbose "Processing Count: $($policyIDs.count)"
        foreach ($policyID in $policyIDs) {
            Write-Verbose "Getting the applicable content for the policy ID: $policyID"
            $applicableConent = Get-DriverUpdatePolicyApplicableContent -policyID $policyID
            if ($applicableConent.catalogEntry.id -contains $catalogEntryID) {
                Write-Verbose "The catalog entry ID is applicable to the policy ID: $policyID"
                Write-Verbose "Adding the catalog entry ID with the defer days of $deferDays to the policy ID: $policyID"
                $startDate = (Get-Date).AddDays($deferDays).ToString("yyyy-MM-ddT00:00:00Z")
                try {
                    Write-Verbose "Adding the catalog entry ID to the policy ID: $policyID"
                    $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 {
        Write-Verbose "Returning the responce from the API."
        $return
    }
}
#EndRegion './Public/Add-DriverUpdateApproval.ps1' 90
#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)) {
                Write-Verbose "Getting all deployment audiences because no audienceID was specified."
                $DriverUpdateDeploymentAudience = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences" -All
            }
            elseif (-Not([string]::IsNullOrEmpty($audienceID))) {
                Write-Verbose "Getting the deployment audience for the audienceID '$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 {
        Write-Verbose "Returning the deployment audience(s)."
        return $DriverUpdateDeploymentAudience
    }
}
#EndRegion './Public/Get-DeploymentAudience.ps1' 41
#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 {
        Write-Verbose "Checking to make sure either the audienceID or policyID parameter was specified."
        if ([String]::IsNullOrEmpty($audienceID) -and ([String]::IsNullOrEmpty($policyID))) {
            throw "You must specify either the audienceID or policyID parameter."
        }
    }
    process {
        try {
            Write-Verbose "Getting the members of the deployment audience."
            if(-Not([String]::IsNullOrEmpty($audienceID))) {
                Write-Verbose "Using the audienceID parameter to get the members of the deployment audience."
                $members = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$($audienceID)')/members" -All
            }
            ELSEIF (-Not([String]::IsNullOrEmpty($policyID))) {
                Write-Verbose "Using the policyID parameter to get the members of the deployment audience."
                $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 {
        Write-Verbose "Returning the members of the deployment audience."
        return $members
    }
}
#EndRegion './Public/Get-DeploymentAudienceMember.ps1' 55
#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-IntuneDriverUpdatePolicy.ps1' 0
function Get-IntuneDriverUpdatePolicy{
    <#
.SYNOPSIS
    This function gets all update of the policies for Windows Updates for Business for Driver and Firmware Servicing from Intune.
.DESCRIPTION
    This function gets all update of the policies for Windows Updates for Business for Driver and Firmware Servicing from Intune
.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
    # This will return all policies
    Get-IntuneDriverUpdatePolicy -All

    # This will return a specific policy by ID
    Get-IntuneDriverUpdatePolicy -policyID <policyID>

    # This will return all policies that are called Test
    Get-IntuneDriverUpdatePolicy -PolicyName "Test"
.PARAMETER policyID
    The policy ID to get. If not specified, all policies will be returned.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $policyID,
        [Parameter(Mandatory = $false)]
        [string]
        $PolicyName
    )
    process {
        try {
            if(([string]::IsNullOrEmpty($policyID)) -and ([string]::IsNullOrEmpty($PolicyName))){
                $policy = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles" -All
            } elseif (-Not([string]::IsNullOrEmpty($PolicyID))) {
                $policy = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles/$policyID"
            } elseif (-Not([string]::IsNullOrEmpty($PolicyName))) {
                $policies = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles"
                $policy = $policies | Where-Object {$_.displayName -eq $PolicyName}
            }
        }
        catch {
            throw "Unable to get the policies. $($_.Exception.Message)"
        }
    }
    end {
        return $policy
    }
}
#EndRegion './Public/Get-IntuneDriverUpdatePolicy.ps1' 50
#Region './Public/Get-IntuneDriverUpdatePolicyAssignments.ps1' 0
function Get-IntuneDriverUpdatePolicyAssignments {
    <#
.SYNOPSIS
    This function gets the assignments for a Driver Policy within Intune
.DESCRIPTION
    This function gets the assignments for a Driver Policy within Intune
.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

    You can get the group ID by using (Get-IntuneDriverUpdatePolicyAssignments -policyID <PolicyID>).id.split('_') | Where-Object {$_ -ne "<PolicyID"}
.EXAMPLE
    Get-IntuneDriverUpdatePolicyAssignments -policyID <PolicyID>
.PARAMETER policyID
    The policy ID to get the assignments for.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $policyID
    )
    process {
        try {
            $assignments = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles/$($PolicyID)/assignments"
        }
        catch {
            throw "Unable to get the policies. $($_.Exception.Message)"
        }
    }
    end {
        return $assignments
    }
}
#EndRegion './Public/Get-IntuneDriverUpdatePolicyAssignments.ps1' 35
#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 -EntraIDDeviceID <AzureADDeviceID>
    .PARAMETER AzureADDeviceID
        The Azure AD Device ID to get the updatable asset for.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]
        $EntraIDDeviceID
    )
    process {
        try {
            IF ([string]::IsNullOrEmpty($EntraIDDeviceID)) {
                $updatableAsset = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets" -All
            }
            elseif (-Not([string]::IsNullOrEmpty($EntraIDDeviceID))) {
                $updatableAsset = Invoke-GetRequest -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets/$EntraIDDeviceID"
            }
        }
        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/New-IntuneDriverUpdatePolicy.ps1' 0
function New-IntuneDriverUpdatePolicy
{
    <#
    .SYNOPSIS
        This function is to be used to create a Driver Policy within your intune tenant.
    .DESCRIPTION
        Based on the inputs, will depend on the policy creation type of Manual or Automatic.
    .EXAMPLE
        New-IntuneDriverUpdatePolicy -policyType Automatic -deferralDays 1 -policyName "MyDriverUpdates" -policyDescription "My Graph Posted Policy"
    .PARAMETER policyType
        The type of policy to create. Manual or Automatic.
    .PARAMETER deferralDays
        The deferral days for the policy. This is only required for Automatic policies.
    .PARAMETER policyName
        The name of the policy to create.
    .PARAMETER policyDescription
        The description of the policy to create.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $policyName,
        [Parameter(Mandatory = $false)]
        [string]
        $policyDescription = $null,
        # Manual or Automatic Publishing Policy
        [Parameter(Mandatory = $true)]
        [ValidateSet("Manual", "Automatic")]
        [string]
        $policyType,
        [Parameter()]
        [string]
        $deferralDays = "0"
    )
    begin
    {
        #Initialise the base object body.
        $paramBody = @{
            displayName = $policyName
            description = $policyDescription
        }
    }
    process
    {
        switch ($policyType)
        {
            Manual
            {
                $paramBody.approvalType = "manual"
            }
            Automatic
            {
                $paramBody.approvalType = "automatic"
                $paramBody.deploymentDeferralInDays = $deferralDays
            }
        }
    }
    end
    {
        if ($PSCmdlet.ShouldProcess(
                'Creating a Intune Driver Update Policy',
                'Warning: Creating a Intune Driver Update Policy',
                'Question: Are you sure you want to do continue?'))
        {
            Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles" `
                -Method POST `
                -Body $paramBody `
                -ContentType 'application/json'
        }
    }
}
#EndRegion './Public/New-IntuneDriverUpdatePolicy.ps1' 73
#Region './Public/New-IntuneDriverUpdatePolicyAssignment.ps1' 0
function New-IntuneDriverUpdatePolicyAssignment {
    <#
    .SYNOPSIS
        This function is to be used to add groups to a Driver Policy within Intune
    .DESCRIPTION
        This function is to be used to add groups to a Driver Policy within Intune
    .EXAMPLE
        New-IntuneDriverUpdatePolicyAssignment -assignmentGroupIDs '<Groupid1>','<Groupid2>' -policyID $policyID
    .PARAMETER assignmentGroupIDs
        The Entra ID Object ID of the group to add to the policy
    .PARAMETER PolicyID
        The ID of the policy to update
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        # The Deployment Audience ID
        [Parameter(Mandatory = $true)]
        [array]
        $assignmentGroupIDs,
        [Parameter(Mandatory = $true, HelpMessage="The ID of the policy to update")]
        [string]
        $policyID
    )
    begin {
        #Initialise the base object body.
        $parambody = @{
            assignments = @(
            )
        }
    }
    process {
        $currentAssignments = (Get-IntuneDriverUpdatePolicyAssignments -policyID $policyID)
        IF ($currentAssignments) {
            Write-Verbose "Found existing assignments"
            $currentAssignments | Select-Object -Property Target | % {
                $target = @{
                    target = @{
                        "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
                        groupId       = $_.target.groupId
                    }
                }
                $paramBody.assignments += $target
            }#Add current assignments to the param body as it would remove them if not
        } ELSE {
            Write-Verbose "No existing assignments found"
        }
        $currentAssignmentIds = (Get-IntuneDriverUpdatePolicyAssignments -policyID $policyID).id.split('_') | Where-Object {$_ -ne $policyID}
        FOREACH ($id in $assignmentGroupIDs) {
            if ($currentAssignmentIds -notcontains $id) {
                Write-Verbose "Adding $id to the param body, as it is not already enrolled"
                $target = @{
                    target = @{
                        "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
                        groupId       = $id
                    }
                }
                $paramBody.assignments += $target
            } ELSE {
                Write-Verbose "Skipping $id as it is already a member of the policy"
            }
        }
    }
    end {
        if ($PSCmdlet.ShouldProcess(
                'Creating Intune Policy Assingments',
                'Warning: Creating Intune Policy Assingments',
                'Question: Are you sure you want to do continue?')) {
            IF ($paramBody.assignments.count -gt 0) {
                Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles/$policyID/assign"`
                    -Method POST `
                    -Body $paramBody `
                    -ContentType 'application/json'
            }
        }
    }
}
#EndRegion './Public/New-IntuneDriverUpdatePolicyAssignment.ps1' 77
#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 {
        Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)"
        # Create the param body base
        Write-Verbose "Creating the param body base"
        $paramBody = @{
            updateCategory = "driver"
            assets         = @(
            )
        }
    }
    process {
        if($PSCmdlet.ShouldProcess("Enroll Assets", "Enroll Assets")) {
            Write-Verbose "Getting updatable assets"
            $updatableAssets = Get-UpdatableAsset
            Write-Verbose "Checking if the assets are already enrolled"
            foreach ($id in $azureDeviceIDs) {
                IF (-Not($updatableAssets | Where-Object { $_.id -match $id }).enrollments.updateCategory -notcontains "driver") {
                    Write-Verbose "Adding $id to the param body, as it is not already enrolled"
                    $memberObject = @{
                        "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
                        id            = $id
                    }
                    $paramBody.assets += $memberObject
                }
                Write-Verbose "Param Body has $($paramBody.assets.Count) assets"
            }
        }
    }
    end {
        IF ($paramBody.assets.Count -ge 1) {
            Invoke-MgGraphRequest `
                -Method POST `
                -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets/enrollAssets" `
                -Body $paramBody
        }
        Write-Verbose "Finished function $($MyInvocation.MyCommand.Name)"
    }
}
#EndRegion './Public/Push-EnrollUpdatableAsset.ps1' 60
#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