Worklet: Automatic OS Upgrade to Windows 10 1909

  • 20 February 2020
  • 35 replies
  • 634 views

Userlevel 5
Badge

Hi Ya’ll


Big Shoutout and thanks to @rich for getting the information needed in order for this script to work successfully!


This is a one-click automatic upgrade without needing to pre-deploy the 1909 .iso image to your devices. This Worklet will automatically build out the download link for 1909 based on your OS’s architecture, download the .iso image to you local system, mount the .iso to a disk drive, and then automatically run the download setup all silently without any user interaction. It will remove the iso after the installation completes.


The Evaluation and Remediation code for the Worklet is as follows:


Evaluation:


$iso = "C:\programdata\Windows1909OSUpgrade.iso"

if ((Test-Path $iso) -eq $true)
{Remove-Item $iso
}

$osversion = (Get-Item "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue('ReleaseID')

if (($osversion -lt "1909"))
{exit 1
}
else
{exit 0
}

Remediation:


$lang = "English"
$locID = "en-US"
$verID = "Windows10ISO"
$skuID = "9029"
$prodID = "1429"
$archID = "IsoX64"

## variables you might not want to change (unless msft changes their schema)
$pgeIDs = @("a8f8f489-4c7f-463a-9ca6-5cff94d8d041", "cfa9e580-a81e-4a4b-a846-7b21bf4e2e5b")
$actIDs = @("getskuinformationbyproductedition", "GetProductDownloadLinksBySku")
$hstParam = "www.microsoft.com"
$segParam = "software-download"
$sdvParam = "2"

## used to spoof a non-windows web request
$userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"

## used to maintain session in subsequent requests
$sessionID = [guid]::NewGuid()

## builds session request url
$uri = "https://www.microsoft.com/" + $locID + "/api/controls/contentinclude/html"
$uri += "?pageId=" + $pgeIDs[0]
$uri += "&host=" + $hstParam
$uri += "&segments=" + $segParam + "," + $verID
$uri += "&query="
$uri += "&action=" + $actIDs[0]
$uri += "&sessionId=" + $sessionID
$uri += "&productEditionId=" + $prodID
$uri += "&sdvParam=" + $sdvParam

## requests user session
Invoke-WebRequest -UserAgent $userAgent -WebSession $session $uri -ErrorAction:Stop -Method:Post -Headers $headers -UseBasicParsing | Out-Null

## builds link request url
$uri = "https://www.microsoft.com/" + $locID + "/api/controls/contentinclude/html"
$uri += "?pageId=" + $pgeIDs[1]
$uri += "&host=" + $hstParam
$uri += "&segments=" + $segParam + "," + $verID
$uri += "&query="
$uri += "&action=" + $actIDs[1]
$uri += "&sessionId=" + $sessionID
$uri += "&skuId=" + $skuID
$uri += "&lang=" + $lang
$uri += "&sdvParam=" + $sdvParam

## requests link data
$response = Invoke-WebRequest -UserAgent $userAgent -WebSession $session $uri -ErrorAction:Stop -Method:Post -Headers $headers -UseBasicParsing

## parses response data
$objs = $response.Links
$objs | Foreach-Object {$_.href = ($_.href).Replace('amp;','')}
$linky = $objs | Select-Object -Property href | Sort-Object -Property href
$link = $linky | Select-Object -ExpandProperty href

## Determine correct link for OS Architecture
$osarch = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
If ($osarch -eq "64-bit") {
$dllink = $link | Where-Object {$_ -match 'x64.iso'}
} elseif ($osarch -eq "32-bit") {
$dllink = $link | Where-Object {$_ -match 'x32.iso'}
}

Write-Output $dllink

Try {
(new-object System.Net.WebClient).DownloadFile("$dllink", "C:\programdata\Windows1909OSUpgrade.iso") |
Out-file C:\Windows\Temp\output.csv -Append
} Catch {
Write-Error ".iso linked was successfully created but download Failed. Reference c:\windows\temp\output.csv for more information or contact Automox Support"
exit 1
}

#specify path to ISO image
#############Change the settings in this block#######################
$isoImg = "C:\programdata\Windows1909OSUpgrade.iso"
##############################################################



Mount-DiskImage -ImagePath $isoImg
$letter = (Get-DiskImage $isoImg | Get-Volume).DriveLetter
$dos = ":" ### Unnecessary with changed line below
$drivemount = "$letter" + "$dos"
Set-Location $drivemount

# Required: The drive letter needs to be manually changed to match the value of $driverletter (ex: Y:)

# Optional: /pkey is for a device that needs a product key. Default is disable, remove "#" to enable. (ex: /pkey XXXXX-XXXXX). Not required for install

# Optional: /noreboot is enabled by default. Remove "#" to automatically reboot the device after install is complete

./setup.exe /auto upgrade /quiet #/pkey

The Evaluation code exits with a 1 if a devices OS version is less than 1909 and executes the policy at the scheduled time


Choose the setup.exe arguments you want when it performs the OS update. This is the last line of code in the Remediation block. By Default it is set to do a quiet install and automatically reboot the device to complete the upgrade.


Here is an example of the setup.exe command using the /quiet and /noreboot argument:


Copy to clipboard


.\setup.exe /auto upgrade /quiet /noreboot

That should do it! Be sure to test this on a few devices before deploy to production environment.


Let me know if there are any questions!


35 replies

Thank you sooooo very much for publishing this worklet! 🤑 🤑 🤑

Userlevel 5
Badge

Some enhancements have been made to the 1909 upgrade Worklet. These include the following:



  1. Automatic removal of the 1909 .iso image once upgrade has been completed

  2. Automatic drive letter assignment to image disk mount.


These enhancements are aimed to make the script fully automated without any user or admin interaction once the policy is created, and scheduled.


The code above has already been edited to reflect these enhancements.

to begin, dude…BRAVO 🤑 🤑 🤑 running a small shop with little/no help, this one worklet has saved my ass!


couple items/questions (sorry if these have been addressed elsewhere/or are appealingly nonsensical)…



  1. if you don’t mind, which lines within the remediation above automatically remove the .iso image? (or how is this auto-magically being removed?)

  2. regarding notifications :: currently, when patching has been completed, a “patches” applied notification (with summary) is emails. could something similar be done with worklets?

  3. regarding user deferred reboots :: currently, when patching has been completed, the user can be given the option to defer pending reboots. could something similar be done with worklets?


again, you are my hero for 2020!! 🦾 🦾 🦾

Userlevel 5
Badge

@jermicide Thanks for the kind words! Glad this is helping you out in your Windows environment!




  1. The .iso removal code is actually in the evaluation code. This works because once a Worklet runs it automatically re-evaluates to ensure the Worklet was successful and is compliant for the device. So, I added the remove-item command in the first line of evaluation so it removes the .iso once the remediation finishes.




  2. You could work the logic in PS to send an email. It could run the $osversion command and if it’s on 1909, then it would send an email letting you know the upgrade was successful. If $osversion returns the same version you were on, then you know it failed and would send a failure email. You could use the same IF statement logic and instead of exiting 1 or 0, it would send the appropriate email. However, it would need to be added in addition to the current IF statement, as if you remove it, it will not evaluate the devices.




  3. No with Worklets today. I do have a reboot notification worklet in this community page that could be sent after the 1909 upgrade and you could use the /noreboot flag in the install command. This way it wont reboot after upgrading to 1909 and then the reboot notification script would run after and schedule the reboot letting users know we will be rebooting.




Hope this helps!

Userlevel 5
Badge +1

Still analyzing. But I think the addition of removing the ISO when the ISO doesn’t exist is causing a failed exit code, and for the remediation to kick off. This is how I’ve updated mine


$iso = ‘C:\programdata\amagent\Windows1909OSUpgrade.iso’

IF((Test-Path $iso) -eq $true){Remove-Item $iso}

Userlevel 5
Badge

Jack,


This is a great catch, thanks for sharing! Definitely don’t want evaluation failing since remediation is pretty heavy. I have updated the original code to include this logic.


Thanks again!

Phenomenal. This will save me hours of manual time. Thank you for such wonderful work.

Way above anything I could ever do, but I will use willingly and with gratitude.

Any idea how long the update takes? Should I expect approximately the same amount of time as a creator update? Hour or so? Those things take forever sometimes!

Userlevel 5
Badge

You are very welcome! Glad this will give you some time back in your day to focus on more important things. The upgrade time really depends on how old the current OS. For devices running 1903, it took about 20-35 minutes from start to finish for the Worklet to upgrade a device.

Ran this today and still had the iso under amagent/Windows1909OSUpgrade.iso after the installation finished. Tracked it down and the quotes when copied from the script in the first line of the evaluation aren’t safe. Be sure to change the quotes if you copy this section of code:



For whatever reason the quotes were “fancy” and I ended up having to change them out with normal quotes.


When I ran it on a device, it upgraded from 1903 to 1909 in just under 22 minutes with zero intervention. When I ran it the second time to see if it would evaluate correctly, it reinstalled 1909.

Userlevel 7

Thanks for the heads up @ews - I’ll let @awhitman know to come take a look.

Userlevel 5
Badge

Thanks for finding this. and sharing!


I am not sure why this is not working for you. The single quote vs the double quote should not matter in this scenario. The only difference in single and double quotes in PS is that a double quote is Powershell reads every character in the string, and looks for characters that can be substituted for a variable defined outside the string. Since there is no variable, or PS operation between the double quotes, it just reads it as a normal string. I will test this further and see if I can reproduce it. however, I am glad this change got it working for you!


22 minutes is about what I see as well. The reason the upgrade ran again on the device is because you manually executed the policy on the device, which skips evaluation, and runs the remediation code immediately on the device(s). The Evaluation code of the Worklet is ran every time a device rescan takes place, either manually, or on the rescan schedule of the Group. The evaluation results determine if remediation needs to run when the policy is scheduled for execution, and also marks the policy for the device as “pending” or “compliant”


A Worklet will automatically rescan a device once it completes running on the device in order to determine if the device is compliant after remediation. If you want to determine the results of evaluation code on a device, all you need to do is rescan the device and see if the policy marks it as “pending”, or "complaint.


Let me know if you have any further questions.


Thanks!

@awhitman - thanks for following up on this. I read the manual execution and skipping the evaluation and it didn’t even click. Thanks for the explanation.


Here’s more detail to the issue I experienced:


Using Chrome 81.0.4044.113, this is how the code block appears on screen:



Notice the “fancy” quotes.


When you copy the code (using the “copy to clipboard” widget) and paste into automox, it keeps the “fancy” quotes, which does not properly enclose the string, which means it isn’t being evaluated correctly:

image


If I change the quotes to either single or double, it properly encloses the string (the text turns green):

Single Quotes

image


Double Quotes

image


Same issue in Firefox 75.0

image


Just an oddity… not sure why it is happening.

I can concur with this…

Mine acted the same way. It was not deleting the iso. in fact some of my 1903’s were doing the same thing, and I checked the “fancy” quotes, and they were there as well. Changed to regular quotes and the string enclosed properly. Haven’t checked if the iso deleted or not, but I’m guessing it probably did, since yours did.

Userlevel 5
Badge

Thanks so much for noticing this! It must be the way I moved it into the community post. The one in my Github that I used to post this is using double quotes.


I have made the appropriate change to the original code post for both 1909, and 1903.


Thanks again!

Userlevel 7

Thanks for figuring this out @ews - I gave you the community bug finder badge for giving us the solution!

We haven’t had much luck getting this to work. Checking the activity log we see the following. When I search for the c:\windows\temp\output.csv file referenced one does not exist. Any ideas on how best to identify our issue?


You cannot call a method on a null-valued expression. At C:\ProgramData\amagent\execDir807239699\execcmd579763798.ps1:41 char:25 + $objs | Foreach-Object {$.href = ($.href).Replace(‘amp;’,’’)} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (🙂 [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull C:\ProgramData\amagent\execDir807239699\execcmd579763798.ps1 : .iso linked was successfully created but download Failed. Reference c:\windows\temp\output.csv for more information or contact Automox Support + CategoryInfo : NotSpecified: (🙂 [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,execcmd579763798.ps1

Userlevel 7

Sorry about that. I’ve notified @awhitman to come take a look and see if he can figure out where it’s failing.

Userlevel 5
Badge

Chris,


Take the remediation code and try running it directly on a device. It looks like the download link is failing, so I wonder if it has something to do with the Worklet being executed remotely.


Copy the remediation into a x86 Powershell ISE admin session directly on one of the devices and see if it errors out there as well. I tested it and it worked for me.

Wow, for the first time in my life I found an active discussion about the same issue I am experiencing. Amazing


I am doing a trial of Automox and experiencing the same issue Chris_2.0 is seeing. I just tried your recommendation awhitman - running the remediation code manually in Powershell and low and behold the .ISO file is actually downloading now.


I’m not sure what this means for deploying it as intended?

Unfortunately it errors out locally for me as well


You cannot call a method on a null-valued expression.

At line:40 char:25



  • $objs | Foreach-Object {$.href = ($.href).Replace(‘amp;’,’’)}


  •                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    • CategoryInfo : InvalidOperation: (🙂 [], RuntimeException

    • FullyQualifiedErrorId : InvokeMethodOnNull



@Ckraem608 Did you have to make any adjustments to the remediation code to run it locally or does it work as posted above?

Chris - I did not make any adjustments, just copied the code and pasted into x86 Powershell ISE. The download started right away. Unfortunately I did get a pop-up on that machine from UAC asking me to confirm I wanted to make changes. Afterwards it installed and rebooted automatically. Hopefully I can figure it out so it is seamless.


I copied the code from the blog post not this post though.


https://blog.automox.com/automox-worklet-os-upgrade-windows-10-1909?hs_amp=true

Userlevel 7

I think the code in this topic has been updated since the blog post was made, so you’ll want to use the top post which @awhitman keeps updated, rather than the blog post.

I need to give this another go as tried a few weeks ago and ISO downloaded but install didn’t happen. Need to work through why.

Userlevel 5
Badge

@trilly what happens when you take the remediation code and try to run it locally on the device using Powershell x86 ISE? It will probably throw and error that we can look at to help root cause this.

Reply