Skip to content

Instantly share code, notes, and snippets.

@Smalls1652
Created March 15, 2021 20:09
Show Gist options
  • Save Smalls1652/93badf4256bf5ab4c92ee5384cb200a7 to your computer and use it in GitHub Desktop.
Save Smalls1652/93badf4256bf5ab4c92ee5384cb200a7 to your computer and use it in GitHub Desktop.
Download Windows Admin Center through PowerShell
<#PSScriptInfo
.VERSION 2021.03.00
.GUID 0bbe0f49-d3c4-4479-993f-0b7f824f9ace
.AUTHOR Tim Small
.COMPANYNAME Smalls.Online
.COPYRIGHT 2021
.TAGS
.LICENSEURI
.PROJECTURI
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
.PRIVATEDATA
#>
<#
.DESCRIPTION
Download Windows Admin Center through PowerShell.
#>
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$OutputDirectory
)
#Resolve the output directory provided and throw a terminating error if it can't be resolved.
$outputResolvedPath = (Resolve-Path -Path $OutputDirectory -ErrorAction "Stop").Path
#Define the generic 'aka.ms' URL for Windows Admin Center.
$wacGenericDownloadUrl = "https://aka.ms/WACDownload"
Write-Verbose "Getting the redirect URL from '$($wacGenericDownloadUrl)'."
#Need to create a HttpClient object with a 'ClientHandler' option for not allowing auto-redirect. This is to prevent the HttpClient from automatically resolving to the redirect-uri from the HTTP response.
$redirectHttpClient = [System.Net.Http.HttpClient]::new(
[System.Net.Http.HttpClientHandler]@{
"AllowAutoRedirect" = $false
},
$true
)
$redirectHttpClient.DefaultRequestHeaders.ConnectionClose = $true
#Send a HttpRequestMessage for the generic download URL, so we can grab what the redirect-uri is.
$redirectHttpReqMsg = [System.Net.Http.HttpRequestMessage]::new(
[System.Net.Http.HttpMethod]::Get,
$wacGenericDownloadUrl
)
$redirectHttpRsp = $redirectHttpClient.Send($redirectHttpReqMsg)
#Test to see if the 'Location' property exists in the header and that the StatusCode is set to 'Moved'.
$redirectLocation = $null
switch (($redirectHttpRsp.Headers.Contains("Location")) -and ($redirectHttpRsp.StatusCode -eq [System.Net.HttpStatusCode]::Moved)) {
$false {
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
[System.Exception]::new("The response was returned with either the header not containing the 'Location' property or the 'StatusCode' was not set to 'Moved'. See the target object in the error record."),
"FailedToParseHttpHeaders",
[System.Management.Automation.ErrorCategory]::InvalidData,
$redirectHttpRsp
)
)
}
Default {
$redirectLocation = $redirectHttpRsp.Headers.Location.ToString()
break
}
}
$redirectHttpClient.Dispose() #Dispose the HttpClient
Write-Verbose "Redirected URL is '$($redirectLocation)'."
#Perform a regex on the URL that we would have been redirected to. This is to get the filename automatically.
$redirectRegex = [System.Text.RegularExpressions.Regex]::new(
"(?>https|http):\/\/download\.microsoft\.com[a-zA-Z0-9\/-]+\/(?'fileName'WindowsAdminCenter\d{1,4}\.msi)"
)
#Test to make sure the regex match will be successful.
switch ($redirectRegex.IsMatch($redirectLocation)) {
$false {
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
[System.Exception]::new("The input URL, '$($redirectLocation)' failed the regex match."),
"FailedToGetFileNameFromUrl",
[System.Management.Automation.ErrorCategory]::ParserError,
$redirectLocation
)
)
}
}
#Run the match and get the matched string.
$redirectRegexMatch = $redirectRegex.Match($redirectLocation)
$downloadFileName = $redirectRegexMatch.Groups.Item('fileName').Value
#Join the path of the output directory and the file name it should be.
$downloadPath = Join-Path -Path $outputResolvedPath -ChildPath $downloadFileName
Write-Verbose "File name will be '$($downloadFileName)' and will be saved to '$($outputResolvedPath)'."
#Check to see if the file already exists and if the file, does prompt for overwrite.
Write-Verbose "Testing to see if the file already exists in '$($outputResolvedPath)'."
switch (Test-Path -Path $downloadPath) {
$true {
$overwriteFilePrompt = $PSCmdlet.ShouldContinue("File already exists", "Do you want to overwrite '$($downloadPath)'?")
switch ($overwriteFilePrompt) {
$false {
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
[System.Exception]::new("File already exists"),
"FileAlreadyExists",
[System.Management.Automation.ErrorCategory]::ResourceExists,
$downloadPath
)
)
}
Default {
Remove-Item -Path $downloadPath -Force
break
}
}
}
}
#Create an HttpClient for the download
$downloadHttpClient = [System.Net.Http.HttpClient]::new()
$downloadHttpReqMsg = [System.Net.Http.HttpRequestMessage]::new(
[System.Net.Http.HttpMethod]::Get,
$redirectLocation
)
#Download the file.
Write-Verbose "Downloading file."
$downloadHttpRsp = $downloadHttpClient.Send($downloadHttpReqMsg)
$installFileByteArray = $downloadHttpRsp.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
$downloadHttpClient.Dispose() #Dispose the HTTP client
$null = [System.IO.File]::WriteAllBytes($downloadPath, $installFileByteArray) #Write the byte array to the output path
$downloadedFile = Get-Item -Path $downloadPath
return $downloadedFile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment