Demo-AutopilotDevicePreparationPolicy.ps1


<#PSScriptInfo
 
.VERSION 1.0.0.0
 
.GUID a9526506-99e7-4c01-98a7-f5fbcb240a70
 
.AUTHOR Frits van Drie
 
.COMPANYNAME 3-Link Opleidingen (3-link.nl)
 
.COPYRIGHT (c) 2023 3-Link bv (NL). Anyone is free to use and distribute this module freely without modification. If you like this or experience failures, please notify me
 
.TAGS Demoscript
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES PowerShellGet, Microsoft.Graph.apAuthentication
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES 1.0.0.0 (2024-09-30) Initial release
 
.PRIVATEDATA
 
#>


<#
    .SYNOPSIS
        Full setup for demonstrating Autopilot 'Device Preparation Policies'
 
    .DESCRIPTION
        'Device Preparation Policies' demonstration. This script will setup all needed components.
 
    .PARAMETER CsvPath
        The full path to the csv-file for importing Corporate Identifiers.
        To extract Corporate Identifiers (Manufacturer,Model,SerialNumber) from your Windows computer using PowerShell:
        Set-Content -Path $CsvPath -Encoding UTF8 -Value ("{0},{1},{2}" -f
                (Get-CimInstance -Class Win32_ComputerSystem).Manufacturer,
                (Get-CimInstance -Class Win32_ComputerSystem).Model,
                (Get-CimInstance -Class Win32_BIOS).SerialNumber
        )
         
    .PARAMETER TenantDnsName
        The FQDN of the Entra tenant
 
    .PARAMETER CsvPath
        Full path to a Csv-file containing 'Corporate Identifier(s)' (optional)
 
 
    .LINK
 
    .NOTES
        Author : Frits van Drie (f.vandrie)
        Company : 3-Link Opleidingen (3-Link.nl)
 
        This script is developed for training and demonstration only. Do not run this script in a production environment.
        A perfect way to run this script is to open it in your favorite PowerShell editor and run each region separate in sequence.
 
        Prerequisites for this demo:
            . A valid subscription for 'EMS Enterprise E3 or E5'
            . A test computer/VM for registration with Autopilot in OOBE phase. Windows 11 23H2 or later, at least 4GB memory and 4 (v)CPU's
            . Access to internet for all computers
            . PowerShell 5.1, 7+
 
        Requirements for the client computer to provision:
            . Windows 11, version 23H2 with KB5035942 or later – Windows installation media dated April 2024 or later has KB5035942 included.
            . Windows 11, version 22H2 with KB5035942 or later – Windows installation media dated April 2024 or later has KB5035942 included.
 
 
        This script will create or install:
            . Install some required PowerShell modules
            . Create a local Folder with scripts and csv-files
            . Create an Entra User account
            . Assign an EMSPremium license to the demo user
            . Create an Entra Dynamic Usergroup containing all users with 'EMSPremium' license
            . Create an Entra Devicegroup
            . Create an Entra Service Principal for 'Intune Provisioning Client' with User logon enabled.
            . Add 'Intune Provioning Client' as an owner of the devicegroup
            . Import some demo 'Corporate Identifiers' in Intune
            . Add 'Office 365' to Intune MAM if not already available
            . Upload some PowerShell scripts for demo
            . Create an Autopilot 'Device preparation policy' in Intune
            . If this script is run multiple times, most creations from previous runs will be removed and recreated.
 
 
#>
                   

    [CmdletBinding()]

    param (
        [Parameter(Mandatory=$false)]
        $TenantDnsName = $TenantDnsName,

        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        $CsvPath = $CsvPath
    )

    cls

#region: Requirements

    $invocationInfo   = $myInvocation.InvocationName
    $currentUser      = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $windowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($currentUser)

    Write-Host "Requirements" -f Cyan
    Write-Host "`tScript path : $invocationInfo"
    Write-Host "`tCurrent user: $($currentUser.Name)"

    # Device not in Autopilot
    # Device OS
    # MDM Auto enrollment
    # Personal device enrollment allowed

#endregion


#region: Variables

    Do {
        if ($TenantDnsName -notmatch '^([a-zA-Z0-9]{2,}\.){2,}[a-zA-Z0-9]{2,}$') {
            $validTenantName = $false
        }
        else {
            try {
                $null = Resolve-DnsName $TenantDnsName -ErrorAction Stop
                $validTenantName = $true
            }
            catch {
                $validTenantName = $false
            }
        }
        if (-not $validTenantName) {
            Write-Host "`tNot a valid Tenant DNS name: $TenantDnsName" -f Red
            $TenantDnsName = Read-Host "`tTenantname DNS Name"
        }
    }
    Until ($validTenantName)

    $demoFolderName          = 'DemoIntuneDevicePreparation'
    $basePath                = "$env:USERPROFILE\Documents"
    $demoFolderPath          = "$basePath\$demoFolderName"

    $userGivenName           = 'Ademo'
    $userSurName             = 'Youser'
    $userDisplayName         = "$userGivenName $userSurName"
    $userUpn                 = "$userGivenName@$tenantDnsName"
    $userPassPlain           = 'DemoY0user'

    $dppName                 = 'Demo Device Prep Policy'
    $dppDescription          = 'Autopilot Device preparation policy for demo'

    $userGroupName           = 'Autopilot Device Prep Users'

    $deviceGroupName         = 'Autopilot Device Prep Devices'
    $deviceGroupOwnerName    = "Intune Provisioning Client"
    $deviceGroupOwnerAppId   = 'f1346770-5b25-470b-88bd-d5744ab7952c'

    $intuneProductId         = 'c1ec4a95-1f05-45b3-a911-aa3fa01094f5'
    $userGroupMembershipRule = "(user.assignedPlans -any (assignedPlan.servicePlanId -eq `"$intuneProductId`" -and assignedPlan.capabilityStatus -eq `"Enabled`"))"

    $filePath                = "$demoFolderPath\CorpIdentifiers"
    $IdTypes                 = "Windows","Serial","IMEI"
    $corporateIdentifiersWindows = @"
MICROSOFTCORPORATION,VIRTUALMACHINE,0667-1996-4296-7445-8157-7226-71
MICROSOFTCORPORATION,VIRTUALMACHINE,5554-8932-8110-6895-8157-8816-33
HP,Elitebook 640 G9,12234567890321
Lenovo,Thinkpad T14,02234567890123
Microsoft,Surface 5,56789012345678
"@

    if ($CsvPath) {
        $corporateIdentifiersWindows += "`n$(Get-content -Path $CsvPath -ErrorAction Stop)"
    }
    $corporateIdentifiersIMEI    = @"
