Worklet: Automox OS Upgrade to Latest Windows10 OS version



Show first post

67 replies

Userlevel 5
Badge +1

Thanks @aescolastico I’ve not mastered using the web dev tools in Chrome to extract those details. The #2020H1 was the Spanish skuID. I’m going to dig in and see if I can figure out how to understand using those web dev tools. Appreciate the inspiration!

Yeah its a great tool. Pretty crucial if you’re going to do any sort of web scraping. Sorry about the skuID, I copied the wrong one. Ive updated my comment with what should be en-us

Thanks @aescolastico. But like @jack.smith said, latest is now 20h2 not 2004. I’m still trying to figure out what needs to change so that it will only download 2004.


I’m in the middle of an Automox trial and still figuring out the best way to do things.

My comment should have the correct values for 2004 (H1). Do you know how to write powershell? These two blocks can be modified with if else statements for 2004,


    # prefered prodID
if ($Version -eq "Latest") {
# grabs latest id
$response = Invoke-WebRequest -UserAgent $userAgent -WebSession $session -Uri "https://www.microsoft.com/$Locale/software-download/windows10ISO" -UseBasicParsing
$prodID = ([regex]::Match((($response).RawContent), 'product-info-content.*option value="(.*)">Windows 10')).captures.groups[1].value
} else{
# uses hard-coded id
$prodID = "1429"
}

    # prefered skuid
if ($Version -eq "Latest") {
# grabs latest id
$skuIDs = (($response.RawContent) -replace "&quot;" -replace '</div><script language=.*' -replace '</select></div>.*' -split '<option value="' -replace '">.*' -replace '{' -replace '}'| Select-String -pattern 'id:') -replace 'id:' -replace 'language:' -replace '\s' | ConvertFrom-String -PropertyNames SkuID, Language -Delimiter ','
$skuID = $skuIDs | Where-Object {$_.Language -eq "$Language"} | Select-Object -ExpandProperty SkuID
}
else{
# uses hard-coded id
$skuID = "9029"
}

Then theres adding the values to this params Validation Set


        [Parameter(Mandatory=$false)] 
[ValidateSet("1909", "Latest")]
[String] $Version = "Latest"
Userlevel 5
Badge +1

Here is the updated function I built based on what @aescolastico relayed. I’m still testing to see if it will actually use the ISO to update to that version. I’ve been deep down the rabbit hole here. Even when the 20H2 link downloaded, using DISM to look at the version of the WIM file still showed 2004. So I think that the update process itself is what is installing 20H2 rather than what ISO is used… I could be wrong…


function Get-Win10ISOLink {
<#
.SYNOPSIS
This function generates a fresh download link for a Windows 10 ISO
.NOTES
Version: 1.6
Author: Andy Escolastico
Creation Date: 10/11/2019
#>
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)]
[ValidateSet("64-bit", "32-bit")]
[String] $Architecture = (Get-WmiObject Win32_OperatingSystem).OSArchitecture,
[Parameter(Mandatory=$false)]
[ValidateSet("fr-dz", "es-ar", "en-au", "nl-be", "fr-be", "es-bo", "bs-ba", "pt-br", "en-ca", "fr-ca", "cs-cz", "es-cl", "es-co", "es-cr", "sr-latn-me", "en-cy", "da-dk", "de-de", "es-ec", "et-ee", "en-eg", "es-sv", "es-es", "fr-fr", "es-gt", "en-gulf", "es-hn", "en-hk", "hr-hr", "en-in", "id-id", "en-ie", "is-is", "it-it", "en-jo", "lv-lv", "en-lb", "lt-lt", "hu-hu", "en-my", "en-mt", "es-mx", "fr-ma", "nl-nl", "en-nz", "es-ni", "en-ng", "nb-no", "de-at", "en-pk", "es-pa", "es-py", "es-pe", "en-ph", "pl-pl", "pt-pt", "es-pr", "es-do", "ro-md", "ro-ro", "en-sa", "de-ch", "en-sg", "sl-si", "sk-sk", "en-za", "sr-latn-rs", "en-lk", "fr-ch", "fi-fi", "sv-se", "fr-tn", "tr-tr", "en-gb", "en-us", "es-uy", "es-ve", "vi-vn", "el-gr", "ru-by", "bg-bg", "ru-kz", "ru-ru", "uk-ua", "he-il", "ar-iq", "ar-sa", "ar-ly", "ar-eg", "ar-gulf", "th-th", "ko-kr", "zh-cn", "zh-tw", "ja-jp", "zh-hk")]
[String] $Locale = (Get-WinSystemLocale).Name,
[Parameter(Mandatory=$false)]
[ValidateSet("Arabic", "Brazilian Portuguese", "Bulgarian", "Chinese (Simplified)", "Chinese (Traditional)", "Croatian", "Czech", "Danish", "Dutch", "English", "English International", "Estonian", "Finnish", "French", "French Canadian", "German", "Greek", "Hebrew", "Hungarian", "Italian", "Japanese", "Korean", "Latvian", "Lithuanian", "Norwegian", "Polish", "Portuguese", "Romanian", "Russian", "Serbian Latin", "Slovak", "Slovenian", "Spanish", "Spanish (Mexico)", "Swedish", "Thai", "Turkish", "Ukrainian")]
[String] $Language = "English",
[Parameter(Mandatory=$false)]
[ValidateSet("1903","1909","2004","Latest")]
[String] $Version = "Latest"
)

