WindowsAutoPilotIntune.psm1

####################################################

#region Initialization code

#$m = Get-Module -Name Microsoft.Graph.Intune -ListAvailable
#if (-not $m)
#{
# Install-Module NuGet -Force
# Install-Module Microsoft.Graph.Intune
#}
#Import-Module Microsoft.Graph.Intune -Global

#endregion

####################################################

#region Core methods

Function Get-AutoPilotDevice(){
<#
.SYNOPSIS
Gets devices currently registered with Windows Autopilot.
 
.DESCRIPTION
The Get-AutoPilotDevice cmdlet retrieves either the full list of devices registered with Windows Autopilot for the current Azure AD tenant, or a specific device if the ID of the device is specified.
 
.PARAMETER id
Optionally specifies the ID (GUID) for a specific Windows Autopilot device (which is typically returned after importing a new device)
 
.PARAMETER serial
Optionally specifies the serial number of the specific Windows Autopilot device to retrieve
 
.PARAMETER expand
Expand the properties of the device to include the Autopilot profile information
 
.EXAMPLE
Get a list of all devices registered with Windows Autopilot
 
Get-AutoPilotDevice
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$True)] $id,
        [Parameter(Mandatory=$false)] $serial,
        [Parameter(Mandatory=$false)] [Switch]$expand = $false
    )

    Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeviceIdentities"
    
        if ($id -and $expand) {
            $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)/$($id)?`$expand=deploymentProfile,intendedDeploymentProfile"
        }
        elseif ($id) {
            $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)/$id"
        }
        elseif ($serial) {
            $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)?`$filter=contains(serialNumber,'$serial')"
        }
        else {
            $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        }
        try {
            $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
            if ($id) {
                $response
            }
            else {
                $devices = $response.value
                $devicesNextLink = $response."@odata.nextLink"
    
                while ($devicesNextLink -ne $null){
                    $devicesResponse = (Invoke-MSGraphRequest -Url $devicesNextLink -HttpMethod Get)
                    $devicesNextLink = $devicesResponse."@odata.nextLink"
                    $devices += $devicesResponse.value
                }
    
                if ($expand) {
                    $devices | Get-AutopilotDevice -Expand
                }
                else
                {
                    $devices
                }
            }
        }
        catch {
            Write-Error $_.Exception 
            break
        }
    }
}


Function Set-AutoPilotDevice(){
<#
.SYNOPSIS
Updates settings on an Autopilot device.
 
.DESCRIPTION
The Set-AutoPilotDevice cmdlet can be used to change the updatable properties on a Windows Autopilot device object.
 
.PARAMETER id
The Windows Autopilot device id (mandatory).
 
.PARAMETER userPrincipalName
The user principal name.
 
.PARAMETER addressibleUserName
The name to display during Windows Autopilot enrollment. If specified, the userPrincipalName must also be specified.
 
.PARAMETER displayName
The name (computer name) to be assigned to the device when it is deployed via Windows Autopilot. This is presently only supported with Azure AD Join scenarios. Note that names should not exceed 15 characters. After setting the name, you need to initiate a sync (Invoke-AutopilotSync) in order to see the name in the Intune object.
 
.PARAMETER groupTag
The group tag value to set for the device.
 
.EXAMPLE
Assign a user and a name to display during enrollment to a Windows Autopilot device.
 
Set-AutoPilotDevice -id $id -userPrincipalName $userPrincipalName -addressableUserName "John Doe" -displayName "CONTOSO-0001" -groupTag "Testing"
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id,
        [Parameter(ParameterSetName = "Prop")] $userPrincipalName = $null,
        [Parameter(ParameterSetName = "Prop")] $addressableUserName = $null,
        [Parameter(ParameterSetName = "Prop")][Alias("ComputerName","CN","MachineName")] $displayName = $null,
        [Parameter(ParameterSetName = "Prop")] $groupTag = $null
    )

    Process {
    
        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeviceIdentities"
    
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/UpdateDeviceProperties"

        $json = "{"
        if ($userPrincipalName)
        {
            $json = $json + " userPrincipalName: `"$userPrincipalName`","
        }
        if ($addressableUserName)
        {
            $json = $json + " addressableUserName: `"$addressableUserName`","
        }
        if ($displayName)
        {
            $json = $json + " displayName: `"$displayName`","
        }
        if ($groupTag)
        {
            $json = $json + " groupTag: `"$groupTag`""
        }
        else
        {
            $json = $json.Trim(",")
        }
        $json = $json + " }"

        try {
            Invoke-MSGraphRequest -Url $uri -HttpMethod POST -Content $json
        }
        catch {
            Write-Error $_.Exception 
            break
        }
    }
}

    
Function Remove-AutoPilotDevice(){
<#
.SYNOPSIS
Removes a specific device currently registered with Windows Autopilot.
 
.DESCRIPTION
The Remove-AutoPilotDevice cmdlet removes the specified device, identified by its ID, from the list of devices registered with Windows Autopilot for the current Azure AD tenant.
 
.PARAMETER id
Specifies the ID (GUID) for a specific Windows Autopilot device
 
.EXAMPLE
Remove all Windows Autopilot devices from the current Azure AD tenant
 
Get-AutoPilotDevice | Remove-AutoPilotDevice
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id
    )

  Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeviceIdentities"    
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"

        try {
            Write-Verbose "Removing device $id"
            Invoke-MSGraphRequest -Url $uri -HttpMethod DELETE
        }
        catch {
            Write-Error $_.Exception 
            break
        }
    }        
}