123456789012345,iPad Linda
098765432109876,iPad Tom
"@

    $corporateIdentifiersSerial  = @"
RF81AB2RZP,Samsung A35 Betty
F9FC34DEFHK9,iPhone 16 Barney
RF67GK8DDA,Samsung Galaxy S24 Joe
"@


    $appName                 ='Microsoft 365 Apps for Windows 10 and later'

    $arrScripts              = @(
        @{
            'FileName'    = 'Set-LastExecute.ps1'
            'Name'        = 'User-Set-LastExecute'
            'Description' = 'This script creates a folder (Intune) in the User Documents folder with a file containing the time of execution'
            'Content'     = @"
New-Item -ItemType Directory -Path `"`$env:USERPROFILE\Documents`" -Name 'Intune' -Force
(Get-Date -Format 'yyyy-MM-dd_HH:mm:ss') | Out-File -FilePath `"`$env:USERPROFILE\Documents\Intune\LastExecuted.txt`" -Append
"@

            'Scope'       = 'User'
            'Assignments' = @("$deviceGroupName")
        },
        @{
            'FileName'    = 'Test-DppOSVersionRequirement.ps1'
            'Name'        = 'Test-DppOSVersionRequirement'
            'Description' = 'This script will display a popup with OS version at user logon'
            'Content'     = @"
`$osVersion = ([Environment]::OSVersion).version
[version]`$reqOsVersion = '10.0.22631.0'
`$wshell = New-Object -ComObject Wscript.Shell
if (`$osVersion -lt `$reqOsVersion) {
    `$Output = `$wshell.Popup("This computer might not meet the OS requirements for Intune 'Device Preparation Policies'!`nRequired version: `$reqOsVersion`nCurrent version: `$osVersion",0,'Warning',48 + 4096)
}
else {
    `$Output = `$wshell.Popup("Current OS version: `$osVersion",0,'Warning',48 + 4096)
}
"@

            'Scope'       = 'User'
            'Assignments' = @("$deviceGroupName")
        }

    )

#endregion

#region: Functions

    Write-Host 'Functions' -f Cyan

    Write-Host "`tConvertToBase64{}"
    Function ConvertToBase64 {

        [CmdletBinding()]

        param (
            [parameter(Mandatory=$true)]
            $Path
        )

        Write-Debug "Filepath: $Path"
        $content = Get-Content -Path $Path -Raw
        $byte_array = [System.Text.Encoding]::UTF8.GetBytes($content)
        $base64 = [System.Convert]::ToBase64String($byte_array)
        Write-Output $base64

    }

    Write-Host "`tNewConfigurationPolicyAssignment{}"
    Function NewConfigurationPolicyAssignment {

        [CmdletBinding()]

        param (
            [parameter(Mandatory=$true)]
            $configurationPolicyId,

            [parameter(Mandatory=$true)]
            $target
            
        )

        $jsonTarget = $target | ConvertTo-Json

        $jsonBody = @"
{
    "assignments": [
        {
            "id": "",
            "source": "direct",
            "target": $jsonTarget
        }
    ]
}
"@


        Write-Verbose "`t`tAssigning group"
        $uri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$ConfigurationPolicyId/assign"
        try {
            $null = (Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop).value
            Write-Verbose 'success'
        }
        catch {
            Write-Verbose 'failed'
            throw $_
        }

        Write-Verbose "`tGet Current Assignments"
        
        $uri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$ConfigurationPolicyId/assignments"
        try {
            $assignments = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
            Write-Verbose 'success'
        }
        catch {
            Write-Verbose 'failed'
            throw $_
        }

        foreach ($item in $assignments.target) {
            $odataType = $item.'@odata.type'
            if ($odataType -like '*exclusionGroupAssignmentTarget') {
                Write-Verbose "`t`tExcluded:"
            }
            else {
                Write-Verbose "`t`tIncluded:"
            }

            if ($item.groupId) {
                
                $uri = "https://graph.microsoft.com/beta/groups/$($item.groupId)"
                $grpDisplayName = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).displayName
                Write-Verbose "`t`t$grpDisplayName [$($item.groupId)]"
            }
            else {
                Write-Verbose "`t`t$($item.'@odata.type')"
            }

        }

    }

#endregion

