AutomoxAutomox

Worklet: Automox OS Upgrade to Latest Windows10 OS version

AdamW-Automox
July 13, 2020 at 5:53 pm UTC

The following Worklet will ensure all of your devices are always on the most current release of Windows 10. Since the current version is 2004, that is the version this Worklet will upgrade you.

 

This Worklet supports multiple languages:

 

 

DISCLAIMER: THIS WILL AUTOMATICALLY REBOOT THE DEVICE WHEN THE UPGRADE IS COMPLETE WITHOUT USER NOTIFICATION

 

 

Thanks to @aescolastico for creating this!

 

 

Evaluation:

 

 

powershell23 lines
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:

 

 

powershell477 lines
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("1909", "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"

    } 



    # 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"

    } 



    # 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

}



function Start-Win10UpgradeISO {

    <#

    .SYNOPSIS

        Downloads the latest Windows 10 ISO, mounts it, and runs it silently.

    .NOTES

        Version:        1.1

        Author:         Andy Escolastico

        Creation Date:  02/11/2020



        Version 1.0 (2020-02-11)

        Version 1.1 (2020-06-03) - Added handling for case where drive letter was not mounted.                

        Version 1.2 (2020-06-03) - Added ISO download functionality

    #>

    [CmdletBinding()]

    param (

        #THIS FLAG DOES NOT WORK FOR THIS FUNCTION

        [Parameter(Mandatory=$false)] 

        [Boolean] $Reboot = $true,

        [Parameter(Mandatory=$false)] 

        [ValidateSet("64-bit", "32-bit")]

        [String] $Architecture = (Get-WmiObject Win32_OperatingSystem).OSArchitecture,

        [Parameter(Mandatory=$false)] 

        [String] [String] $DLPath = (Get-Location).Path + "\" +"Win10_" + $Architecture + ".iso",

        [Parameter(Mandatory=$false)] 

        [String] $LogPath = (Get-Location).Path 

    )



    Write-Verbose "Attempting to generate a $Architecture windows 10 iso download link" -Verbose

    try {

        $DLLink = Get-Win10ISOLink -Architecture $Architecture

    }

    catch {

        throw "Failed to generate windows 10 iso download link."

    }



    Write-Verbose "Attempting to download windows 10 iso to '$DLPath'" -Verbose

    try {

        (New-Object System.Net.WebClient).DownloadFile($DLLink, "$DLPath")

    }

    catch {

        throw "Failed to download ISO at path specified."

    }



    $ISOPath = $DLPath



    if (Test-Path $ISOPath) {

        $DriveLetter = (Mount-DiskImage -ImagePath $ISOPath | Get-Volume).DriveLetter

    } else {

        throw "ISO could not be found under $($ISOPath)."

    }



    Write-Warning "The Upgrade will commence shortly. Your PC will be rebooted soon. Please save any work you do not want to lose."



    if ($DriveLetter) {

        if ($Reboot -eq $true){

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

        } else{

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

        }    

    } else {

        throw "ISO could not be mounted on this system."

    }



}

New-Alias -Name "Start-Win10FeatureUpdate" -Value "Start-Win10UpgradeISO" -ea 0