Function Get-AutoPilotImportedDevice(){
<#
.SYNOPSIS
Gets information about devices being imported into Windows Autopilot.
 
.DESCRIPTION
The Get-AutoPilotImportedDevice cmdlet retrieves either the full list of devices being imported into Windows Autopilot for the current Azure AD tenant, or information for a specific device if the ID of the device is specified. Once the import is complete, the information instance is expected to be deleted.
 
.PARAMETER id
Optionally specifies the ID (GUID) for a specific Windows Autopilot device being imported.
 
.EXAMPLE
Get a list of all devices being imported into Windows Autopilot for the current Azure AD tenant.
 
Get-AutoPilotImportedDevice
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$false)] $id
)

    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/importedWindowsAutopilotDeviceIdentities"

    if ($id) {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
    }
    else {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    }
    try {
        $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
        if ($id) {
            $response
        }
        else {
            $devices = $response.value
    
            $devicesNextLink = $response."@odata.nextLink"
    
            while ($devicesNextLink -ne $null){
                $devicesResponse = (Invoke-MSGraphRequest -Url $devicesNextLink -HttpMethod Get)
                $devicesNextLink = $devicesResponse."@odata.nextLink"
                $devices += $devicesResponse.value
            }
    
            $devices
        }
    }
    catch {
            Write-Error $_.Exception 
            break
    }

}


<#
.SYNOPSIS
Adds a new device to Windows Autopilot.
 
.DESCRIPTION
The Add-AutoPilotImportedDevice cmdlet adds the specified device to Windows Autopilot for the current Azure AD tenant. Note that a status object is returned when this cmdlet completes; the actual import process is performed as a background batch process by the Microsoft Intune service.
 
.PARAMETER serialNumber
The hardware serial number of the device being added (mandatory).
 
.PARAMETER hardwareIdentifier
The hardware hash (4K string) that uniquely identifies the device.
 
.PARAMETER groupTag
An optional identifier or tag that can be associated with this device, useful for grouping devices using Azure AD dynamic groups.
 
.EXAMPLE
Add a new device to Windows Autopilot for the current Azure AD tenant.
 
Add-AutoPilotImportedDevice -serialNumber $serial -hardwareIdentifier $hash -groupTag "Kiosk"
#>

Function Add-AutoPilotImportedDevice(){
    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true)] $serialNumber,
        [Parameter(Mandatory=$true)] $hardwareIdentifier,
        [Parameter(Mandatory=$false)] [Alias("orderIdentifier")] $groupTag = ""
    )
    
        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/importedWindowsAutopilotDeviceIdentities"
    
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
        $json = @"
{
    "@odata.type": "#microsoft.graph.importedWindowsAutopilotDeviceIdentity",
    "orderIdentifier": "$groupTag",
    "serialNumber": "$serialNumber",
    "productKey": "",
    "hardwareIdentifier": "$hardwareIdentifier",
    "state": {
        "@odata.type": "microsoft.graph.importedWindowsAutopilotDeviceIdentityState",
        "deviceImportStatus": "pending",
        "deviceRegistrationId": "",
        "deviceErrorCode": 0,
        "deviceErrorName": ""
        }
}
"@


        try {
            Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json
        }
        catch {
            Write-Error $_.Exception 
            break
        }
    
}

    
Function Remove-AutoPilotImportedDevice(){
<#
.SYNOPSIS
Removes the status information for a device being imported into Windows Autopilot.
 
.DESCRIPTION
The Remove-AutoPilotImportedDevice cmdlet cleans up the status information about a new device being imported into Windows Autopilot. This should be done regardless of whether the import was successful or not.
 
.PARAMETER id
The ID (GUID) of the imported device status information to be removed (mandatory).
 
.EXAMPLE
Remove the status information for a specified device.
 
Remove-AutoPilotImportedDevice -id $id
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id
    )

    Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/importedWindowsAutopilotDeviceIdentities"    
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"

        try {
            Write-Verbose "Removing imported device $id"
            Invoke-MSGraphRequest -Url $uri -HttpMethod DELETE
        }
        catch {
            Write-Error $_.Exception 
            break
        }

    }
        
}


Function Get-AutoPilotProfile(){
<#
.SYNOPSIS
Gets Windows Autopilot profile details.
 
.DESCRIPTION
The Get-AutoPilotProfile cmdlet returns either a list of all Windows Autopilot profiles for the current Azure AD tenant, or information for the specific profile specified by its ID.
 
.PARAMETER id
Optionally, the ID (GUID) of the profile to be retrieved.
 
.EXAMPLE
Get a list of all Windows Autopilot profiles.
 
Get-AutoPilotProfile
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$false)] $id
)

    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"

    if ($id) {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
    }
    else {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    }
    try {
        $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
        if ($id) {
            $response
        }
        else {
            $devices = $response.value
    
            $devicesNextLink = $response."@odata.nextLink"
    
            while ($devicesNextLink -ne $null){
                $devicesResponse = (Invoke-MSGraphRequest -Url $devicesNextLink -HttpMethod Get)
                $devicesNextLink = $devicesResponse."@odata.nextLink"
                $devices += $devicesResponse.value
            }
    
            $devices
        }
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Get-AutoPilotProfileAssignedDevice(){
<#
.SYNOPSIS
Gets the list of devices that are assigned to the specified Windows Autopilot profile.
 
.DESCRIPTION
The Get-AutoPilotProfileAssignedDevice cmdlet returns the list of Autopilot devices that have been assigned the specified Windows Autopilot profile.
 
.PARAMETER id
The ID (GUID) of the profile to be retrieved.
 
.EXAMPLE
Get a list of all Windows Autopilot profiles.
 
Get-AutoPilotProfileAssignedDevices -id $id
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$True)] $id
)

    Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"

        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assignedDevices"
        try {
            $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
            $response.Value
        }
        catch {
            Write-Error $_.Exception 
            break
        }
    }
}