# prefered architecture
if ($Architecture -eq "64-bit"){ $archID = "x64" } else { $archID = "x32" }

# prefered prodID
if ($Version -eq "Latest") {
# grabs latest id
$response = Invoke-WebRequest -UserAgent $userAgent -WebSession $session -Uri "https://www.microsoft.com/$Locale/software-download/windows10ISO" -UseBasicParsing
$prodID = ([regex]::Match((($response).RawContent), 'product-info-content.*option value="(.*)">Windows 10')).captures.groups[1].value
} else{
# uses hard-coded id
#$prodID = "1429"
$prodID = switch ($Version)
{
'1903' {'1384'}
'1909' {'1429'}
'2004' {'1626'}
Default {}
}
}

# 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"
$verID = "Windows10ISO"

# 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/" + $Locale + "/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
$response = Invoke-WebRequest -UserAgent $userAgent -WebSession $session -Uri $uri -UseBasicParsing

# prefered skuid
if ($Version -eq "Latest") {
# grabs latest id
$skuIDs = (($response.RawContent) -replace "&quot;" -replace '</div><script language=.*' -replace '</select></div>.*' -split '<option value="' -replace '">.*' -replace '{' -replace '}'| Select-String -pattern 'id:') -replace 'id:' -replace 'language:' -replace '\s' | ConvertFrom-String -PropertyNames SkuID, Language -Delimiter ','
$skuID = $skuIDs | Where-Object {$_.Language -eq "$Language"} | Select-Object -ExpandProperty SkuID
}
else{
# uses hard-coded id
#$skuID = "9029"
$skuID = switch ($Version)
{
'1903' {'8829'}
'1909' {'9029'}
'2004' {'9959'} #9982 spanish
Default {}
}
}


# builds link request url
$uri = "https://www.microsoft.com/" + $Locale + "/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=" + $Language
$uri += "&sdvParam=" + $sdvParam

# requests link data
$response = Invoke-WebRequest -UserAgent $userAgent -WebSession $session -Uri $uri -UseBasicParsing

# parses response data
$raw = ($response.Links).href
$clean = $raw.Replace('amp;','')

# stores download link
$dlLink = $clean | Where-Object {$_ -like "*$archID*"}

# outputs download link
Write-Output $dlLink
}
Get-Win10ISOLink -Architecture $Architecture -Version 2004

Thats interesting. Im not in a postion to be able to test this myself but the two ISOs should install different versions of windows. The only reason I could see a 20H1 ISO resulting in a 20H2 installation, would be the flag being used when calling setup.exe


/DynamicUpdate