#region: Packages

    Write-Host "Setup: Packages" -f Cyan

    # Package provider
    $packageProviderName = 'NuGet'
    $ppMinimalVersion = '2.8.5.201'
    Write-Host "`tPackage provider"
    Write-Host "`t`tProvider Name : $packageProviderName"
    Write-Host "`t`tMinimal Version: $ppMinimalVersion " -NoNewline
    try {
        $packageProvider = Get-PackageProvider -Name $packageproviderName -ForceBootstrap -ErrorAction Stop | Where Version -ge $minimalVersion | Sort Version
        if ($packageProvider) {
            Write-Host 'present' -f Green
            Write-Host "`t`tCurrent version: $($packageProvider.Version)"

        }
        else {
            Write-Host "failed" -f Red
            
        }
    }
    catch {
        throw $_
    }

    $repoName = 'PSGallery'
    try {
        Write-Host "`tGet Repository"
        Write-Host "`t`tName : $repoName " -NoNewline
        $repo = Get-PSRepository -Name $repoName -ErrorAction Stop
        Write-Host "present" -f Green
        if ($repo.InstallationPolicy -ne 'Trusted') {
            $color = 'Red'
        }
        else {
            $color = 'Green'
        }
        Write-Host "`t`tPolicy: " -NoNewline
        Write-Host "$($repo.InstallationPolicy)" -f $color
    }
    catch {
        Write-Host "failed" -f Red

        try {
            Write-Host "`tRegister Repository"
            Write-Host "`t`tName : $repoName " -NoNewline
            $repo = Register-PSRepository -Default -ErrorAction Stop
            Write-Host 'success' -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

    }

    if ($repo.InstallationPolicy -ne 'Trusted') {
        try {
            Write-Host "`tTrust Repository"
            Write-Host "`t`tName : $repoName"
            Set-PSRepository -Name $repoName -InstallationPolicy Trusted -ErrorAction Stop
            $repo = Get-PSRepository -Name $repoName -ErrorAction Stop
            Write-Host "`t`tPolicy: " -NoNewline
            Write-Host "$($repo.InstallationPolicy)" -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

    }
    
#endregion

#region: Modules

    Write-Host "Setup: Modules" -f Cyan

    $modules = @(
        'PowerShellGet',
        'Microsoft.Graph.Authentication'
    )

    Write-Host "`tImport Module"
    $missingModule = $false

    foreach ($module in $modules) {
        try {
            Write-Host "`t`t$module " -NoNewline
            Import-Module -Name $module -ErrorAction Stop
            Write-Host "present" -f Green
        }
        catch {
            Write-Host "installing " -NoNewline -f yellow
            try {
                Install-Module $module -Repository PSGallery -Confirm:$false -Force -ErrorAction Stop
                Import-Module -Name $module -ErrorAction Stop
                Write-Host "success" -f Green
            }
            catch {
                Write-Host "failed" -f Red
                $missingModule = $true
            }
        }
    }

    if ($missingModule) {
        throw 'Missing one or more required modules'
    }

#endregion

#region: Connections

    Write-Host "Setup: Connections" -f Cyan

    Write-Host "`tMicrosoft Graph"

    $scopes = @(
        'Application.ReadWrite.All',
        'Device.ReadWrite.All',
        'DeviceManagementApps.ReadWrite.All',
        'DeviceManagementConfiguration.ReadWrite.All',
        'DeviceManagementManagedDevices.ReadWrite.All',
        'DeviceManagementServiceConfig.ReadWrite.All',
        'Directory.Read.All',
        'Group.ReadWrite.All',
        'Organization.ReadWrite.All',
        'Policy.ReadWrite.MobilityManagement',
        'User.ReadWrite.All'

    )

    try {
        Write-Host "`t`tscopes: $($scopes -join "`n`t ")"
        Write-Host "`t`tState : " -NoNewline
        Connect-MgGraph -Scopes $scopes -NoWelcome -ErrorAction Stop
        Write-Host "Connected" -f Green
        $tenantId = (Get-MgContext).tenantId

    }
    catch {
        Write-Host "failed" -f Red
        Write-Error $_
    }


#endregion

#region: Overview

    Write-Host "Overview" -f Cyan

    Write-Host "`tScript : $PSCommandPath" #$($myInvocation.InvocationName)"
    Write-Host "`tTenant Name : $tenantDnsName"
    Write-Host "`tDemo User : $userUpn [Pass: $userPassPlain]"
    Write-Host "`tPolicy name : $dppName"
    Write-Host "`tUser group : $userGroupName"
    Write-Host "`tDevice group : $deviceGroupName"
    Write-Host "`tLocal Folder : $demoFolderPath"
    Write-Host "`tCsv specified : $(!!$csvPath)"
    Write-Host "`tMgGraph : $(if (Get-MgContext) {'Connected'} else {'Not connected'})"

#endregion

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

#region: [Local ] Folders and Files

    Write-Host "Folders and Files" -f Cyan

    Write-Host "`tCreate '$demoFolderPath' " -NoNewline
    try {
        $folder = New-Item -Path $basePath -Name $demoFolderName -ItemType Directory -Force -ErrorAction Stop
        Write-Host 'created' -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }


    Write-Host "`tCreate demo CSV-file:"
    foreach ($idType in $IdTypes) {
        $corpIdPath = "$filePath$idType.csv"
        Write-Host "`t`t$corpIdPath " -NoNewline
        try {
            Get-Variable "corporateIdentifiers$IdType" -ValueOnly| Set-Content -Path $corpIdPath -Encoding UTF8
            Write-Host 'created' -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }
    }


    Write-Host "`tCreate demo Script:"

    foreach ($item in $arrScripts) {

        $itemPath = "$demoFolderPath\$($item.fileName)"

        try {
            Write-Host "`t`t$itemPath " -NoNewline
            $item.content | Set-Content -Path $itemPath -ErrorAction Stop
            Write-Host 'created' -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

    }



#endregion

#region: [Entra ] User

    Write-Host "Entra User account" -f Cyan

    write-Host "`tGet User: $userDisplayName " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/users?`$filter=displayName eq '$userDisplayName'"
        if ($user =  (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value) {
            Write-Host "found" -f Green
            if ($user.accountEnabled -eq $false) {
                Write-Host "`tAccount is disabled" -f Red
                break
            }
        }
        else {
            Write-Host 'not found' -f Red
        }
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }

    if ( -not $user) {

        $jsonBody = @"
{
    "accountEnabled" : true,
    "givenName" : "$userGivenName",
    "surname" : "$userSurName",
    "displayName" : "$userDisplayName",
    "userPrincipalName": "$userUpn",
    "mailNickname" : "$($userDisplayName.replace(' ',''))",
    "passwordProfile" : {
        "forceChangePasswordNextSignIn": false,
        "password" : "$userPassPlain"
    },
    "ageGroup" : "Adult",
    "department" : "Learning",
    "usageLocation" : "NL"
}
"@


        Write-Host "`tCreate user: $userDisplayName " -NoNewline
        try {
            $uri  = 'https://graph.microsoft.com/beta/users'
            $user = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
            write-Host "[$($user.id)]`tsuccess" -f Green
        }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }

    }

#endregion

#region: [Entra ] Licenses

    Write-Host "Licenses" -f Cyan

    $requiredSubscriptions = 'EMSPREMIUM'

    Write-Host "`tGet subscribed products " -NoNewline
    $uri  = "https://graph.microsoft.com/beta/subscribedSkus"
    try {
        $subscribedSku = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        Write-Host "found $($subscribedSku.count)" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }


    Write-Host "`tGet user licenses"

    Write-Host "`t`tUser: $userDisplayName " -NoNewline

    $uri = "https://graph.microsoft.com/beta/users/$($user.id)/licenseDetails"
    try {
        $userLicenses = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        Write-Host "found $($userLicenses.count)" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }


    foreach ($product in $requiredSubscriptions) {
        Write-Host "`t`tSku : $product " -NoNewline
        if ($product -in $userLicenses.skuPartNumber) {
            Write-Host 'licensed' -f Green
            continue
        }

        Write-Host 'missing license' -f Red

        Write-Host "`t`tLicenses available: "-NoNewline
        $sku = $subscribedSku | Where SkuPartNumber -eq $product
        if ( $sku) {
            $unitsAvailable = ($sku.prepaidUnits.enabled) - ($sku.consumedUnits)
            Write-Host $unitsAvailable
            if ($unitsAvailable -eq 0) {
                throw "No license available for product: $product"
            }
        }
        else {
            Write-Host "`t`tProduct not found" -f Red
            throw "No subscription found for product: $product"
        }

        Write-Host "`tAssign license " -NoNewline

        $jsonBody = @"
{
    "addLicenses": [
        {
            "disabledPlans": [],
            "skuId" : "$($sku.skuId)"
        }
    ],
    "removeLicenses": []
}
"@

        try {
            $uri  = "https://graph.microsoft.com/beta/users/$($user.id)/assignLicense"
            $userLicense = (Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ContentType 'application/json' -ErrorAction Stop).value
            Write-Host "success" -f Green
        }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }


    } # foreach product