Function ConvertTo-AutoPilotConfigurationJSON(){
<#
.SYNOPSIS
Converts the specified Windows Autopilot profile into a JSON format.
 
.DESCRIPTION
The ConvertTo-AutoPilotConfigurationJSON cmdlet converts the specified Windows Autopilot profile, as represented by a Microsoft Graph API object, into a JSON format.
 
.PARAMETER profile
A Windows Autopilot profile object, typically returned by Get-AutoPilotProfile
 
.EXAMPLE
Get the JSON representation of each Windows Autopilot profile in the current Azure AD tenant.
 
Get-AutoPilotProfile | ConvertTo-AutoPilotConfigurationJSON
#>

[cmdletbinding()]
param
(
        [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
        [Object[]] $profile
)

  Process {

    $oobeSettings = $_.outOfBoxExperienceSettings

    # Build up properties
    $json = @{}
    $json.Add("Comment_File", "Profile $($_.displayName)")
    $json.Add("Version", 2049)
    $json.Add("ZtdCorrelationId", $_.id)
    if ($_."@odata.type" -eq "#microsoft.graph.activeDirectoryWindowsAutopilotDeploymentProfile")
    {
        $json.Add("CloudAssignedDomainJoinMethod", 1)
    }
    else
    {
        $json.Add("CloudAssignedDomainJoinMethod", 0)
    }
    if ($_.deviceNameTemplate)
    {
        $json.Add("CloudAssignedDeviceName", $_.deviceNameTemplate)
    }

    # Figure out config value
    $oobeConfig = 8 + 256
    if ($oobeSettings.userType -eq 'standard') {
        $oobeConfig += 2
    }
    if ($oobeSettings.hidePrivacySettings -eq $true) {
        $oobeConfig += 4
    }
    if ($oobeSettings.hideEULA -eq $true) {
        $oobeConfig += 16
    }
    if ($oobeSettings.skipKeyboardSelectionPage -eq $true) {
        $oobeConfig += 1024
    if ($_.language) {
            $json.Add("CloudAssignedLanguage", $_.language)
        }
    }
    if ($oobeSettings.deviceUsageType -eq 'shared') {
        $oobeConfig += 32 + 64
    }
    $json.Add("CloudAssignedOobeConfig", $oobeConfig)

    # Set the forced enrollment setting
    if ($oobeSettings.hideEscapeLink -eq $true) {
        $json.Add("CloudAssignedForcedEnrollment", 1)
    }
    else {
        $json.Add("CloudAssignedForcedEnrollment", 0)
    }

    # Set the org-related info
    $org = Get-Organization
    foreach ($domain in $org.VerifiedDomains) {
        if ($domain.isDefault) {
            $tenantDomain = $domain.name
        }
    }
    $json.Add("CloudAssignedTenantId", $org.id)
    $json.Add("CloudAssignedTenantDomain", $tenantDomain)
    $embedded = @{}
    $embedded.Add("CloudAssignedTenantDomain", $tenantDomain)
    $embedded.Add("CloudAssignedTenantUpn", "")
    if ($oobeSettings.hideEscapeLink -eq $true) {
        $embedded.Add("ForcedEnrollment", 1)
    }
    else
    {
        $embedded.Add("ForcedEnrollment", 0)
    }
    $ztc = @{}
    $ztc.Add("ZeroTouchConfig", $embedded)
    $json.Add("CloudAssignedAadServerData", (ConvertTo-JSON $ztc -Compress))

    # Return the JSON
    ConvertTo-JSON $json
  }

}


Function Set-AutoPilotProfile(){
<#
.SYNOPSIS
Sets Windows Autopilot profile properties.
.DESCRIPTION
The Set-AutoPilotProfile cmdlet sets properties on an existing Autopilot profile.
.PARAMETER id
Type: Integer - The ID (GUID) of the profile to be updated.
.PARAMETER language
Type: String - The language identifier (e.g. "en-us") to be configured in the profile.
.PARAMETER description
Type: String - The description to be configured in the profile.
.PARAMETER ConvertDeviceToAutopilot
Type: Boolean - Configure the value "Convert all targeted devices to Autopilot"
.PARAMETER OOBE_HideEULA
Type: Boolean - Configure the OOBE option to hide or not the EULA
.PARAMETER OOBE_EnableWhiteGlove
Type: Boolean - Configure the OOBE option to allow or not White Glove OOBE
.PARAMETER OOBE_hidePrivacySettings
Type: Boolean - Configure the OOBE option to hide or not the privacy settings
.PARAMETER OOBE_HideChangeAccountOpts
Type: Boolean - Configure the OOBE option to hide or not the change account options
.PARAMETER OOBE_userTypeAdmin
Type: Switch - Configure the user account type as administrator.
.PARAMETER OOBE_userTypeUser
Type: Switch - Configure the user account type as standard.
.PARAMETER OOBE_NameTemplate
Type: String - Configure the OOBE option to apply a device name template
.PARAMETER OOBE_SkipKeyboard
Type: String - Configure the OOBE option to skip or not the keyboard selection page
.EXAMPLE
Get a list of all Windows Autopilot profiles.
Set-AutoPilotProfile -ID <guid> -Language "en-us"
Set-AutoPilotProfile -ID <guid> -Language "en-us" -displayname "My testing profile" -Description "Description of my profile" -OOBE_HideEULA $True -OOBE_hidePrivacySettings $True
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id,
    [string]$language,
    [string]$displayname,
    [string]$description,
    [bool]$ConvertDeviceToAutopilot,
    [bool]$OOBE_HideEULA,    
    [bool]$OOBE_hidePrivacySettings,
    [bool]$OOBE_HideChangeAccountOpts,        
    [Switch]$OOBE_userTypeAdmin,
    [Switch]$OOBE_userTypeUser,        
    [string]$OOBE_NameTemplate,    
    [bool]$OOBE_SkipKeyboard,
    [bool]$OOBE_EnableWhiteGlove        
)

    # LIST EXISTING VALUES FOR THE SELECTING PROFILE
    # Default profile values
    $Profile_Values = Get-AutoPilotProfile -ID $id
    $Profile_DisplayName = $Profile_Values.displayName
    $Profile_Description = $Profile_Values.description
    $Profile_language = $Profile_Values.language
    $Profile_ConvertDeviceToAutopilot = $Profile_Values.extractHardwareHash
    $Profile_enableWhiteGlove = $Profile_Values.enableWhiteGlove
    $Profile_deviceType = $Profile_Values.deviceType
    $Profile_deviceNameTemplate = $Profile_Values.deviceNameTemplate
    
    # OOBE profile values
    $Profile_OOBE_NameTemplate = $Profile_Values.deviceNameTemplate    
    $Profile_OOBE_HideEULA = $Profile_Values.outOfBoxExperienceSettings.hideEULA
    $Profile_OOBE_hidePrivacySettings = $Profile_Values.outOfBoxExperienceSettings.hidePrivacySettings
    $Profile_OOBE_userTypeAdmin = $Profile_Values.outOfBoxExperienceSettings.userType    
    $Profile_OOBE_SkipKeyboard = $Profile_Values.outOfBoxExperienceSettings.skipKeyboardSelectionPage
    $Profile_OOBE_HideChangeAccountOpts = $Profile_Values.outOfBoxExperienceSettings.hideEscapeLink

    
    # If user has selected admin mode
    If($OOBE_userTypeAdmin)
        {        
            $OOBE_userType = "administrator"
        }
        
    If($OOBE_userTypeUser)
        {        
            $OOBE_userType = "standard"
        }        
        
    If(($OOBE_userTypeAdmin) -and ($OOBE_userTypeUser))     
        {
            write-warning "Please select OOBE_userTypeAdmin OR OOBE_userTypeUser, not both !!!"
            break
        }            
        
    If((!($OOBE_userTypeAdmin)) -and (!($OOBE_userTypeUser)))     
        {
            $OOBE_userType = $Profile_OOBE_userTypeAdmin
        }            

    If(($displayname -eq "")) # If user hasn't typed a display name
        {
            $displayname = $Profile_DisplayName # We will used the existing value
        }
    Else # If user has typed a display name
        {
            $displayname = $displayname # We will use the typed display name
        }

    If(($OOBE_NameTemplate -eq $null))
        {
            $OOBE_NameTemplate = $Profile_deviceNameTemplate
        }
    ElseIf(($OOBE_NameTemplate -eq ""))
        {
            $OOBE_NameTemplate = ""        
        }        
        
    If(($language -eq ""))
        {
            $language = $Profile_language
        }

    If(($description -eq ""))
        {
            $description = $Profile_Description
        }

    If(($ConvertDeviceToAutopilot -eq $null))
        {
            $ConvertDeviceToAutopilot = $Profile_ConvertDeviceToAutopilot
        }
        
    If(($OOBE_HideEULA -eq $null))
        {
            $OOBE_HideEULA = $Profile_OOBE_HideEULA
        }

    If(($OOBE_hidePrivacySettings -eq $null))
        {
            $OOBE_hidePrivacySettings = $Profile_OOBE_hidePrivacySettings
        }
    
    If(($OOBE_SkipKeyboard -eq ""))
        {
            $OOBE_SkipKeyboard = $Profile_OOBE_SkipKeyboard
        }    

    If(($OOBE_HideChangeAccountOpts -eq $null))
        {
            $OOBE_HideChangeAccountOpts = $Profile_OOBE_HideChangeAccountOpts
        }            
        
    If(($OOBE_EnableWhiteGlove -eq $null))
        {
            $OOBE_EnableWhiteGlove = $OOBE_EnableWhiteGlove
        }        

    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
    $json = @"
{
    "@odata.type": "#microsoft.graph.azureADWindowsAutopilotDeploymentProfile",
    "displayName": "$displayname",
    "description": "$description",
    "language": "$language",
    "extractHardwareHash": "$ConvertDeviceToAutopilot",
    "deviceNameTemplate": "$OOBE_NameTemplate",
    "deviceType": "$Profile_deviceType",
    "enableWhiteGlove": "$OOBE_EnableWhiteGlove",
    "outOfBoxExperienceSettings": {
        "@odata.type": "microsoft.graph.outOfBoxExperienceSettings",
        "hidePrivacySettings": "$OOBE_hidePrivacySettings",
        "hideEULA": $OOBE_HideEULA,
        "userType": "$OOBE_userType",
        "deviceUsageType": "singleUser",
        "skipKeyboardSelectionPage": "$OOBE_SkipKeyboard",
        "hideEscapeLink": $OOBE_HideChangeAccountOpts
    }
}
"@


    try {
        Invoke-MSGraphRequest -Url $uri -HttpMethod PATCH -Content $json
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Add-AutoPilotProfile(){
<#
.SYNOPSIS
Sets Windows Autopilot profile properties.
.DESCRIPTION
The Add-AutoPilotProfile cmdlet sets properties on an existing Autopilot profile.
.PARAMETER id
The ID (GUID) of the profile to be updated.
.PARAMETER language
Type: String - The language identifier (e.g. "en-us") to be configured in the profile.
.PARAMETER description
Type: String - The description to be configured in the profile.
.PARAMETER ConvertDeviceToAutopilot
Type: Boolean - Configure the value "Convert all targeted devices to Autopilot"
.PARAMETER OOBE_HideEULA
Type: Boolean - Configure the OOBE option to hide or not the EULA
.PARAMETER OOBE_EnableWhiteGlove
Type: Boolean - Configure the OOBE option to allow or not White Glove OOBE
.PARAMETER OOBE_hidePrivacySettings
Type: Boolean - Configure the OOBE option to hide or not the privacy settings
.PARAMETER OOBE_HideChangeAccountOpts
Type: Boolean - Configure the OOBE option to hide or not the change account options
.PARAMETER OOBE_userTypeAdmin
Type: Switch - Configure the user account type as administrator.
.PARAMETER OOBE_userTypeUser
Type: Switch - Configure the user account type as standard.
.PARAMETER ModeUserDriven
Type: Switch - Configure the deployment mode to user driven
.PARAMETER ModeSelfDeploying
Type: Switch - Configure the deployment mode to self deploying
.PARAMETER OOBE_NameTemplate
Type: String - Configure the OOBE option to apply a device name template
.PARAMETER OOBE_SkipKeyboard
Type: Boolean - Configure the OOBE option to skip or not the keyboard selection page
.EXAMPLE
Get a list of all Windows Autopilot profiles.
Add-AutoPilotProfile -Language "en-us" -displayname "My testing profile" -Description "Description of my profile" -OOBE_HideEULA $True -OOBE_hidePrivacySettings $True
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)][string]$displayname,
    [string]$language,
    [string]$description,
    [bool]$ConvertDeviceToAutopilot,
    [bool]$OOBE_HideEULA,
    [bool]$OOBE_EnableWhiteGlove,            
    [bool]$OOBE_hidePrivacySettings,
    [bool]$OOBE_HideChangeAccountOpts,        
    [Switch]$ModeUserDriven,
    [Switch]$ModeSelfDeploying,    
    [Switch]$OOBE_userTypeAdmin,
    [Switch]$OOBE_userTypeUser,        
    [string]$OOBE_NameTemplate
)

    # If user has selected admin mode
    If($OOBE_userTypeAdmin)
        {        
            $OOBE_userType = "administrator"
        }
        
    If($OOBE_userTypeUser)
        {        
            $OOBE_userType = "standard"
        }        
        
    If(($OOBE_userTypeAdmin) -and ($OOBE_userTypeUser))     
        {
            write-warning "Please select OOBE_userTypeAdmin OR OOBE_userTypeUser, not both !!!"
            break
        }            

    If((!($OOBE_userTypeAdmin)) -and (!($OOBE_userTypeUser)))     
        {
            $OOBE_userType = "standard"
        }        

    If($ModeUserDriven)
        {        
            $Deployment_Mode = "singleUser"
        }
        
    If($OOBE_EnableWhiteGlove)
        {        
            $OOBE_HideChangeAccountOpts = $True
        }        
        
    If($ModeSelfDeploying)
        {        
            $Deployment_Mode = "Shared"
        }        
        
    If(($ModeUserDriven) -and ($ModeSelfDeploying))     
        {
            write-warning "Please select ModeUserDriven OR ModeSelfDeploying, not both !!!"
            break
        }            

    If((!($ModeUserDriven)) -and (!($ModeSelfDeploying)))     
        {
            $Deployment_Mode = "singleUser"
        }        
        

    If(($displayname -eq "")) # If user hasn't typed a display name
        {
            $displayname = $Profile_DisplayName # We will used the existing value
        }
    Else # If user has typed a display name
        {
            $displayname = $displayname # We will use the typed display name
        }

        
    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    $json = @"
{
    "@odata.type": "#microsoft.graph.azureADWindowsAutopilotDeploymentProfile",
    "displayName": "$displayname",
    "description": "$description",
    "language": "$language",
    "extractHardwareHash": $ConvertDeviceToAutopilot,
    "deviceNameTemplate": "$OOBE_NameTemplate",
    "deviceType": "windowsPc",
    "enableWhiteGlove": $OOBE_EnableWhiteGlove,
    "outOfBoxExperienceSettings": {
        "hidePrivacySettings": $OOBE_hidePrivacySettings,
        "hideEULA": $OOBE_HideEULA,
        "userType": "$OOBE_userType",
        "deviceUsageType": "$Deployment_Mode",
        "skipKeyboardSelectionPage": false,
        "hideEscapeLink": $OOBE_HideChangeAccountOpts
    }
}
"@


    $json = $json -replace "True", "true"
    try {
        Invoke-MSGraphRequest -Url $uri -HttpMethod POST -Content $json
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Remove-AutoPilotProfile(){
<#
.SYNOPSIS
Remove a Deployment Profile
.DESCRIPTION
The Remove-AutoPilotProfile allows you to remove a specific deployment profile
.PARAMETER id
Mandatory, the ID (GUID) of the profile to be removed.
.EXAMPLE
Remove-AutoPilotProfile -id $id
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$True,ValueFromPipelineByPropertyName=$True)] $id
)

    Process {
        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"

        Try 
        {
            Invoke-MSGraphRequest -Url $uri -HttpMethod DELETE
        }
        catch 
        {
            Write-Error $_.Exception 
            break
        }
    }
}


