Migrate to the cloud – Part 1. Setup

Introduction

This blog series is comprised of 3 parts. In this part  I’ll cover how you can migrate your existing configuration manager managed, domain joined devices to Azure AD joined, and Intune managed devices. During that process the app also converts those devices to Windows Autopilot devices, all with minimal downtime for the end user and via an easy to use self-service app.

  • Migrate to the cloud – Part 1. Setup <- you are here
  • Migrate to the cloud – Part 2. Customization
  • Migrate to the cloud – Part 3. Troubleshooting

migrate to the cloud.png

If you’d like to see a video showing the entire migration process then here it is.

Note: In the video,  I refer to a Windows Hello for Business problem pin entry, but that was simply because I was restoring a check-pointed virtual machine and testing it over and over. You shouldn’t see that problem on regular computers.

The app itself is comprised of 3 main parts which does the following:

Part #1
1. Create local migration admin account APSweepback, enable Autologin
2. Remove MBAM client
3. Remove SCCM client
4. Change the Windows shell
5. Drop out of the domain
6. Restart computer…

Part #2
1. AutoLogin as APSweepback account
2. Start second script (shell)
3. rename old ‘on prem’ account to xxxxx.OLD
4. popup AADJ wizard asking for credentials
5. convert the device to Windows Autopilot device
6. create scheduled task for part 3
7. restart computer (to reapply UAC settings and for WHFB)

Part #3
1. Install Company Portal
2. Launch OneDrive for Business
3. Cleanup registry keys and changes
4. add to Autopilot Sweepback completed AAD group (for reporting and remediation scripts)
5. popup notification that all is done and logoff

The app development is mostly done, but it’s still evolving based on feedback, if you know of better ways of doing things within the app then do please let me know.

Here’s a short overview of the apps main features

  • Created with Powershell
  • Uses Azure Functions (HttpTriggers)
  • Disconnects a device from the domain/ConfigMgr
  • Connects to Azure AD/Intune
  • Convert device to Windows Autopilot
  • Users data stored in OneDrive
  • Users data remains on the device after migration
  • Installed apps remain installed on the device
  • UI front end for the end user
  • Status screen indicating progress
  • Detailed Logs
  • Email ability (via Azure/Sendgrid)

Step 1. Get the scripts

Note: You can only download these files when logged on as a member of https://www.windows-noob.com

autopilot_sweepback.zip 212.87 kB · 2 downloads

Extract the zip to C:\DEV

The extracted ZIP files should look something like this

extract to c dev.png

 

Step 2. Get ServiceUI.exe from MDT

You’ll need the ServiceUI.exe executable file to display user interfaces (UI) to end users in SYSTEM context. As our app will be deployed from ConfigMgr in SYSTEM context, we’ll utilize ServiceUI.exe. To get the file, download and install MDT somewhere and navigate to C:\Program Files\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64. To download MDT click here.

Copy the ServiceUI.exe file to your extracted C:\DEV\autopilot_sweepback\Encode files folder so it looks like this.

ServiceUI extracted.png

 

Step 3. create some azure ad groups

In Microsoft Endpoint Manager (MEM), create two Static Azure AD groups with the following names:

  • Autopilot Sweepback Completed
  • Convert devices to Windows Autopilot

After creating the groups, take note of the ObjectId of each aad group.

 

aad group objectid.png

 

Step 4. create a Windows Autopilot deployment profile

In MEM, navigate to Devices, Windows, Windows Enrollment, and select Windows Autopilot Deployment Profiles, select Create to create a new profile and make sure that Convert all targeted devices to Autopilot is set to YES and that the profile is assigned to the Convert devices to Windows Autopilot Azure AD group created in step 3 above.
Convert all targeted devices to Autopilot.png
Step 5. create an Azure function

In Azure, I will assume you’ve already created a functionapp as per Step 4 of this blog post. If not, go ahead and create one and then return to this step. Next create a new httptrigger called add_device_to_aad_group and insert the following code into it.

 

# Niall Brady 2022/05/21 (used by the Check Compliance, Software Updates to devices solutions amongst others...)
# Dynamically ADDS a device to an azure ad group 
#

using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$deviceId = $Request.Query.deviceId
$GroupID = $Request.Query.GroupId

if (-not $deviceId) { 
$deviceId = $Request.Body.deviceId
}
if (-not $GroupId) { 
$GroupId = $Request.Body.GroupId
} 

# define the following variables
$ApplicationID =    "" # this is the id of the app you created in app registrations
$TenantDomainName = "" # your tenant name, eg: windowsnoob.com
$AccessSecret =     "" # this is the secret of the app you create in app registrations


# create the body
$Body = @{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
client_Id = $ApplicationID
Client_Secret = $AccessSecret
}

# make initial connection to Graph
$ConnectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantDomainName/oauth2/v2.0/token" -Method POST -Body $Body
# get the token
$token = $ConnectGraph.access_token
$token

# to improve logging...
$triggerName = "add_device_to_aad_group"
$a = Get-Date
$body = " `n"
$body = $body + "$a Starting the '$triggerName' function...`n"
$body = $body + "$a Connected to tenant: $TenantDomainName.`n"