#endregion

#region: [Entra ] Service Principal

    Write-Host "Service Principal" -f Cyan

    write-Host "`tGet ServicePrincipal"
    Write-Host "`t`tName : $deviceGroupOwnerName"
    Write-Host "`t`tAppid : $deviceGroupOwnerAppId " -NoNewline
     
    try {
        Connect-MgGraph -Scopes 'Directory.Read.All' -NoWelcome
        $uri = "https://graph.microsoft.com/beta/servicePrincipals?`$filter=appId eq '$deviceGroupOwnerAppId'"
        $deviceGroupOwner = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        if ($deviceGroupOwner.count -gt 0) {
            Write-Host "found" -f Green
            Write-Host "`t`tEnabled: " -NoNewline
            if ($deviceGroupOwner.accountEnabled) {
                Write-Host "true" -f Green
            }
            else {
                Write-Host "false" -f Red
            }
        }
        else {
            Write-Host 'not found' -f Red
        }
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }

    if ($deviceGroupOwner.count -eq 0) {
        Write-Host "`tRegister required Service Principal"
        Write-Host "`t`tName : $deviceGroupOwnerName"
        Write-Host "`t`tAppId: $deviceGroupOwnerAppId" -NoNewline

        $jsonBody = @"
{
    "appid": "$deviceGroupOwnerAppId",
    "accountEnabled": "true"
}
"@

        try {
            $uri = "https://graph.microsoft.com/beta/servicePrincipals/"
            $null = (Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop)

            Write-Host "success" -f Green
        }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }
    }
    elseIf (-not $deviceGroupOwner.accountEnabled) {
        Write-Host "`tEnable User logon for required Service Principal"
        Write-Host "`t`tName : $($deviceGroupOwner.displayName)"
        Write-Host "`t`tAppId: $($deviceGroupOwner.appId)"

        $jsonBody = @"
{
    "appid": "$deviceGroupOwnerAppId",
    "accountEnabled": "true"
}
"@

        try {
            $uri = "https://graph.microsoft.com/beta/servicePrincipals/$($deviceGroupOwner.Id)"
            $null = (Invoke-MgGraphRequest -Method PATCH -Uri $uri -Body $jsonBody -ErrorAction Stop)

            Write-Host "success" -f Green
        }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }
    }

#endregion

#region: [Entra ] Device Group

    Write-Host "Entra Device group" -f Cyan

    write-Host "`tGet group: $deviceGroupName " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$deviceGroupName'"
        [array]$entraGroups = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value

        Write-Host "found $($entraGroups.count)" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }

    foreach ($group in $entraGroups) {
        write-Host "`tRemove group: $($group.displayName) [$($group.id)] " -NoNewline
        try {
            $uri = "https://graph.microsoft.com/beta/groups/$($group.id)"
            $return = Invoke-MgGraphRequest -Method DELETE -Uri $uri -ErrorAction Stop
            Write-Host "removed" -f Green
       }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }

    }

    Write-Host "`tCreate group: $deviceGroupName " -NoNewline
    try {
        $jsonBody = @"
{
    "DisplayName" : "$deviceGroupName",
    "MailEnabled" : false,
    "MailNickName" : "$($deviceGroupName.Replace(' ',''))",
    "SecurityEnabled": true
}
"@
 #| ConvertFrom-Json
        $uri      = 'https://graph.microsoft.com/beta/groups'

        $deviceGroup = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
        write-Host "[$($deviceGroup.id)] " -NoNewline
        Write-Host "created" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }


    # Add Owner
    $jsonBody = @"
{
    "requests": [
        {
            "id": "owner_$($deviceGroupOwner.id)",
            "method": "POST",
            "url": "/groups/$($devicegroup.Id)/owners/`$ref",
            "headers": {
                "Content-Type": "application/json"
            },
            "body": {
                "@odata.id": "https://graph.microsoft.com/beta/directoryObjects/$($deviceGroupOwner.id)"
            }
        }
    ]
}
"@


    write-Host "`tAdd Owner : $deviceGroupOwnerName [$($deviceGroupOwner.id)] " -NoNewline
    try {
        $uri = "https://graph.microsoft.com/beta/`$batch"
        $null = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
        Write-Host "success" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }


#endregion

#region: [Entra ] Users Group

    Write-Host "Entra Users group" -f Cyan

    write-Host "`tGet group: $userGroupName " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$userGroupName'"
        [array]$entraGroups =  (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value

        Write-Host "found $($entraGroups.count)" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }

    foreach ($group in $entraGroups) {
        write-Host "`tRemove group: $($group.displayName) [$($group.id)] " -NoNewline
        try {
            $uri = "https://graph.microsoft.com/beta/groups/$($group.id)"
            $return = Invoke-MgGraphRequest -Method DELETE -Uri $uri -ErrorAction Stop
            Write-Host "removed" -f Green
       }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }

    }

    Write-Host "`tCreate group: $userGroupName " -NoNewline
    try {
        $jsonBody = @"
{
    "DisplayName" : "$userGroupName",
    "MailEnabled" : false,
    "MailNickName" : "$($userGroupName.Replace(' ',''))",
    "GroupTypes" :
        [
                "DynamicMembership"
        ],
    "MembershipRule" : "$($userGroupMembershipRule.Replace('"','\"'))",
    "MembershipRuleProcessingState": "On",
    "SecurityEnabled": true
}
"@
 #| ConvertFrom-Json
        $uri       = 'https://graph.microsoft.com/beta/groups'
        $userGroup = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
        write-Host "[$($userGroup.id)] " -NoNewline
        write-host "created" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }




#endregion