Function Get-AutoPilotProfileAssignments(){
<#
.SYNOPSIS
List all assigned devices for a specific profile ID
.DESCRIPTION
The Get-AutoPilotProfileAssignments cmdlet returns the list of groups that ae assigned to a spcific deployment profile
.PARAMETER id
Type: Integer - Mandatory, the ID (GUID) of the profile to be retrieved.
.EXAMPLE
Get-AutoPilotProfileAssignments -id $id
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id
)

    Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assignments"
        try {
            $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
            $Group_ID = $response.Value.target.groupId
            ForEach($Group in $Group_ID)
            {
                Try {
                    Get-AzureADGroup | where {$_.ObjectId -like $Group}
                }
                Catch {
                    $Group
                }            
            }
        }
        catch {
            Write-Error $_.Exception 
            break
        }

    }

}


Function Remove-AutoPilotProfileAssignments(){
<#
.SYNOPSIS
Removes a specific group assigntion for a specifc deployment profile
.DESCRIPTION
The Remove-AutoPilotProfileAssignments cmdlet allows you to remove a group assignation for a deployment profile
.PARAMETER id
Type: Integer - Mandatory, the ID (GUID) of the profile
.PARAMETER groupid
Type: Integer - Mandatory, the ID of the group
.EXAMPLE
Remove-AutoPilotProfileAssignments -id $id
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true)]$id,
    [Parameter(Mandatory=$true)]$groupid
)
    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
    
    $full_assignment_id = $id + "_" + $groupid + "_0"

    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assignments/$full_assignment_id"

    try {
        Invoke-MSGraphRequest -Url $uri -HttpMethod DELETE
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Set-AutoPilotProfileAssignedGroup(){
<#
.SYNOPSIS
Assigns a group to a Windows Autopilot profile.
.DESCRIPTION
The Set-AutoPilotProfileAssignedGroup cmdlet allows you to assign a specific group to a specific deployment profile
.PARAMETER id
Type: Integer - Mandatory, the ID (GUID) of the profile
.PARAMETER groupid
Type: Integer - Mandatory, the ID of the group
.EXAMPLE
Set-AutoPilotProfileAssignedGroup -id $id -groupid $groupid
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true)]$id,
        [Parameter(Mandatory=$true)]$groupid
    )
        $full_assignment_id = $id + "_" + $groupid + "_0"  
  
        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"        
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assignments"        