function Start-Win10UpgradeWUA {

    <#

    .SYNOPSIS

        This function downloads the Windows update assistant tool and runs it silently.

    .NOTES

        Version:        1.0

        Author:         Andy Escolastico

        Creation Date:  05/10/2020

    #>

    [CmdletBinding()]

    param (

        [Parameter(Mandatory=$false)] 

        [Boolean] $Reboot = $true,

        #THIS FLAG DOES NOT WORK FOR THIS FUNCTION

        [Parameter(Mandatory=$false)] 

        [String] $DLPath = (Get-Location).Path,

        [Parameter(Mandatory=$false)] 

        [String] $LogPath = (Get-Location).Path

    )

    if(!(Test-Path -Path $DLPath)){$null = New-Item -ItemType directory -Path $DLPath -Force}   

    if(!(Test-Path -Path $LogPath)){$null = New-Item -ItemType directory -Path $LogPath -Force}      

    $DLLink = "https://go.microsoft.com/fwlink/?LinkID=799445"

    $PackagePath = "$DLPath\Win10_WUA.exe"

    $LogPath = "$LogPath\Win10_WUA.log"

    (New-Object System.Net.WebClient).DownloadFile($DLLink, "$PackagePath")

    Write-Host "The Upgrade will commence shortly. Your PC will be rebooted. Please save any work you do not want to lose."

    if ($Reboot -eq $true){

        Invoke-Expression "$PackagePath /copylogs $LogPath /auto upgrade /dynamicupdate /compat ignorewarning enable /skipeula /quietinstall"

    } else{

        Invoke-Expression "$PackagePath /NoReboot /NoRestartUI /NoRestart /copylogs $LogPath /auto upgrade /dynamicupdate /compat ignorewarning enable /skipeula /quietinstall"

    }

}



function Start-Win10UpgradeCAB{

    <#

    .SYNOPSIS

        This function downloads the feature enablement package cab file and runs it silently using dism.exe.

    .NOTES

        Version:        1.0

        Author:         Andy Escolastico

        Creation Date:  06/11/2020

    #>

    [CmdletBinding()]

    param (

        [Parameter(Mandatory=$false)] 

        [ValidateSet("1909")]

        [String] $Version = "1909",

        [Parameter(Mandatory=$false)] 

        [Boolean] $Reboot = $true,

        [Parameter(Mandatory=$false)]

        [String] $DLPath = (Get-Location).Path,

        [Parameter(Mandatory=$false)] 

        [String] $LogPath = (Get-Location).Path

    )

    if(!(Test-Path -Path $DLPath)){$null = New-Item -ItemType directory -Path $DLPath -Force}   

    if(!(Test-Path -Path $LogPath)){$null = New-Item -ItemType directory -Path $LogPath -Force}    

    if($Version -eq "1909"){

        $DLLink = 'http://b1.download.windowsupdate.com/d/upgr/2019/11/windows10.0-kb4517245-x64_4250e1db7bc9468236c967c2c15f04b755b3d3a9.cab'

    }

    $PackagePath = "$DLPath\Win10_CAB.cab"

    $LogPath = "$LogPath\Win10_CAB.log"

    (New-Object System.Net.WebClient).DownloadFile($DLLink, "$PackagePath")

    if ($Reboot -eq $true){

        Invoke-Expression "DISM.exe /Online /Add-Package /Quiet /PackagePath:$PackagePath /LogPath:$LogPath"

    } else{

        Invoke-Expression "DISM.exe /Online /Add-Package /Quiet /NoRestart /PackagePath:$PackagePath /LogPath:$LogPath"

    }

}



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

This discussion is archived and closed to replies. Continue it in the Automox Community Slack.

Discuss on Slack

Replies (67)

MaikelJuly 13, 2020
#1

sweet , the top check still refers to the 1909 iso? should that not be variable too?

AdamW-AutomoxJuly 13, 2020
#2

@Maikel The 1909 was for removing the iso after the install. This is in the evaluation so it will remove it after remediation is complete and we auto re-evaulate. This is actually not needed anymore so I removed it.

ss_tkJuly 13, 2020
#3

What if there is an update in between this one and the one that the device is presently on? Will it step up to the one in between and then this one or just go straight to the latest?
This sounds wonderful, just want to make sure I understand it correctly. My patch window is tomorrow, and I’d like to give this a go on a test group.

aescolasticoJuly 13, 2020
#4