#region: [Entra ] MDM auto enrollment

    Write-Host "Entra ID Automatic MDM enrollment" -f Cyan

    $addGroupName = $userGroupName

    write-Host "`tGroup: $addGroupName " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$addGroupName'"
        [array]$arrGroups = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value

        if ($arrGroups.count -eq 0) {
            throw "Cannot find group: $addGroupName"
        }
        if ($arrGroups.count -gt 1) {
            throw "Multiple groups found with name: $addGroupName"
        }
        $objGroup = $arrGroups
        Write-Host "found" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }

    # Policy.ReadWrite.MobilityManagement
    Write-Host "`tCurrent MDM policy`t" -NoNewline
    try {
        $uri = 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups'
        $policy = Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop

    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }

    $mdmAppliesTo = $policy.appliesTo
    Write-Host $mdmAppliesTo -f Green

    foreach ($group in $policy.includedGroups) {
        Write-Host "`t`tGroup: " -NoNewline
        if ($group.id -eq $objGroup.Id) {
            Write-Host "$($group.displayName) " -NoNewline
            Write-Host "[$($group.id)]" -f Green
        }
        else {
            Write-Host "[$($group.id)] $($group.displayName)"
        }
    }

    if ( ($mdmAppliesTo -ne 'all') -and ($objGroup.Id -notin $policy.includedGroups.id) ) {

        Write-Host "`tUpdate current MDM policy"
        Write-Host "`t`tScope: Selected"
        Write-Host "`t`tGroup: [$($objGroup.id)] $($objgroup.displayName) " -NoNewline

        $jsonBody = @"
{
    "requests": [
        {
            "id": "1",
            "method": "POST",
            "url": "/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000/includedGroups/`$ref",
            "body": {
                "@odata.id": "https://graph.microsoft.com/odata/groups('$($objGroup.id)')"
            },
            "headers": {
                "x-ms-command-name": "MDMApplications - AddMdmGroup",
                "Content-Type": "application/json"
            }
        }
    ]
}
"@

        $retry = 0
        do {
            try {
                $uri = 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000/includedGroups/$ref'
                $uri = 'https://graph.microsoft.com/beta/$batch'
                $policy = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
                if ($policy.responses.status -ne 400) {
                    $retry++
                    if ($retry -gt 10) {
                        throw "Group not found: $addGroupName"
                    }
                    Write-Host '.' -NoNewline
                    sleep 5
                }
            }
            catch {
                Write-Host 'failed' -f Red
                throw $_

            }
        } Until ($policy.responses.status -eq 400)
        Write-Host 'added' -f Green

    }


#endregion

#region: [Intune] Applications

    # Get added Apps
    Write-Host "Applications" -f Cyan
    Write-Host "`tGet Intune Apps:"
    Write-Host "`t`t$appName " -NoNewline
    try {
        $uri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$filter=(microsoft.graph.managedApp/appAvailability eq null or microsoft.graph.managedApp/appAvailability eq 'lineOfBusiness' or isAssigned eq true)&`$orderby=displayName"
        $intuneApp = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop -OutputType PSObject).value | where displayName -eq $appName
        Write-Host "found $($intuneApp.count)" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }

    # Add App
    if ( $intuneApp.count -eq 0) {

        $jsonBody = @"
{
    "@odata.type": "#microsoft.graph.officeSuiteApp",
    "description": "Microsoft 365 Apps for Windows 10 and later",
    "developer": "Microsoft",
    "displayName": "Microsoft 365 Apps for Windows 10 and later",
    "informationUrl": "https://products.office.com/explore-office-for-home",
    "isFeatured": true,
    "roleScopeTagIds": [],
    "publisher": "Microsoft",
    "largeIcon": {
        "type": "image/png",
        "value": "iVBORw0KGgoAAAANSUhEUgAAAF0AAAAeCAMAAAEOZNKlAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJhUExURf////7z7/i9qfF1S/KCW/i+qv3q5P/9/PrQwfOMae1RG+s8AOxGDfBtQPWhhPvUx/759/zg1vWgg+9fLu5WIvKFX/rSxP728/nCr/FyR+tBBvOMaO1UH+1RHOs+AvSScP3u6f/+/v3s5vzg1+xFDO9kNPOOa/i7pvzj2/vWyes9Af76+Pzh2PrTxf/6+f7y7vOGYexHDv3t5+1SHfi8qPOIZPvb0O1NFuxDCe9hMPSVdPnFs/3q4/vaz/STcu5VIe5YJPWcfv718v/9/e1MFfF4T/F4TvF2TP3o4exECvF0SexIEPONavzn3/vZze1QGvF3Te5dK+5cKvrPwPrQwvKAWe1OGPexmexKEveulfezm/BxRfamiuxLE/apj/zf1e5YJfSXd/OHYv3r5feznPakiPze1P7x7f739f3w6+xJEfnEsvWdf/Wfge1LFPe1nu9iMvnDsfBqPOs/BPOIY/WZevJ/V/zl3fnIt/vTxuxHD+xEC+9mN+5ZJv749vBpO/KBWvBwRP/8+/SUc/etlPjArP/7+vOLZ/F7UvWae/708e1OF/aihvSWdvi8p+tABfSZefvVyPWihfSVde9lNvami+9jM/zi2fKEXvBuQvOKZvalifF5UPJ/WPSPbe9eLfrKuvvd0uxBB/7w7Pzj2vrRw/rOv+1PGfi/q/eymu5bKf3n4PnJuPBrPf3t6PWfgvWegOxCCO9nOO9oOfaskvSYePi5pPi2oPnGtO5eLPevlvKDXfrNvv739Pzd0/708O9gL+9lNfJ9VfrLu/OPbPnDsPBrPus+A/nArfarkQAAAGr5HKgAAADLdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AvuakogAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAz5JREFUOE+tVTtu4zAQHQjppmWzwIJbEVCzpTpjbxD3grQHSOXKRXgCAT6EC7UBVAmp3KwBnmvfzNCyZTmxgeTZJsXx43B+HBHRE34ZkXgkerXFTheeiCkRrbB4UXmp4wSWz5raaQEMTM5TZwuiXoaKgV+6FsmkZQcSy0kA71yMTMGHanX+AzMMGLAQCxU1F/ZwjULPugazl82GM0NEKm/U8EqFwEkO3/EAT4grgl0nucwlk9pcpTTJ4VPA4g/Rb3yIRhhp507e9nTQmZ1OS5RO4sS7nIRPEeHXCHdkw9ZEW2yVE5oIS7peD58Avs7CN+PVCmHh21oOqBdjDzIs+FldPJ74TFESUSJEfVzy9U/dhu+AuOT6eBp6gGKyXEx8euO450ZE4CMfstMFT44broWw/itkYErWXRx+fFArt9Ca9os78TFed0LVIUsmIHrwbwaw3BEOnOk94qVpQ6Ka2HjxewJnfyd6jUtGDQLdWlzmYNYLeKbbGOucJsNabCq1Yub0o92rtR+i30V2dapxYVEePXcOjeCKPnYyit7BtKeNlZqHbr+gt7i+AChWA9RsRs03pxTQc67ouWpxyESvjK5Vs3DVSy3IpkxPm5X+wZoBi+MFHWW69/w8FRhc7VBe6HAhMB2b8Q0XqDzTNZtXUMnKMjwKVaCrB/CSUL7WSx/HsdJC86lFGXwnioTeOMPjV+szlFvrZLA5VMVK4y+41l4e1xfx7Z88o4hkilRUH/qKqwNVlgDgpvYCpH3XwAy5eMCRnezIUxffVXoDql2rTHFDO+pjWnTWzAfrYXn6BFECblUpWGrvPZvBipETjS5ydM7tdXpH41ZCEbBNy/+wFZu71QO2t9pgT+iZEf657Q1vpN94PQNDxUHeKR103LV9nPVOtDikcNKO+2naCw7yKBhOe9Hm79pe8C4/CfC2wDjXnqC94kEeBU3WwN7dt/2UScXas7zDl5GpkY+M8WKv2J7fd4Ib2rGTk+jsC2cleEM7jI9veF7B0MBJrsZqfKd/81q9pR2NZfwJK2JzsmIT1Ns8jUH0UusQBpU8d2JzsHiXg1zXGLqxfitUNTDT/nUUeqDBp2HZVr+Ocqi/Ty3Rf4Jn82xxfSNtAAAAAElFTkSuQmCC"
    },
    "notes": "",
    "owner": "Microsoft",
    "privacyInformationUrl": "https://privacy.microsoft.com/privacystatement",
    "autoAcceptEula": true,
    "excludedApps": {
        "access": true,
        "oneNote": true,
        "powerPoint": true,
        "publisher": true,
        "lync": true,
        "teams": true,
        "word": true,
        "infoPath": true,
        "sharePointDesigner": true,
        "groove": true
    },
    "officePlatformArchitecture": "x64",
    "localesToInstall": [],
    "productIds": [
        "o365ProPlusRetail"
    ],
    "shouldUninstallOlderVersionsOfOffice": true,
    "targetVersion": "",
    "updateChannel": "deferred",
    "updateVersion": "",
    "useSharedComputerActivation": false,
    "officeSuiteAppDefaultFileFormat": "OfficeOpenDocumentFormat"
}
"@

        Write-Host "`tAdd app to Intune:"
        Write-Host "`t`t$appName " -NoNewline
        try {
            $uri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/"
            $intuneApp = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
            Write-Host "success" -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }
    }

    $appIdOffice     = $intuneApp.id
    $assignGroupName = $deviceGroupName

    # Assignments
    Write-Host "`tAssign Group to App"
    Write-Host "`t`tApp : $appIdOffice"
    write-Host "`t`tGroup: $assignGroupName " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$assignGroupName'"
        $entraGroup =  (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        if ($entraGroup.count -eq 0) {
            throw "Group not found: $assignGroupName"
        }

        Write-Host "found" -f Green
        $entraGroupId = $entraGroup.id
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }


    $jsonBody = @"
{
    "mobileAppAssignments": [
        {
            "@odata.type": "#microsoft.graph.mobileAppAssignment",
            "target": {
                "@odata.type": "#microsoft.graph.groupAssignmentTarget",
                "groupId": "$entraGroupId"
            },
            "intent": "Required",
            "settings": null
        }
    ]
}
"@


    Write-Host "`t`tAssigning group " -NoNewline
    try {
        $uri  = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appIdOffice/assign"
        $assignments = (Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop).value

        Write-Host "success" -f Green
    }
    catch {
        Write-Host "failed" -f Red
        throw $_
    }