$json = @"
{
    "id": "$full_assignment_id",
    "target": {
        "@odata.type": "#microsoft.graph.groupAssignmentTarget",
        "groupId": "$groupid"
    }
}
"@


        try {
            Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json
        }
        catch {
            Write-Error $_.Exception 
            break
        }
}


Function Get-EnrollmentStatusPage(){
<#
.SYNOPSIS
List enrollment status page
.DESCRIPTION
The Get-EnrollmentStatusPage cmdlet returns available enrollment status page with their options
.PARAMETER id
The ID (GUID) of the status page (optional)
.EXAMPLE
Get-EnrollmentStatusPage
#>


[cmdletbinding()]
param
(
    [Parameter()] $id
)

    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/deviceEnrollmentConfigurations"

    if ($id) {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
    }
    else {
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    }
    try {
        $response = Invoke-MSGraphRequest -Url $uri -HttpMethod Get
        if ($id) {
            $response
        }
        else {
            $response.Value | ? { $_.'@odata.type' -eq "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration" }
        }
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Add-EnrollmentStatusPage(){
<#
.SYNOPSIS
Adds a new Windows Autopilot Enrollment Status Page.
.DESCRIPTION
The Add-EnrollmentStatusPage cmdlet sets properties on an existing Autopilot profile.
.PARAMETER DisplayName
Type: String - Configure the display name of the enrollment status page
.PARAMETER description
Type: String - Configure the description of the enrollment status page
.PARAMETER HideProgress
Type: Boolean - Configure the option: Show app and profile installation progress
.PARAMETER AllowCollectLogs
Type: Boolean - Configure the option: Allow users to collect logs about installation errors
.PARAMETER Message
Type: String - Configure the option: Show custom message when an error occurs
.PARAMETER AllowUseOnFailure
Type: Boolean - Configure the option: Allow users to use device if installation error occurs
.PARAMETER AllowResetOnError
Type: Boolean - Configure the option: Allow users to reset device if installation error occurs
.PARAMETER BlockDeviceUntilComplete
Type: Boolean - Configure the option: Block device use until all apps and profiles are installed
.PARAMETER TimeoutInMinutes
Type: Integer - Configure the option: Show error when installation takes longer than specified number of minutes
.EXAMPLE
Add-EnrollmentStatusPage -Message "Oops an error occured, please contact your support" -HideProgress $True -AllowResetOnError $True
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$True)][string]$DisplayName,
    [string]$Description,        
    [bool]$HideProgress,    
    [bool]$AllowCollectLogs,
    [bool]$blockDeviceSetupRetryByUser,    
    [string]$Message,    
    [bool]$AllowUseOnFailure,
    [bool]$AllowResetOnError,    
    [bool]$BlockDeviceUntilComplete,                
    [Int]$TimeoutInMinutes        
)

    If($HideProgress -eq $False)
        {
            $blockDeviceSetupRetryByUser = $true
        }

    If(($Description -eq $null))
        {
            $Description = $EnrollmentPage_Description
        }        

    If(($DisplayName -eq $null))
        {
            $DisplayName = ""
        }    

    If(($TimeoutInMinutes -eq ""))
        {
            $TimeoutInMinutes = "60"
        }                

    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/deviceEnrollmentConfigurations"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    $json = @"
{
    "@odata.type": "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration",
    "displayName": "$DisplayName",
    "description": "$description",
    "showInstallationProgress": "$hideprogress",
    "blockDeviceSetupRetryByUser": "$blockDeviceSetupRetryByUser",
    "allowDeviceResetOnInstallFailure": "$AllowResetOnError",
    "allowLogCollectionOnInstallFailure": "$AllowCollectLogs",
    "customErrorMessage": "$Message",
    "installProgressTimeoutInMinutes": "$TimeoutInMinutes",
    "allowDeviceUseOnInstallFailure": "$AllowUseOnFailure",
}
"@


    try {
        Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}


