Jump to content


anyweb

Migrate to the cloud - Part 1. Setup

Recommended Posts

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.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 (uses Auto MDM enrollment)
  • 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

 

Note: Last updated 2022/05/31 (v1.4 logsfolder bug)

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:

Note: DjoinPwd and DjoinAcct must use valid credentials for a user in your domain. The values below are examples from my lab.

image.png

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

image.png

Note: The APIKey value is for sending emails using sendgrid, if you'd like info about setting that up see this guide (point 4)

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

Share this post


Link to post
Share on other sites

Hi Niall

 

Love this idea in principal but can't seem to get it to work. The first screen shows as it goes through ccm uninstall etc but after it reboots, it fails to auto-logon (seems to select Administrator rather than APSweepback). When I manually choose APSweepback and type the password, it logs on but doesn't seem to do anything afterwards. I dare say it's something I have done wrong. Can you provide a bit more clarity on the obfuscated values? I am assuming for instance that the DJoinAcc user/password are DOMAIN accounts rather than AAD accounts and that the FQDN/Domain name are your local domain you are migrating from. Also, where does the API Key value come from? I used a defaul app key from the function app but don't think this is right.

Thanks and keep up the good work!

image.png.ab0fec64dcd729cead1ca78522f1f549.png

Share this post


Link to post
Share on other sites

HI @bondy666 i'm happy to do a remote quickassist session to take a look if you want, and yes the djoin variables are for an on-premises account, ideally i'd like to remove that from the script altogether but haven't got around to that yet

when testing the script manually, make sure to run it as Administrator, or better yet, as SYSTEM context (using psexec /i /s cmd.exe)

 

the APIkey is only used for sending emails via Sendgrid (emails are sent when something fails) and this key is generated when you configure sendgrid to send emails, see from point 4 of this blog post

Share this post


Link to post
Share on other sites

Part 1 works (appears) absolutely fine. When it reboots machine fails to autologon (selects Administrator). I have tried this on different hardware and a VM. Interestingly the VM did pick up the script again but then got stuck at 'Launching AAD Join'. Happy to run a remote quick assist (please confirm if the email you have starts with 'mail' - if so this is an old address). In the meantime I attach logs for the VM and for physical hardware.

Thanks

hardware.zip VM.zip

Share this post


Link to post
Share on other sites

have you verified it's correctly creating the APSweepback account ? you can place an

EXIT 1

just after that section in the takeaction function....to troubleshoot it further and then make sure you have devmode=$true so that you can click abort to the status screen.

 

you can reach me at niall AT windowsnoob.com

 

Share this post


Link to post
Share on other sites

ok i've uploaded a new copy of the scripts, it should resolve the devmode issues we saw in your lab last night

Share this post


Link to post
Share on other sites

Hi niall

 

Im having a hard time getting step 2 to kick in from the scheduled task. the APSweepback user get created and autologs on just fine. but itll go to desktop and not run the Task Schedular. when looking at the task scheduler i get an error

"Task Scheduler Failed to start "\ChangeWindowsShell" task for user "AUKWINTEST01\APSweepback. Additional Data: Error Value: 2147943726""

I have tried reassigning the user to the scheduled task but i get the same issue.

 

if i manually run the command the scheduled task uses in CMD and restart, the part2 will start.

 

Am i missing something, or is something wrong? 

Share this post


Link to post
Share on other sites

hi @ryand274

did you modify the step to create the scheduled task in any way, it's very sensitive to any changes

secondly, how are you testing this, i'd recommend you take a look at part 3 where I explain how to troubleshoot things

Share this post


Link to post
Share on other sites

1 hour ago, anyweb said:

hi @ryand274

did you modify the step to create the scheduled task in any way, it's very sensitive to any changes

secondly, how are you testing this, i'd recommend you take a look at part 3 where I explain how to troubleshoot things

No I didn’t, I’ll go through it again from start to finish and see if I can find anything I did wrong, hopefully it’s some simple, the only things I changed were the variables in the script and the part that asks for the application ID etc. 

 

I am testing by just deploying it as if I was a user, I’ll take a look at step 3.

 

by the way, thank you for making this tool, this is going to save me so much time!

Share this post


Link to post
Share on other sites

1 minute ago, ryand274 said:

No I didn’t, I’ll go through it again from start to finish and see if I can find anything I did wrong, hopefully it’s some simple, the only things I changed were the variables in the script and the part that asks for the application ID etc. 

 

I am testing by just deploying it as if I was a user, I’ll take a look at step 3.

 

by the way, thank you for making this tool, this is going to save me so much time!

Also, when creating the program in SCCM, what settings did you create on the program? Did you ask it to run as admin or under the user context? 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...


×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.