It tells the installer to grab the latest windows updates during the upgrade process. But thats really only supposed to grab rollups, drivers etc.

Userlevel 4

It (/DynamicUpdate) is probably grabbing the feature update enablement package along with the other updates. The previous cumulative updates would contain all the actual update bits for 20H2, and the enablement package is just switching them on.


Oh right of course. I forgot about the enablement package. I suppose removing that flag would be a good first step to testing that theory.

Userlevel 5
Badge +1

Going to test that out with the 2004 ISO. The function does set it to disabled as identified here https://techcommunity.microsoft.com/t5/windows-it-pro-blog/the-benefits-of-windows-10-dynamic-update/ba-p/467847

Time will tell. Fingers crossed it works, I personally want to wait to upgrade to 20H2 for audit purposes. Need vendors to catch up to a baseline measure I can deploy against it.

If it helps anyone, all I had to do was add 2004 to ValidateSet, then change $Version to 2004 and change the prodID and skuID to match 2004 based on @aescolastico previous post . If I understand it right, this forces the ‘else’ block so the hard-coded ID get used.


[ValidateSet("1909","2004","Latest")]
[String] $Version = "2004"

Has anybody tested & got this working with 20H2?

Either of these lines in the remediation block should result in 20H2


Start-Win10FeatureUpdate -DLPath 'C:\Windows\Temp\Windows10.iso' -LogPath 'C:\Windows\Temp\WindowsUpgradeLogs'

and


Start-Win10UpgradeWUA -DLPath 'C:\Windows\Temp\Windows10.iso' -LogPath 'C:\Windows\Temp\WindowsUpgradeLogs'

@maph,


The Evaluation Script below (just an edited version of the one at the start of the article which has some code fragments) and the Remediation Script shown at the start of this page will result in an upgrade to 20H2.


In fact, it works so well that some of us are now trying to figure out how to modify the code to get the 2004 update instead.


Evaluation Script:


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

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

The only thing you may want to tweak is the Evaluation Script. Currently it’s looking for a device running a version less than 2004. But if you’re already running 2004, it will not execute the update. The fix is to make it less than or equal to, like this:


if (($osversion -le "2004")) 
Userlevel 5
Badge +1

I’m currently convinced that the ISO upgrade process, even using the 2004 ISO will ultimately upgrade to 20H2 (2009). Don’t think that using the /DynamicUpdate option as disabled/enabled is preventing that at all.


Invoke-Expression "$($DriveLetter):\setup.exe /auto Upgrade /quiet /Compat IgnoreWarning /DynamicUpdate disable /copylogs $LogPath"


For those of you wanting to download older ISO, this script can likely be analyzed to fit your needs. https://gist.github.com/AveYo/c74dc774a8fb81a332b5d65613187b15

i am not sure how to say this…i mean, i am having a moment.


adam…dude, you are killing it. you have saved me sooooo much time. first 1909. now this.




O_o


adam et al., keep it up!


fwiw, if there is some beer/coffee fund that i can venmo/cash app, let me know…i will pony up!!


🙈🙉🙊

Userlevel 1
Badge

I’m curious if there’s a way to run a similar worklet that could mount / install the ISO from a network location rather than downloading one from the internet? I’m not that savvy in powershell, but I’m open to do some tinkering.

Userlevel 7

That should work, although you might need to tinker with network creds and mapped drives. The worklets run as system and not the currently logged in user, so you can’t rely on any mapped drives or creds from the logged in user.

I got this working once I pulled the iso down, I just placed it on a network share then mapped in the powershell code to that location, works pretty good:

 

You just need to replace: $networkPath with the path you placed the iso in.

 

 

Good Luck!!

 

 

Jason

 

 

Evaluation Code:

 

 

if ((Test-Path $iso) -eq $true)

 

 

{Remove-Item $iso

 

 

}

 

 

$osversion = (Get-Item “HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion”).GetValue(‘ReleaseID’)

 

 

if (($osversion -lt “2004”))

 

 

{exit 1

 

 

}

 

 

else

 

 