Function Set-EnrollmentStatusPage(){
<#
.SYNOPSIS
Sets Windows Autopilot Enrollment Status Page properties.
.DESCRIPTION
The Set-EnrollmentStatusPage cmdlet sets properties on an existing Autopilot profile.
.PARAMETER id
The ID (GUID) of the profile to be updated.
.PARAMETER DisplayName
Type: String - Configure the display name of the enrollment status page
.PARAMETER description
Type: String - Configure the description of the enrollment status page
.PARAMETER HideProgress
Type: Boolean - Configure the option: Show app and profile installation progress
.PARAMETER AllowCollectLogs
Type: Boolean - Configure the option: Allow users to collect logs about installation errors
.PARAMETER Message
Type: String - Configure the option: Show custom message when an error occurs
.PARAMETER AllowUseOnFailure
Type: Boolean - Configure the option: Allow users to use device if installation error occurs
.PARAMETER AllowResetOnError
Type: Boolean - Configure the option: Allow users to reset device if installation error occurs
.PARAMETER BlockDeviceUntilComplete
Type: Boolean - Configure the option: Block device use until all apps and profiles are installed
.PARAMETER TimeoutInMinutes
Type: Integer - Configure the option: Show error when installation takes longer than specified number of minutes
.EXAMPLE
Set-EnrollmentStatusPage -id $id -Message "Oops an error occured, please contact your support" -HideProgress $True -AllowResetOnError $True
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$True)] $id,
    [string]$DisplayName,    
    [string]$Description,        
    [bool]$HideProgress,
    [bool]$AllowCollectLogs,
    [string]$Message,    
    [bool]$AllowUseOnFailure,
    [bool]$AllowResetOnError,    
    [bool]$AllowUseOnError,    
    [bool]$BlockDeviceUntilComplete,                
    [Int]$TimeoutInMinutes        
)

    Process {

        # LIST EXISTING VALUES FOR THE SELECTING STAUS PAGE
        # Default profile values
        $EnrollmentPage_Values = Get-EnrollmentStatusPage -ID $id
        $EnrollmentPage_DisplayName = $EnrollmentPage_Values.displayName
        $EnrollmentPage_Description = $EnrollmentPage_Values.description
        $EnrollmentPage_showInstallationProgress = $EnrollmentPage_Values.showInstallationProgress
        $EnrollmentPage_blockDeviceSetupRetryByUser = $EnrollmentPage_Values.blockDeviceSetupRetryByUser
        $EnrollmentPage_allowDeviceResetOnInstallFailure = $EnrollmentPage_Values.allowDeviceResetOnInstallFailure
        $EnrollmentPage_allowLogCollectionOnInstallFailure = $EnrollmentPage_Values.allowLogCollectionOnInstallFailure
        $EnrollmentPage_customErrorMessage = $EnrollmentPage_Values.customErrorMessage
        $EnrollmentPage_installProgressTimeoutInMinutes = $EnrollmentPage_Values.installProgressTimeoutInMinutes
        $EnrollmentPage_allowDeviceUseOnInstallFailure = $EnrollmentPage_Values.allowDeviceUseOnInstallFailure

        If(!($HideProgress))
        {
            $HideProgress = $EnrollmentPage_showInstallationProgress
        }    
    
        If(!($BlockDeviceUntilComplete))    
        {
            $BlockDeviceUntilComplete = $EnrollmentPage_blockDeviceSetupRetryByUser
        }        
        
        If(!($AllowCollectLogs))    
        {
            $AllowCollectLogs = $EnrollmentPage_allowLogCollectionOnInstallFailure
        }            
    
        If(!($AllowUseOnFailure))    
        {
            $AllowUseOnFailure = $EnrollmentPage_allowDeviceUseOnInstallFailure
        }    

        If(($Message -eq ""))
        {
            $Message = $EnrollmentPage_customErrorMessage
        }        
        
        If(($Description -eq $null))
        {
            $Description = $EnrollmentPage_Description
        }        

        If(($DisplayName -eq $null))
        {
            $DisplayName = $EnrollmentPage_DisplayName
        }    

        If(!($AllowResetOnError))    
        {
            $AllowResetOnError = $EnrollmentPage_allowDeviceResetOnInstallFailure
        }    

        If(($TimeoutInMinutes -eq ""))
        {
            $TimeoutInMinutes = $EnrollmentPage_installProgressTimeoutInMinutes
        }                

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/deviceEnrollmentConfigurations"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
        $json = @"
{
    "@odata.type": "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration",
    "displayName": "$DisplayName",
    "description": "$description",
    "showInstallationProgress": "$HideProgress",
    "blockDeviceSetupRetryByUser": "$BlockDeviceUntilComplete",
    "allowDeviceResetOnInstallFailure": "$AllowResetOnError",
    "allowLogCollectionOnInstallFailure": "$AllowCollectLogs",
    "customErrorMessage": "$Message",
    "installProgressTimeoutInMinutes": "$TimeoutInMinutes",
    "allowDeviceUseOnInstallFailure": "$AllowUseOnFailure"
}
"@


        try {
            Invoke-MSGraphRequest -Url $uri -HttpMethod PATCH -Content $json
        }
        catch {
            Write-Error $_.Exception 
            break
        }

    }

}