The last line

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Ssl3, [Net.SecurityProtocolType]::Tls, [Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls12; (new-object Net.WebClient).DownloadString('https://raw.githubusercontent.com/RFAInc/windows10-iso/master/win10-iso-functions.ps1') | Invoke-Expression; Start-Win10FeatureUpdate -DLPath 'C:\Windows\Temp\Windows10.iso' -LogPath 'C:\Windows\Temp\WindowsUpgradeLogs'

actually downloads the entire script from GitHub and runs the function. Its a cool technique for giving clients scripts that you can update after the fact.

Meaning you could replace the entirety of the remediation block with just that last line. With this setup, you get the latest code updates, but you have less control over what gets deployed. I wouldn’t recommend it. I would advise doing what you guys normally do, where you paste the actual code, and run the function. Right now you’re halfway there. You’re still just downloading the code directly from github and invoking that, instead of running what’s above it. ATM they are the same but that could change if we push new code.

It also means that if a device fails to download the script from github, the whole thing will fail. Despite the fact that you have the code downloaded from Automox servers already.

This is what the last line would look like instead:

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

Apologies if that explanation isnt very clear.

jasonehandJuly 17, 2020
#5

trying to upgrade win10 1909 and i get the following error:

Mount-DiskImage : The I/O operation has been aborted because of either a thread exit or an application request. At line:150 char:25 + $DriveLetter = (Mount-DiskImage -ImagePath $ISOPath | Get-Vol … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (MSFT_DiskImage …torageType = 1):ROOT/Microsoft/…/MSFT_DiskImage) [Mou nt-DiskImage], CimException + FullyQualifiedErrorId : HRESULT 0x800703e3,Mount-DiskImage ISO could not be mounted on this system. At line:164 char:9 + throw “ISO could not be mounted on this system.” + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (ISO could not be mounted on this system.:String) [], RuntimeException + FullyQualifiedErrorId : ISO could not be mounted on this system. Error: 0x800703E3, -2147023901 Error Source: FACILITY_WIN32 Error Message: FACILITY_WIN32 is not currently supported by this translator.

DavidM-AutomoxAugust 6, 2020
#6

Thanks @aescolastico, Nic just updated the last line as you defined in this update!

DavidM-AutomoxAugust 6, 2020
#7

We had a few other customers with a similar issue, and we were also able to reproduce in our own testing. Our issue turned out to not be an actual issue with mounting the iso properly, but the drive letter was not returned. Here is what fixed it for me:

In the “Start-Win10UpgradeISO” function

Try replacing this:

powershell2 lines
if (Test-Path $ISOPath) {
        $DriveLetter = (Mount-DiskImage -ImagePath $ISOPath | Get-Volume).DriveLetter

With this:

powershell6 lines
   if (Test-Path $ISOPath) {
        $DriveLetter = (Mount-DiskImage -ImagePath $ISOPath | Get-Volume).DriveLetter
        $DriveLetter = (Get-DiskImage -ImagePath $ISOPath |Get-Volume).DriveLetter
        if (!$DriveLetter){
            Mount-DiskImage -ImagePath $ISOPath -StorageType ISO
            } 
aescolasticoAugust 6, 2020
#8

Hey I noticed a silly bug in my original code the other day.
The two lines that have:
if ($Version = “Latest”)

Are not using correct comparison syntax. Instead it’s assigning a value to the variable $Version.

It should be “-eq” instead of “=“. I’ve since update the code on GitHub. This would cause the script to always install the latest and never actually install version 1909. Apologies for not seeing this earlier. I would recommend updating the code in your worklet.

Nic-AutomoxAugust 6, 2020
#9

Thanks for catching that @aescolastico - I’ve changed those two lines; let me know if they look ok now.

aescolasticoAugust 6, 2020
#10

Looks right.

@Nic You guys ever find out if there’s a way to suppress reboots with the new ISOs?

Nic-AutomoxAugust 6, 2020
#11

Not that I know of. Let me ping @awhitman and see if he’s found a way for that.

SteveLordSeptember 23, 2020
#12

I can’t get this to work reliably at all. Ran it on 10 desktops and only 2-3 actually worked. Others just sit at pending and don’t ever do anything. My office has gigabit internet and computers running 7th gen i7s or newer so its not that.

This is a huge part of us investing in Automox and if we can’t get it to work reliably, that’s a big problem.

DavidM-AutomoxSeptember 23, 2020
#13

Hi SteveLord. You may want to open a help request with support for assistance.

If the Worklet is starting, but fails to run properly, as a test try running the scripts locally. Run the evaluation code to verify its return, then if it returns a non-zero, test the remediation code block locally on one of your devices where it is failing.
Test tips: Use an elevated ISE (x86) instance to run your tests. This will mimic the way the PS script will run via Automox. If you use a proxy, try running ISE as system rather than as a user to verify the download is working properly (as Automox agent would be making its internet connection as system rather thank your user).

SteveLordSeptember 23, 2020
#14

Yeah I’ve done that before and it worked that day/ate up a bunch of time. Just seems like it isn’t reliable enough to be consistent. Guess I’ll carve time out for that again.

Whole point of me using this system is so I do not have to manually touch each machine and run these over and over. Otherwise I would just run Microsoft’s update assistant or ISO myself. Granted, I am not saying it isn’t part Microsoft’s fault either with their awful update architecture.

aescolasticoSeptember 24, 2020
#15

The success rate really depends on a lot of unpredictable factors that vary from environment to environment. I’m sure there are plenty of edge cases this script fails to address. But maybe you’ll have better luck with the function that uses the Update Assistant instead of an ISO.
Clone the Worklet and replace the last line in the remediation code with:

Start-Win10UpgradeWUA -DLPath ‘C:\Windows\Temp’ -LogPath ‘C:\Windows\Temp’

TBastidasOctober 13, 2020
#16

@awhitman @d.mccleskey @Nic

Gentlemen, I’m new to the Automox world, and I’m not very familiar with PowerShell, so please forgive me for my ignorance. At the risk of being flamed: I’ve noticed 2 things.

  1. Is the first line of the evaluation code missing on this page? It starts on line 2, and the code for the 1909 Automatic Upgrade is very similar, except the first line. Is it okay to leave it out and copy exactly as shown above?

  2. On the blog post related to this update, step 4 says:
    “Copy and paste the Evaluation code scripts…”

As a newbie, I was thrown by this for a while. I didn’t know for sure what the purpose of the remediation code above was. I also didn’t see a way to let anyone know that it seemed incorrect, so here I am. Based on the 1909 blog post, I think it should actually say:
“Copy and paste the Evaluation and Remediation code scripts…”

Thank you so much for your time and graciousness.

~TB

WGrahamOctober 14, 2020
#17

We are currently using this for updates, but have been running into an issue with the 2004 update and drive mappings. Is there a way to use this same worklet but only get them to the 1909 version until the drive mappings issue is fixed?

DavidM-AutomoxOctober 14, 2020
#18

Hi @WGraham,
I had a problem with mounting too, and added an alternative mounting snippet above. Have you tried that?

WGrahamOctober 14, 2020
#19

Hi d.mccleskey,
I may have misspoke. The script works just fine and mounts the ISO perfect and does the update. The issue I am running into is when computers that are upgraded to 2004, they lose all network drives after being updated. That is why i was hoping for something similar but upgrades the computer to 1909 instead.

DavidM-AutomoxOctober 14, 2020
#20

Hi @TBastidas. Thank you for the feedback. I Believe you are correct with item 1. The iso check makes no sense if you don’t have the $iso variable defined. @awhitman

I would suggest removing everything before $osversion in your Evaluation Code. Any change should be handled in the Remediation Code.

For item 2, I believe the text should state “Copy and paste the Evaluation and Remediation Code Scripts” @ValB

@Nic, any chance we could get @TBastidas some swag for the input?

Nic-AutomoxOctober 14, 2020
#21

Definitely! I’ll send you a link to redeem one of the Automox Yeti mugs @TBastidas.

jack.smithOctober 23, 2020
#22

Heads up. The Get-Win10ISOLink function is now pulling a 20H2 link rather the 2004 link

SteveOctober 28, 2020
#23

What values need to be changed to specify a different version other than “latest” you want to download?

ie 1903->1909 or 1903->2004 not 20H2

jack.smithOctober 28, 2020
#24

Hey Steve. I’ve been looking at that function trying to figure out that very answer. I believe it has something to do with understanding both ProdID and the SKU. When I set the ProdID variable in that function to 1429 (found that ID already in the function) it returned release 1909. What I’m trying to figure out how is how to understand the SKU ID.

aescolasticoOctober 28, 2020
#25

@jack.smith The skuID and prodID are both required. Microsoft’s download website references these ids in the client side code. You can see them using web dev tools. They only go back 1 or 2 versions tho. You can get the ids of the older ISOs from the previous worklets. I would imagine all of these values are archived somewhere online by someone.

Keep in mind these are the values for Home/Pro:en-us ISOs.

9 lines
#1903
$prodID = "1384"
$skuID = "8829"
#1909
$prodID = "1429"
$skuID = "9029"
#2004H1
$prodID = "1626"
$skuID = "9959"

@Steve It should be said, that if you’re using the ISO install method, you don’t have to follow any upgrade path. Any version of windows 10 can upgrade to any newer version. This script would need to be modified in order to handle various version choices. When I wrote it, I only added 1909 and latest.

jack.smithOctober 28, 2020
#26

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!

aescolasticoOctober 28, 2020
#27

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

SteveOctober 28, 2020
#28

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.

aescolasticoOctober 28, 2020
#29

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,

9 lines
    # 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"
    } 
powershell10 lines
    # 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

3 lines
        [Parameter(Mandatory=$false)] 
        [ValidateSet("1909", "Latest")]
        [String] $Version = "Latest"
jack.smithOctober 28, 2020
#30

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…

powershell118 lines
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
aescolasticoOctober 28, 2020
#31

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

1 line
/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.

DavidM-AutomoxOctober 28, 2020
#32

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.

aescolasticoOctober 28, 2020
#33

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.

jack.smithOctober 28, 2020
#34

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.

SteveOctober 29, 2020
#35

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.

2 lines
[ValidateSet("1909","2004","Latest")]
[String] $Version = "2004"
maphNovember 4, 2020
#36

Has anybody tested & got this working with 20H2?

aescolasticoNovember 4, 2020
#37

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

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

and

1 line
Start-Win10UpgradeWUA -DLPath 'C:\Windows\Temp\Windows10.iso' -LogPath 'C:\Windows\Temp\WindowsUpgradeLogs'
TBastidasNovember 5, 2020
#38

@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:

8 lines
$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:

1 line
if (($osversion -le "2004")) 
jack.smithNovember 9, 2020
#39

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

jermicideNovember 10, 2020
#40

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!!

🙈🙉🙊

askorniaNovember 19, 2020
#41

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.

Nic-AutomoxNovember 19, 2020
#42

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.

jasonehandNovember 19, 2020
#43

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’

 

askorniaNovember 19, 2020
#44

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

jasonehandNovember 19, 2020
#45

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

askorniaNovember 19, 2020
#46

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

jasonehandNovember 19, 2020
#47

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

DavidM-AutomoxNovember 23, 2020
#48

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

askorniaDecember 7, 2020
#49

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.

aescolasticoDecember 7, 2020
#50

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:

askorniaDecember 7, 2020
#51

Doing this prompts this message in the activity report:

The Upgrade will commence shortly. Your PC will be rebooted. Please save any work you do not want to lose.

Since I’m assuming it’s running silently, I’m not 100% if it’s actually upgrading or doing anything, so I’ll let it run tonight and check back in tomorrow morning. I appreciate the speedy recommendation!

aescolasticoDecember 7, 2020
#52

You can tell if its working by the presence of an Upgrade Assistant process running in the background. The script only calls the executable and exits. It doesnt continue running with the upgrade tool. This does it make a bit more difficult to guage progress.

maphDecember 17, 2020
#53

Just wanted to say thanks for this script. Powershell is not my strong suit and I just used this to update the first 100 machines to 20H2 last night with a simple copy & paste. We’ll see how the rest go!

maphDecember 30, 2020
#54

So I spoke too soon. This was working great for a while and has now ceased to function. It seems to coincide with the 1.0-30 agent. I’m now seeing this error in the activity log:

Failed to download ISO at path specified. At C:\ProgramData\amagent\execDir939412927\execcmd817330194.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.

jasonehandDecember 30, 2020
#55

Hmmm, make sure your virus software or firewall has this path whitelisted. We have Sophos for both and I remember going in and whitelisting that entire amagent directory globally. Maybe that will help.

Jason

rmullenJanuary 12, 2021
#56

@askornia
Can you run the ISO manually and it passes the eval checks?
I know my hardware (Lenovo) is still unable to upgrade from 1909 > 2002/20H1 due to issue with a driver for an SSD.

https://support.microsoft.com/en-us/help/4592819/error-when-plugging-in-a-thunderbolt-nvme-ssd

Tony-AutomoxJanuary 15, 2021
#57

Shouldn’t there be an assignment to $iso at the top of the evaluation? Would it be:
$iso = ‘C:\Windows\Temp\Windows10.iso’

jasonehandJanuary 15, 2021
#58

Yup, that line at top worked, thanks!

Jason

CGalvez_00February 24, 2021
#59

Hey Guys

This is Carlos, I need help Deploying this. Every time I run the Worklet I am successful with ISO download, Mount with D Drive ISO in reports also get that computers are about to RESTART but it never does I get an ACCESS DENIED can someone help, please?

ERROR: Program ‘setup.exe’ failed to run: Access is denied

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
image

djdink828March 3, 2021
#60

Good afternoon all–

I have tested this worklet on several PC’s and it appears to work fine. My question is more concerned with the logistics of rolling this out to several hundred PCs—most of them located outside of our main building location. Since this takes over an hour to complete and reboots the PC without warning, how have you been working with users to upgrade PCs? Are you notifying groups of users in advance to make sure they have their PC powered on? Are you doing it in the evening? Are there any issues with users that shut down their PC in the middle of the process? Any insight would be greatly appreciated.

Thanks,
Dave

djdink828March 3, 2021
#61

I am trying this method now but I don’t seem to get any response on my test PC. It’s just replacing that last line in the remediation code, correct? I see the Powershell task kickoff in Task Manager and it seems to run a few minutes and then disappears. I don’t received any notifications and can see no other signs of the Upgrade Assistant running.

rmullenMarch 3, 2021
#62

@djdink828
If you’re upgrading from 1903 or later to 20H2, I would just use the feature enablement update rather than the full iso install.

djdink828March 4, 2021
#63

Rob, are you referring to this process:

https://support.microsoft.com/en-us/topic/feature-update-through-windows-10-version-20h2-enablement-package-02d91e04-931e-f00d-090c-015467c49f6c

Will this work for Win 10 Pro versions earlier than 2004?

Also, excuse my Automox ignorance, I’m new, but how would this be applied in Automox if it can be done?

Thanks in advance for your help.

Dave

rmullenMarch 4, 2021
#64

@djl52602

You can read Automox’s guide here - Controlled Windows 10 Feature Updates through Automox Patching

Essentially you have a worklet that runs this “Worklet: Enable Controlled Windows 10 Feature Updates through Automox Patching” - just change the $rInfoValue variable to whatever version you want to upgrade to, i.e 20H2.
This sets a registry entry telling Windows that the version you can upgrade to is XXX.

Then from there, you create a patch policy that installs ‘Feature Upgrades’.
I.e. an advanced policy I use that has a scope like this:
Make sure this policy has a reboot otherwise it will not install

Make sure you apply the worklet and policy to the group(s) that contain the devices you want to be updated.

In short:

  • worklet to set the target version
  • patch policy to install the feature upgrade
djdink828March 4, 2021
#65

Thanks Rob, I tried this several days ago and hosed two PCs but I think I did it wrong. I’ll follow your directions tomorrow and try another test. Thanks again.

Dave

whalechrisMarch 10, 2021
#66

I have run into the same issue but can see the feature update available in the windows UI but nothing through automox. Looked for the reg key as well but no luck yet the feature updates appears released to the machine. If anyone has any advice I would appreciate it!

djdink828March 12, 2021
#67

Sorry, deleted this post…I was posting in wrong topic area.