{exit 0

 

 

}

 

 

Remediation Code:

 

 

<#

 

 

.SYNOPSIS

 

 

This script will attempt to copy an iso from a network share to a local directory, mount the iso, and run a Windows 10 upgrade command.

 

 

.DESCRIPTION

 

 

This script may not work on all systems. Modify to fit your needs.

 

 

Please test locally and ensure the script runs as intended before automating deployments.

 

 

.NOTES

 

 

File Name :UpgradeWin10fromISOonNetwork.ps1

 

 

Author :Automox

 

 

Prerequisite :OS and iso must match in architecture, SKU, and language.

 

 

.LINK

 

 

https://www.automox.com/

#>

 

 

############# Change the settings in this block #######################

 

 

$networkPath = ‘\isis\PCCommon\Win10\Windows10.iso’

 

 

$isoPath = ‘C:\Windows\Temp\Windows10.iso’

 

 

$logPath = ‘C:\windows\Temp’

 

Userlevel 1
Badge

Nice, I appreciate the speedy response. Does your above code only mount the ISO or actually run the install? Just from giving it a once-over it appears to just mount it.


I found an applicable code and am trying something similar. I’m currently testing now, will move onto your recommendation if this doesn’t work.


[Evaluation Code]


$osversion = (Get-Item “HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion”).GetValue(‘ReleaseID’)


if (($osversion -lt “2004”))

{exit 1

}

else

{exit 0

}

[Remediation Code]


#Variable Decalration

$isoImg = “\[Location]Windows10x64Enterprise2004.iso”

$arguments = “/auto upgrade /quiet /showoobe none /Telemetry disable”


#Mount ISO and Gather Drive Letter

Mount-DiskImage -ImagePath $isoImg -PassThru | Out-Null


#Obtain Drive Letter

$driveletter = (Get-DiskImage $isoImg | Get-Volume).DriveLetter

Write-Host “ISO file has been mounted to drive $driveletter!”


#Launch Windows Upgrade Tool

Start-Process “$($driveLetter):\setup.exe” -ArgumentList $arguments

Yes, it mounts the iso and then runs the install.


I’ve used it to upgrade a few machines per night, usually without fail.


Occasionally I’ll have 1 that won’t upgrade and I’ll just run it manually, but has been working for me pretty good.


Jason

Userlevel 1
Badge

Is there a powershell installation file uploaded to this worklet that it invokes?

Not that I know of no, I worked with Automox techs for like 2 weeks to get it working, I couldn’t get the upgrade from online working so we downloaded the ISO, then they helped adjust the script to point to the network location.


The only powershell that I’m aware of is the script in the worklet.


During testing with the techs, they had me save the worklet into a powershell .ps1 script so we could test, that’s when we figured out how to point to a network location.


Jason

Userlevel 4

Here is the one @jasonehand and I worked on: Windows 10 Feature Update From Local Server Share

Userlevel 1
Badge

Having a few issues with running the ISO from a share, I decided to circle back and try this one again.


It appears to correctly run…goes through the reboot…then fails and reverts back to the previous version (after doing all the Windows is applying Updates junk).


Here are the details from the report:


VERBOSE: Attempting to generate a 64-bit windows 10 iso download link VERBOSE: Attempting to download windows 10 iso to ‘C:\Windows\Temp\Windows10.iso’ WARNING: The Upgrade will commence shortly. Your PC will be rebooted soon. Please save any work you do not want to lose.


I tried to let it it run again and get the following error in activity reports:

Failed to download ISO at path specified. At C:\ProgramData\amagent\execDir603594699\execcmd550507694.ps1:145 char:9 + throw “Failed to download ISO at path specified.” + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (Failed to downl…path specified.:String) [], RuntimeException + FullyQualifiedErrorId : Failed to download ISO at path specified. COMMAND TIMED OUT.


I’m kind of at a loss for what to try at this point.

Maybe you could try the update assistant method? It doesnt rely on an ISO. Fewer moving pieces means fewer points of failure.


Replace the last line in the remediation block with this:


Reply