#endregion

#region: [Intune] PowerShell scripts

    Write-Host "PowerShell scripts" -f Cyan

    foreach ($item in $arrScripts) {

        $scriptPath           = "$demoFolderPath\$($item.fileName)"
        $scriptFileName       = $item.FileName
        $scriptName           = $item.Name
        $scriptDescr          = $item.Description
        $scriptContentBase64  = ConvertToBase64 -Path $scriptPath -ErrorAction Stop
        $scriptRunAs          = $item.Scope

       $jsonBody = @"
{
    "displayName" : "$scriptName",
    "description" : "$scriptDescr",
    "scriptContent" : "$scriptContentBase64",
    "runAsAccount" : "$scriptRunAs",
    "fileName" : "$scriptFileName",
    "roleScopeTagIds" : [
        "0"
    ],
    "enforceSignatureCheck": false,
    "runAs32Bit" : false
}
"@


        Write-Host "`tGet uploaded script(s): $scriptName " -NoNewline 
        try {
            $uri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts?`$filter=displayName eq '$scriptName'"
            $response = Invoke-MgGraphRequest -Method Get -Uri $uri -ErrorAction Stop
            Write-Host "found $($response.value.Count)" -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

        foreach ($script in $response.value) {
            Write-Host "`tRemove: $($script.displayName) [$($script.id)] " -NoNewline 
            try {
                $uriDelete = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/$($script.id)"
                $response  = Invoke-MgGraphRequest -Method DELETE -Uri $uriDelete -ErrorAction Stop
                Write-Host "removed" -f Green
            }
            catch {
                Write-Host 'failed' -f Red
                throw $_
            }
        }


        Write-Host "`tUpload: $scriptName " -NoNewline 
        try {
            $uriPost = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
            $script = Invoke-MgGraphRequest -Method POST -Uri $uriPost -Body $jsonBody -ErrorAction Stop
            Write-Host "[$($script.id)] " -NoNewline
            Write-Host "success" -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }




        # Assign to User group

        write-Host "`tAssign to group"

        $jsonAssignments = ""
        foreach ($groupName in $item.assignments) {
            Write-Host "`t`tName : $groupName " -NoNewline
            try {
                $uri  = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$groupName'"
                [array]$group = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value

                if ($group.count -eq 0) {
                    throw "Group not found: $groupName"
                }
                Write-Host "[$($group[0].id)]" -f Green

                $jsonAssignments += @"
{
    "target": {
        "@odata.type": "#microsoft.graph.groupAssignmentTarget",
        "groupId": "$($group.id)"
    }
},
"@

            }
            catch {
                Write-Host "failed" -f Red
                throw $_
            }

        } # foreach group

        $jsonAssignments = $jsonAssignments.TrimEnd(',')
        $jsonBody = @"
{
    "deviceManagementScriptAssignments": [
        $jsonAssignments
    ]
}
"@

        Write-Host "`t`tAssigning " -NoNewline
        try {
            $uriPost = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/$($script.id)/assign"
            $assignment = Invoke-MgGraphRequest -Method POST -Uri $uriPost -Body $jsonBody -ErrorAction Stop
            Write-Host "success" -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

    } # foreach script


#endregion

#region: [Intune] Device preparation policies

    Write-Host "Device preparation policies" -f Cyan

    Write-Host "`tGet policy: $dppName " -NoNewline
    try {
        $uriGet = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies?`$filter=Name eq '$dppName'"
        $response  = Invoke-MgGraphRequest -Method GET -Uri $uriGet -ErrorAction Stop

        Write-Host "found $($response.value.count)" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }

    # Remove existing policies
    foreach ($dpp in $response.value) {
        $dppPolicy = $dpp
        $dppName   = $dpp.name
        $dppId     = $dpp.id
        Write-Host "`tRemove policy: $dppName [$dppId] " -NoNewline
        try {
            $uriDelete = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$dppId"
            $response  = Invoke-MgGraphRequest -Method DELETE -Uri $uriDelete -ErrorAction Stop
            Write-Host 'removed' -f Green
        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }
    }


    # Create new policy
    $arrayApps = @()
    $arrayApps += @{id = "$appIdOffice" ; type = "#microsoft.graph.officeSuiteApp" }



    $dppDeploymentMode   = 'enrollment_autopilot_dpp_deploymentmode_0'         # Deployment mode: User-driven (0)
    $dppDeploymentType   = 'enrollment_autopilot_dpp_deploymenttype_0'         # Deployment type: Single user (0)
    $dppJoinType         = 'enrollment_autopilot_dpp_jointype_0'               # Join type: Entra joined (0)
    $dppAccountType      = 'enrollment_autopilot_dpp_accountype_0'             # User Account type: Administrator (0), User (1)
    $dppTimeout          = 90                                                  # Minutes allowed before showing installation error: Default (60)
    $dppErrorMessage     = 'Contact your organization support person for help' # Custom error message
    $dppAllowSkipSetup   = 'enrollment_autopilot_dpp_allowskip_1'              # Allow users to skip setup after multiple attempts: No (0), Yes (1)
    $dppAllowDiagnostics = 'enrollment_autopilot_dpp_allowdiagnostics_1'       # Show link to diagnostics: No (0), Yes (1)


    # Apps
    $dppAllowedAppIds    = @"
[
 
"@

    foreach ( $app in $arrayApps) {
        $appId   = $app.id
        $appType = $app.type
        $dppAllowedAppIds += @"
    {
        "@odata.type": "#microsoft.graph.deviceManagementConfigurationStringSettingValue",
        "value": "{\"id\":\"$appId\",\"type\":\"$appType\"}"
    },
"@

    } # foreach
    $dppAllowedAppIds    = $dppAllowedAppIds.TrimEnd(',')
    $dppAllowedAppIds   += @"
 
    ]
"@



    # Scripts
    Write-Host "`tGet scripts:"
    $dppAllowedScriptIds = @"
[
 
"@

    foreach ($script in $arrScripts) {
        $scriptName = $script.Name
        Write-Host "`t`tName: $scriptName " -NoNewline 
        try {
            $uri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts?`$filter=displayName eq '$scriptName'"
            $response = (Invoke-MgGraphRequest -Method Get -Uri $uri -ErrorAction Stop).value
            if ($response.count -eq 0) {
                Write-Host "not found" -f Red
                Continue
            }
            Write-Host "[$($response.id)]" -f Green
            $dppAllowedScriptIds += @"
{
    "@odata.type": "#microsoft.graph.deviceManagementConfigurationStringSettingValue",
    "value": "$($response.id)"
},
"@

        }
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }
    } # foreach script

    $dppAllowedScriptIds    = $dppAllowedScriptIds.TrimEnd(',')
    $dppAllowedScriptIds   += @"
 
]
"@



    Write-Host "`tCreate policy: $dppName " -NoNewline
    $jsonBody = @"
{
    "name": "$dppName",
    "description": "$dppDescription",
    "settings": [
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "5874c2f6-bcf1-463b-a9eb-bee64e2f2d82"
                    },
                    "value": "$dppDeploymentMode"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_deploymentmode",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "5180aeab-886e-4589-97d4-40855c646315"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "e0af022f-37f3-4a40-916d-1ab7281c88d9"
                    },
                    "value": "$dppDeploymentType"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_deploymenttype",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "f4184296-fa9f-4b67-8b12-1723b3f8456b"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "1fa84eb3-fcfa-4ed6-9687-0f3d486402c4"
                    },
                    "value": "$dppJoinType"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_jointype",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "6310e95d-6cfa-4d2f-aae0-1e7af12e2182"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "bf13bb47-69ef-4e06-97c1-50c2859a49c2"
                    },
                    "value": "$dppAccountType"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_accountype",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "d4f2a840-86d5-4162-9a08-fa8cc608b94e"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance",
                "settingDefinitionId": "enrollment_autopilot_dpp_timeout",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "6dec0657-dfb8-4906-a7ee-3ac6ee1edecb"
                },
                "simpleSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationIntegerSettingValue",
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "0bbcce5b-a55a-4e05-821a-94bf576d6cc8"
                    },
                    "value": $dppTimeout
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance",
                "settingDefinitionId": "enrollment_autopilot_dpp_customerrormessage",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "2ddf0619-2b7a-46de-b29b-c6191e9dda6e"
                },
                "simpleSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationStringSettingValue",
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "fe5002d5-fbe9-4920-9e2d-26bfc4b4cc97"
                    },
                    "value": "$dppErrorMessage"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "a2323e5e-ac56-4517-8847-b0a6fdb467e7"
                    },
                    "value": "$dppAllowSkipSetup"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_allowskip",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "2a71dc89-0f17-4ba9-bb27-af2521d34710"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance",
                "choiceSettingValue": {
                    "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingValue",
                    "children": [],
                    "settingValueTemplateReference": {
                        "settingValueTemplateId": "c59d26fd-3460-4b26-b47a-f7e202e7d5a3"
                    },
                    "value": "$dppAllowDiagnostics"
                },
                "settingDefinitionId": "enrollment_autopilot_dpp_allowdiagnostics",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "e2b7a81b-f243-4abd-bce3-c1856345f405"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingCollectionInstance",
                "settingDefinitionId": "enrollment_autopilot_dpp_allowedappids",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "70d22a8a-a03c-4f62-b8df-dded3e327639"
                },
                "simpleSettingCollectionValue": $dppAllowedAppIds
            }
        },
        {
            "@odata.type": "#microsoft.graph.deviceManagementConfigurationSetting",
            "settingInstance": {
                "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingCollectionInstance",
                "settingDefinitionId": "enrollment_autopilot_dpp_allowedscriptids",
                "settingInstanceTemplateReference": {
                    "settingInstanceTemplateId": "1bc67702-800c-4271-8fd9-609351cc19cf"
                },
                "simpleSettingCollectionValue": $dppAllowedScriptIds
            }
        }
    ],
    "roleScopeTagIds": [
        "0"
    ],
    "platforms": "windows10",
    "technologies": "enrollment",
    "templateReference": {
        "templateId": "80d33118-b7b4-40d8-b15f-81be745e053f_1"
    }
}
"@

    try {
        $uri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies"
        $dpp = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
        Write-Host "created" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }

#endregion

#region: [Intune] Add Devicegroup to Policy

    Write-Host "`tAdd Devicegroup to policy:"

    Write-Host "`t`tGroup : $deviceGroupName " -NoNewline
    $uri = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$deviceGroupName'"
    try {
        $objGroup = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        if ($objGroup.count -eq 0) {
            throw "Group not found: $deviceGroupName"
        }
        Write-Host "[$($objGroup.id)]" -f Green
        $deviceGroupId = $objGroup.Id
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }
    $jsonBody = @"
{
    "enrollmentTimeDeviceMembershipTargets": [
        {
            "targetType": "staticSecurityGroup",
            "targetId": "$deviceGroupId"
        }
    ]
}
"@

    $uri = "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies('$($dpp.id)')/setEnrollmentTimeDeviceMembershipTarget"
    $retry = 0
    Write-Host "`t`tUpdate policy " -NoNewline
    Do {
        try {
            $response = (Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop)
            if ($response.validationSucceeded -eq $false) {
                throw "Group not found: $deviceGroupName"
            }
            Write-Host "success" -f Green
        }
        catch {
            $retry++
            if ($retry -le 5) {
                write-host '.' -NoNewline
                sleep 3
            }
            else {
                Write-Host 'failed' -f Red
                throw $_
            }
        }
    } Until ($response.validationSucceeded)


