How to Automatically Provision Win10 Autopilot Devices – Part 2

Scenario

You have an autopilot demonstration environment running of Hyper-V. Considering the environment dynamics, you need to automate the VM provisioning. Manual approach is no longer feasible – “when you do something more than 2 times the same way, it is time to automate …”

Solution

  • Prepare a golden image you can use to clone new VM.
  • From the Hype-V host start VM provisioning, upload Autopilot data to Intune and invoke remote wipe of the new clone to prepare it for Autopilot OOBE experience

In this post you will prepare the Hyper-V host to control the Autopilot VM creation based on the golden image we created earlier

Steps:

  • Configure AutoPilot profile assignment using a dynamic device group
  • Create the PS script
  • Run the automation …. wait ….. yeeeey! 😊

Let’s configure the deployment profile auto-assignment.

  • Open the Azure portal and login to your tenant
  • Go to Intune > Groups
  • Click new group
  • Set group type to Security
  • Group Name to AutoPilotDevices
  • Membership type is Dynamic Device
  • Open Dynamic Device membership blade click advanced rule

Enter the following rule to add all new devices to the group :

(device.devicePhysicalIDs -any _ -contains "[ZTDId]")
  •  Click Add Query
  • Create

Now, we have a group that will contain all devices we upload as autopilot devices. It is time to create and assign a profile

  • Go to Intune > Device enrollment -> Windows Enrollment – Deployment Profiles
  • Click Create profile
  • In the Create profile blade
  • Enter name – e.g  WinAutoPilotProfile
  • Deployment mode remains User-Driven
  • Set Join to Azure Ad as Azure AD joined
  • Click Out-of-box experience and set the following
  • Leave defaults for the 4 settings
  • Set computer template to WinAP-%RAND:3%. This is not necessary required for our demo but is fun 😊. When you enroll the device in Intune, the device will get a name based on template
  • Click Save then Create  

Now it is time to assign the profile to devices. As you remember, we created a device group that will dynamically contain all autopilot devoices

  • In the windows Autopilot deployment profiles blades
  • Select the newly created profile
  • Click on assignment
  • Click select groups to include
  • Click the group we created before
  • Click select then save

Note: before moving further copy the boot.vhdx file from the golden VM to the same location with the below script. In my example I used C:\VMs\boot-gold.vhdx

Note: before moving further with the script create the password files. As we don’t want to have passwords in clear text in our scripts load the following function in PS

function Create-SecurePass {
<#
.SYNOPSIS
This function is used to create user password and write it to disk securely
.DESCRIPTION
The function gets password and writes it on disk securely 
.EXAMPLE
Create-SecurePass
#>
[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true)][string]$pfile
)
Read-Host "Enter Password" -AsSecureString |  ConvertFrom-SecureString | Out-File $pfile
} 

Then execute it for the Intune Create-SecurePass for both local admin and Intune password as shown below. In my example I put the password files in the same location with the script C:\VMs\

When your done you should have 2 password files in c:\VMS

On the Hyper-V host, you will do create a PowerShell script that will do the following:

  • Import required modules
  • Create credentials for local admin on the VM clone
  • Create credentials for Intune
  • Create a new VM and copy the golden image disk to the new VM. Start the new VM
  • Keep trying to create a PS session with the new VM. Basically, the script keeps trying till the machine is fully booted and able to accept winrm PSSessions
  • When the PSSession is created the script runs the get-WindowsAutopilotInformation command and load the value in a variable ($d)
  • Add the device information to Intune using Add-AutoPilotImportedDevice cmd-let. Note that we pass order value set to “VMs”. You can use this filed for identification. Let’s say you create the clone for someone. You can put that person’s name in order  
  • intune Autopilot cmd-lets work asynchronously. This means we need to wait for the information to propagate. The script sleeps till the data is there
  • Wait for device information to get into Intune
  • Wait for the profile assignment
  • Finally, trigger the remote MDM wipe

Use the below script

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true)][string]$VMName
)

function Create-SecurePass {

<#
.SYNOPSIS
This function is used to create user password and write it to disk securely
.DESCRIPTION
The function gets password and writes it on disk securely 
.EXAMPLE
Create-SecurePass
#>

[cmdletbinding()]

param
(

    [Parameter(Mandatory=$true)][string]$pfile
)

Read-Host "Enter Password" -AsSecureString |  ConvertFrom-SecureString | Out-File $pfile
}


function Create-Cred-fromFile {
param
(

    [Parameter(Mandatory=$true)][string]$user,
    [Parameter(Mandatory=$true)][string]$pfile

)

$myCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, (Get-Content $pfile | ConvertTo-SecureString)
return $myCred

}

