Skip to main content

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:

 

 



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:

 

 

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 (

rParameter(Mandatory=$false)]

rValidateSet("64-bit", "32-bit")]

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

rParameter(Mandatory=$false)]

rValidateSet("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")]

rString] $Locale = (Get-WinSystemLocale).Name,

rParameter(Mandatory=$false)]

rValidateSet("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")]

rString] $Language = "English",

rParameter(Mandatory=$false)]

rValidateSet("1909", "Latest")]

rString] $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.groupst1].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 = sguid]::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

rParameter(Mandatory=$false)]

rBoolean] $Reboot = $true,

rParameter(Mandatory=$false)]

rValidateSet("64-bit", "32-bit")]

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

rParameter(Mandatory=$false)]

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

rParameter(Mandatory=$false)]

rString] $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 (

rParameter(Mandatory=$false)]

rBoolean] $Reboot = $true,

#THIS FLAG DOES NOT WORK FOR THIS FUNCTION

rParameter(Mandatory=$false)]

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

rParameter(Mandatory=$false)]

rString] $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 (

rParameter(Mandatory=$false)]

rValidateSet("1909")]

rString] $Version = "1909",

rParameter(Mandatory=$false)]

rBoolean] $Reboot = $true,

rParameter(Mandatory=$false)]

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

rParameter(Mandatory=$false)]

rString] $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'

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!




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.


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!


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) S], RuntimeException + FullyQualifiedErrorId : Failed to download ISO at path specified. COMMAND TIMED OUT.


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


@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


Shouldn’t there be an assignment to $iso at the top of the evaluation? Would it be:


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


Yup, that line at top worked, thanks!



Jason


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


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


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.


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


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


@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



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


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!


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


Reply