#endregion

#region: [Intune] Assign Usergroup to Policy

    Write-Host "`tAssign Usergroup to policy:"

    Write-Host "`t`tPolicy: $dppName [$($dpp.id)]"
    Write-Host "`t`tGroup : $userGroupName " -NoNewline
    $uri = "https://graph.microsoft.com/beta/groups?`$filter=displayName eq '$userGroupName'"
    try {
        $objGroup = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value
        if ($objGroup.count -eq 0) {
            throw "Group not found: $userGroupName"
        }
        Write-Host "[$($objGroup.id)]" -f Green
        $userGroupId = $objGroup.Id
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }


    $target = @{
        '@odata.type' ='#microsoft.graph.groupAssignmentTarget'
        "deviceAndAppManagementAssignmentFilterType" = "none"
        "groupId" = $userGroupId
    }

    Write-Host "`t`tAssigning " -NoNewline
    try {
        NewConfigurationPolicyAssignment -configurationPolicyId $dpp.id -target $target -ErrorAction Stop
        Write-Host "success" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }

#endregion

#region: [Intune] Import Corporate Identifiers

    Write-Host "Corporate Identifiers" -f Cyan

    foreach ($idType in $IdTypes) {

        $corpIdPath = "$filePath$idType.csv"
        Write-Host "`tImport file: $corpIdPath"
        try {
            $corporateIdentifiers = Get-Content $corpIdPath -ErrorAction Stop

            $importedDeviceIdentities = ""

            foreach ($identity in $corporateIdentifiers) {
                Write-Host "`t`t$identity " -NoNewline
                $arrId = $identity.split(',')
                Switch ($idType)  {
                    'Windows' {
                        Write-Host "valid" -f Green
                        $importedDeviceIdentities += @"
{
    "importedDeviceIdentityType": "manufacturerModelSerial",
    "importedDeviceIdentifier" : "$identity"
},
"@

                    }
                    'Serial' {
                        Write-Host "valid" -f Green
                        $importedDeviceIdentities += @"
{
    "importedDeviceIdentityType": "serialNumber",
    "importedDeviceIdentifier" : "$($arrId[0])",
    "description" : "$($arrId[1])"
},
"@

                    }
                    'IMEI' {
                        Write-Host "valid" -f Green
                        $importedDeviceIdentities += @"
{
    "importedDeviceIdentityType": "imei",
    "importedDeviceIdentifier" : "$($arrId[0])",
    "description" : "$($arrId[1])"
},
"@

                    }
                    default {
                        # Unknown
                        Write-Host "unknown type" -f Red
                    }
                } # switch
            } # foreach

            $importedDeviceIdentities = $importedDeviceIdentities.TrimEnd(',')

        } 
        catch {
            Write-Host 'failed' -f Red
            throw $_
        }

        Write-Host "`t`tRegister device(s) " -NoNewline
        $jsonBody = @"
{
    "overwriteImportedDeviceIdentities": false,
    "importedDeviceIdentities": [
$importedDeviceIdentities
    ]
}
"@

        try {
            $uri = "https://graph.microsoft.com/beta/deviceManagement/importedDeviceIdentities/importDeviceIdentityList"
            $importedDevice = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $jsonBody -ErrorAction Stop
            Write-Host "success" -f Green
        }
        catch {
            Write-Host "failed" -f Red
            throw $_
        }
    }

#endregion

#region:

    Write-Host "Final processing" -f Cyan
    Write-Host "`tDynamic group: $($userGroupName)"
    Write-Host "`tProcessing membershiprule for: $userUpn " -NoNewline
    try {
        do {
            Write-Host '.' -NoNewline
            sleep 5
            $uri  = "https://graph.microsoft.com/beta/groups/$($userGroup.id)/members"
            [array]$arrayGroupMembers = (Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop).value

        }
        until ($userUpn -in $arrayGroupMembers.UserPrincipalName)
        Write-Host " success" -f Green
    }
    catch {
        Write-Host 'failed' -f Red
        throw $_
    }
#endregion

Write-Host
Write-Host ('='*25) " Script ready "  ('='*25)
if (-not $csvPath) {
    Write-Host "You can run this script with the CsvPath parameter to import your own Corporate Identifiers (optional)"
    Write-Host "To extract Corporate Identifiers (Manufacturer,Model,SerialNumber) from your Windows device using PowerShell:"
    Write-Host "`tSet-Content -Path CorpId.csv -Encoding UTF8 -Value (`"{0},{1},{2}`" -f" -f Yellow
    Write-Host "`t`t(Get-CimInstance -Class Win32_ComputerSystem).Manufacturer," -f Yellow
    Write-Host "`t`t(Get-CimInstance -Class Win32_ComputerSystem).Model," -f Yellow
    Write-Host "`t`t(Get-CimInstance -Class Win32_BIOS).SerialNumber" -f Yellow
    Write-Host "`t)" -f Yellow
    Write-Host "`tGet-CimInstance -Class Win32_BIOS | FT SerialNumber" -f Yellow
}
Write-Host "Make sure your device is running Windows 11 version 22H2/22H3 or later with KB5035942"
Write-Host "Use these credentials for provisioning:"
Write-Host "`tUsername: $userUpn" -f Cyan
Write-Host "`tPassword: $userPassPlain" -f Cyan
Write-Host