function Create-VM {
<#
.SYNOPSIS
This function is used to create a hyper-v VM
.DESCRIPTION
Create a Hyper-V VM. Chnage the parameters inside the function to customize the VM. At the end of the fucntion TPM is set and
boot disk is replace with gold image
#>
param
(
    [Parameter(Mandatory=$true)][string]$VMN,
    [parameter(Mandatory=$true)][string]$bootIMG
)

$NewVMParam = @{
  Name = $VMN
  Generation = 2
  MemoryStartUpBytes = 1GB
  Path = "c:\VMs"
  SwitchName =  "Ext"
  NewVHDPath =  "c:\VMs\$VMN\boot.vhdx"
  NewVHDSizeBytes =  50GB 
  ErrorAction =  'Stop'
  Verbose =  $True
  }

  $SetVMParam = @{
  ProcessorCount =  2
  DynamicMemory =  $True
  MemoryMinimumBytes =  1GB
  MemoryMaximumBytes =  3Gb
  ErrorAction =  'Stop'
  PassThru =  $True
  Verbose =  $True
  }

$VM = New-VM @NewVMParam 
$VM = $VM | Set-VM @SetVMParam 

##### Set TPM
$owner = Get-HgsGuardian UntrustedGuardian
$kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot
Set-VMKeyProtector -VMName $VMN -KeyProtector $kp.RawData
Enable-VMTPM -VMName $VMN

##### Copy gold image
$dst="c:\VMs\$VMN\boot.vhdx"
Write-Output "Copying Disk"
Copy-Item $bootIMG $dst
Write-Output "Starting $VMN"
Start-vm -Name $VMN
}

function Create-Cred {
<#
.SYNOPSIS
This function is used to create a credentiale based on user and password provieded as parameters
#>

param
(
    [Parameter(Mandatory=$true)][string]$user,
    [Parameter(Mandatory=$true)][string]$password
)

$securePass = ConvertTo-SecureString $password -AsPlainText -Force
$myCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, $securepass
return $myCred
}

Function Get-AuthToken-Pass {
<#
.SYNOPSIS
This function is used to create a authenticate token. Tenenat and credentials are parameters
#>
[cmdletbinding()]
param
(
    [Parameter(Mandatory=$true)][string]$tenantName,
    [Parameter(Mandatory=$true)][System.Management.Automation.PSCredential]$cred
)

try{
     return (Get-MSIntuneAuthToken -TenantName $tenantName -credential $cred)
    }
catch {
    write-output "Error Getting token" 
    }
}

#########
### Main
#########


###### import required modules
Import-Module -Name AzureAD -ErrorAction Stop
Import-Module -Name PSIntuneAuth -ErrorAction Stop
Import-Module -Name WindowsAutoPilotIntune -ErrorAction Stop

###### set variables for Azure credentials

$AZuser="admin@M365xxxxxx.onmicrosoft.com"
$AZpfile ="c:\VMs\azureadmin.pass"
$AZten="M365xxxxxx.onmicrosoft.com"

####### Set variables for local admin on clone
$user = "marco"
$localpfile = "c:\VMs\admin.pass"

###### Create credentials, tokens and connecto to Intune
$localcred = Create-Cred-fromFile -user $user -pfile $localpfile
$AzCred = Create-Cred-fromFile -user $AZuser -pfile $AZpfile
$Token= Get-AuthToken-Pass -tenantName $AZten -cred $AzCred
$global:authToken = $Token
Connect-AutoPilotIntune -user $AZuser

#### create VM
Create-VM -bootIMG "c:\VMs\boot-gold.vhdx" -VMN $VMName

#### wait for clone to boot up and accept Powershell remote sessions
$file ="hwid.csv"

$session =$null
while ( $session -eq $null) {
    $session = New-PSSession -Credential $localcred -VMName $VMName -ErrorAction Ignore
    if ($session) { break}
    Write-Output "PS Session not ready. Retrying in 1 min "
    Start-Sleep -Seconds 60 
}

### get Autopilot information and upload it in Intune
$d =Invoke-Command -Session $session -command  { c:\powershell\get-ap-info.ps1 }  
Write-Output "Device Serial is $d.'Device Serial Number'"
Add-AutoPilotImportedDevice  -serialNumber $d.'Device Serial Number' -hardwareIdentifier $d.'Hardware Hash' -orderIdentifier "VMs"
Start-Sleep 20


#### wait for autopilot data to propagate in intune
$impdev = $null 
while ( $impdev -eq $null ){
$impdev = ( Get-AutoPilotDevice  |?{$_.serialNumber -eq $d.'Device Serial Number'}) 
if($impdev) 
{
 Write-Output "Device has been Imported"
 break
 }
Write-Output "Device has not been imported yet. Sleeping 2 min"
Invoke-AutopilotSync
Start-Sleep -Seconds 120 
}

$ProfileAssigned ="notAssigned"
while ($ProfileAssigned -like "notAssigned" -or $ProfileAssigned -like "*pending*"){
    $ProfileAssigned =  (Get-AutoPilotDevice|?{$_.serialNumber -eq $d.'Device Serial Number'}).deploymentProfileAssignmentStatus
    Write-Output "Profile has not been assigned yet. Sleeping 1 min"
    Start-Sleep -Seconds 60 
}

### Finaly the wait came to an end. We can wipe the device :D:D
 Write-Output "Invoking remote wipe of $VMName"
Invoke-Command -Session $session -command  { c:\powershell\start-wipe.bat } 

Now let’s see the demo in action 😊

As seen below I ran the PS script to create a VM name Win10-APClient.

Then, the machine performs a wipe

After 20-40 minutes depending on your host power you will have the machine ready for Autopilot demo