#START $FindDevice
if ($deviceId -and $GroupId) {
	$Group = Invoke-RestMethod -Method Get -uri "https://graph.microsoft.com/v1.0/groups?`$filter=Id eq '$GroupId'" -Headers @{Authorization = "Bearer $token"} | Select-Object -ExpandProperty Value
	$GroupName = $Group.displayName
	$body = $body + "$a You supplied deviceId: '$deviceId'" + ".`n" 
	$body = $body + "$a You supplied groupId: '$GroupId'" + ".`n"    
	$body = $body + "$a Group.displayName: '$GroupName'" + ".`n"
    
	#$GroupMembers =  Invoke-RestMethod -Method Get -uri "https://graph.microsoft.com/v1.0/groups/$GroupID/members?$filter " -Headers @{Authorization = "Bearer $token"} | Select-Object -ExpandProperty Value
	# | Select-Object -ExpandProperty Value
    
    # below fixes the 100 members per returned result in AAD problem
    $GroupMembers2 = Invoke-RestMethod -Method GET -uri "https://graph.microsoft.com/v1.0/groups/$GroupID/members?`$count=true&`$filter=startswith(deviceid,'$deviceId')" -Headers @{Authorization = "Bearer $token";"ConsistencyLevel" = "eventual"}
	
	# if found do this
	if ($GroupMembers2.value.deviceId){
		#$body = $body + "--------------------------------------------------------------------`n"
        #$body = $body + "This device was found in the AAD group so no need to add it again...`n"
		#$body = $body + "deviceId: " + $GroupMembers2.value.deviceId + "`n"
        #$body = $body + "displayName: " + $GroupMembers2.value.displayName + "`n"
		#$body = $body + "--------------------------------------------------------------------`n"
		Write-Host -ForegroundColor Yellow "$GroupMembers2.value.displayName is in the group" 
		$body = $body + "$a Device: " + $GroupMembers2.value.displayName + " is already in the " + $GroupName + " group, nothing to do.`n"
		$body = $body + "$a The computer is already in the group, nothing to do.`n"
		$Status = "Already present in group"
	}
	else	{
        $AddDevice = Invoke-RestMethod -Method Get -uri "https://graph.microsoft.com/v1.0/devices?`$filter=deviceId eq '$deviceId'" -Headers @{Authorization = "Bearer $token"} | Select-Object -ExpandProperty Value | %{ 
        Write-Host -ForegroundColor Green "Adding $($_.DisplayName) ($($_.ID)) to the group"
        $body = $body +  "$a Adding $($_.DisplayName) ($($_.ID)) to the group with ObjectID $GroupID.`n"
        $ComputerName = $($_.DisplayName) 
        $Status = "ADDED"
        $BodyContent = @{
            "@odata.id"="https://graph.microsoft.com/v1.0/devices/$($_.id)"
        } | ConvertTo-Json
            # code to add it here...
            # the $ref variable is explained here... kinda # https://docs.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0&tabs=http
            try {Invoke-RestMethod -Method POST -uri "https://graph.microsoft.com/v1.0/groups/$GroupID/members/`$ref" -Headers @{Authorization = "Bearer $token"; 'Content-Type' = 'application/json'} -Body $BodyContent
            # pause some seconds to allow time for the object to be populated if recently added...
            sleep 30
            }
            catch { $body = $body + "$a ERROR ADDING THE DEVICE`n"
                    $body = $body + "Here is the error message: '$_.ErrorMessage'"
                    $Status = "ERROR ADDING THE DEVICE"
                    }
	}
    }

}
#END $FindDevice

$a = Get-Date
$body = $body + "$a Exiting Azure function."
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $body
})

 

In the code above, fill in the following values that correspond to your environment:

  • ApplicationID
  • TenantDomainName
  • AccessSecret

application id tenant and access secret.png

Don’t forget to Save your changes in the Http trigger. Take note of the function URL by clicking on Get Function URL, it’ll look something like this

image.png

Step 6. edit the variables

Next, open Powershell ISE and locate the win.ap.sweepback_part1.ps1 powershell script. Fill in or change the missing values for the variables listed below:

image.png

So it looks more like this (the values from my tenant are blurred):

image.png

Next, open the encode.ps1 script and run it, once completed, browse to the Encoded files folder and locate the

After running the script, locate the encoded_ServiceUI.txt file and open it in notepad.

image.png

Copy the contents of that file using CTRL+A followed by CTRL+C

ecoded_serviceui.png

Paste that code into the following line in between the quotation marks

serviceui code added.png

Save the changes to the win.ap.sweepback_part1.ps1 code.

Step 7. create and deploy the app in ConfigMgr

In my example, I simply created a package/program in ConfigMgr to deploy this app, use whatever application model you wish. To do this simply copy two files to a folder called Autopilot_Sweepback and use that as your package source.

  • ServiceUI.exe
  • win.ap.sweepback_part1.ps1

package source for configmgr.png

the program for the package uses the following line

ServiceUI.exe -process:explorer.exe %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File Win.AP.Sweepback_part1.ps1

as shown here

image.png

Next, deploy the package to a collection containing some devices you want to migrate.

deployed to all windows 10.png

Step 8. Test it

Now the hard work is mostly done and you can start testing it, launch it from Software Center and start migrating to the cloud !

migrate to the cloud in software center.png

That’s it, join me in the next parts where we’ll go a little further!

cheers

niall

Related reading

This entry was posted in Auto MDM enrollment, Azure AD, System Center Configuration Manager (Current Branch), Windows AutoPilot. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.