Function Remove-EnrollmentStatusPage(){
<#
.SYNOPSIS
Remove a specific enrollment status page
.DESCRIPTION
The Remove-EnrollmentStatusPage allows you to remove a specific enrollment status page
.PARAMETER id
Mandatory, the ID (GUID) of the profile to be retrieved.
.EXAMPLE
Remove-EnrollmentStatusPage -id $id
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$True,ValueFromPipelineByPropertyName=$True)] $id
)

    Process {

        # Defining Variables
        $graphApiVersion = "beta"
        $Resource = "deviceManagement/deviceEnrollmentConfigurations"

        $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
        try {
            Invoke-MSGraphRequest -Url $uri -HttpMethod DELETE
        }
        catch {
            Write-Error $_.Exception 
            break
        }

    }

}


Function Invoke-AutopilotSync(){
<#
.SYNOPSIS
Initiates a synchronization of Windows Autopilot devices between the Autopilot deployment service and Intune.
 
.DESCRIPTION
The Invoke-AutopilotSync cmdlet initiates a synchronization between the Autopilot deployment service and Intune.
This can be done after importing new devices, to ensure that they appear in Intune in the list of registered
Autopilot devices. See https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/intune_enrollment_windowsautopilotsettings_sync
for more information.
 
.EXAMPLE
Initiate a synchronization.
 
Invoke-AutopilotSync
#>

[cmdletbinding()]
param
(
)
    # Defining Variables
    $graphApiVersion = "beta"
    $Resource = "deviceManagement/windowsAutopilotSettings/sync"

    $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
    try {
        Invoke-MSGraphRequest -Url $uri -HttpMethod Post
    }
    catch {
        Write-Error $_.Exception 
        break
    }

}

