anyweb Posted May 21, 2022 Report post Posted May 21, 2022 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 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 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. 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. 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. 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 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 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. So it looks more like this (the values from my tenant are blurred): 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. Copy the contents of that file using CTRL+A followed by CTRL+C Paste that code into the following line in between the quotation marks 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 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 Next, deploy the package to a collection containing some devices you want to migrate. 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 ! That's it, join me in the next parts where we'll go a little further! cheers niall Related reading https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/to-aad-join-or-not-that-is-the-question/ba-p/3435768 https://www.windows-noob.com/forums/topic/15773-how-can-i-enable-mdm-auto-enrollment-for-microsoft-intune-in-azure/ https://docs.microsoft.com/en-us/windows/client-management/mdm/mdm-enrollment-of-windows-devices https://docs.microsoft.com/en-us/mem/intune/enrollment/enrollment-restrictions-set https://techcommunity.microsoft.com/t5/microsoft-endpoint-manager-blog/the-journey-to-cloud-based-device-management/ba-p/3264631 https://docs.microsoft.com/en-us/mem/cloud-native-endpoints-overview Quote Share this post Link to post Share on other sites More sharing options...
bondy666 Posted May 25, 2022 Report post Posted May 25, 2022 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! Quote Share this post Link to post Share on other sites More sharing options...
anyweb Posted May 25, 2022 Report post Posted May 25, 2022 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 Quote Share this post Link to post Share on other sites More sharing options...
bondy666 Posted May 26, 2022 Report post Posted May 26, 2022 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 Quote Share this post Link to post Share on other sites More sharing options...
anyweb Posted May 26, 2022 Report post Posted May 26, 2022 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 Quote Share this post Link to post Share on other sites More sharing options...
anyweb Posted May 27, 2022 Report post Posted May 27, 2022 ok i've uploaded a new copy of the scripts, it should resolve the devmode issues we saw in your lab last night Quote Share this post Link to post Share on other sites More sharing options...
ryand274 Posted June 20, 2022 Report post Posted June 20, 2022 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? Quote Share this post Link to post Share on other sites More sharing options...
anyweb Posted June 21, 2022 Report post Posted June 21, 2022 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 Quote Share this post Link to post Share on other sites More sharing options...
ryand274 Posted June 21, 2022 Report post Posted June 21, 2022 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! Quote Share this post Link to post Share on other sites More sharing options...
ryand274 Posted June 21, 2022 Report post Posted June 21, 2022 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? Quote Share this post Link to post Share on other sites More sharing options...