#endregion


Function Import-AutoPilotCSV(){
<#
.SYNOPSIS
Adds a batch of new devices into Windows Autopilot.
 
.DESCRIPTION
The Import-AutoPilotCSV cmdlet processes a list of new devices (contained in a CSV file) using a several of the other cmdlets included in this module. It is a convenient wrapper to handle the details. After the devices have been added, the cmdlet will continue to check the status of the import process. Once all devices have been processed (successfully or not) the cmdlet will complete. This can take several minutes, as the devices are processed by Intune as a background batch process.
 
.PARAMETER csvFile
The file containing the list of devices to be added.
 
.PARAMETER groupTag
An optional identifier or tag that can be associated with this device, useful for grouping devices using Azure AD dynamic groups. This value overrides an Group Tag value specified in the CSV file.
 
.EXAMPLE
Add a batch of devices to Windows Autopilot for the current Azure AD tenant.
 
Import-AutoPilotCSV -csvFile C:\Devices.csv
#>

    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true)] $csvFile,
        [Parameter(Mandatory=$false)] [Alias("orderIdentifier")] $groupTag = ""
    )
    
        # Read CSV and process each device
        $devices = Import-CSV $csvFile
        foreach ($device in $devices) {
            if ($groupTag -ne "")
            {
                $o = $groupTag
            }
            elseif ($device.'Group Tag' -ne "")
            {
                $o = $device.'Group Tag'
            }
            else
            {
                $o = $device.'OrderID'
            }
            Add-AutoPilotImportedDevice -serialNumber $device.'Device Serial Number' -hardwareIdentifier $device.'Hardware Hash' -groupTag $o
        }

        # While we could keep a list of all the IDs that we added and then check each one, it is
        # easier to just loop through all of them
        $processingCount = 1
        while ($processingCount -gt 0)
        {
            $deviceStatuses = @(Get-AutoPilotImportedDevice)
            $deviceCount = $deviceStatuses.Length

            # Check to see if any devices are still processing
            $processingCount = 0
            foreach ($device in $deviceStatuses){
                if ($device.state.deviceImportStatus -eq "unknown") {
                    $processingCount = $processingCount + 1
                }
            }
            Write-Host "Waiting for $processingCount of $deviceCount"

            # Still processing? Sleep before trying again.
            if ($processingCount -gt 0){
                Start-Sleep 2
            }
        }

        # Display the statuses
        $deviceStatuses | ForEach-Object {
            Write-Host "Serial number $($_.serialNumber): $($_.state.deviceImportStatus) $($_.state.deviceErrorCode) $($_.state.deviceErrorName)"
        }

        # Cleanup the imported device records
        $deviceStatuses | ForEach-Object {
            Remove-AutoPilotImportedDevice -id $_.id
        }
}