###################################################################
UPDATE: 10.6.2024
Some customers want maybe know the link speed for download, but even for upload. The script has been extended with these information:
try {
#region Scrape the webpage to get the download link
function Get-SpeedTestDownloadLink {
$url = "https://www.speedtest.net/apps/cli"
$webContent = Invoke-WebRequest -Uri $url -UseBasicParsing
if ($webContent.Content -match 'href="(https://install\.speedtest\.net/app/cli/ookla-speedtest-[\d\.]+-win64\.zip)"') {
return $matches[1]
} else {
Write-Output "Unable to find the win64 zip download link."
return $null
}
}
#endregion
#region Download and extract the zip file
function Download-SpeedTestZip {
param (
[string]$downloadLink,
[string]$destination
)
Invoke-WebRequest -Uri $downloadLink -OutFile $destination -UseBasicParsing
}
function Extract-Zip {
param (
[string]$zipPath,
[string]$destination
)
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $destination)
}
#endregion
#region Run the speedtest executable
function Run-SpeedTest {
param (
[string]$executablePath,
[array]$arguments
)
# Check if '--accept-license' is already in arguments
if (-not ($arguments -contains "--accept-license")) {
$arguments += "--accept-license"
}
# Check if '--accept-gdpr' is already in arguments
if (-not ($arguments -contains "--accept-gdpr")) {
$arguments += "--accept-gdpr"
}
$Result = & $executablePath $arguments
return $Result
}
#endregion
#region Cleanup
function Remove-File {
param (
[string]$Path
)
try {
if (Test-Path -Path $Path) {
Remove-Item -Path $Path -Recurse -ErrorAction Stop
}
} catch {
Write-Debug "Unable to remove item: $_"
}
}
function Remove-Files {
param(
[string]$zipPath,
[string]$folderPath
)
Remove-File -Path $zipPath
Remove-File -Path $folderPath
}
#endregion
$tempFolder = $env:TEMP
$zipFilePath = Join-Path $tempFolder "speedtest-win64.zip"
$extractFolderPath = Join-Path $tempFolder "speedtest-win64"
Remove-Files -zipPath $zipFilePath -folderPath $extractFolderPath
$downloadLink = Get-SpeedTestDownloadLink
Write-SectionHeader "Downloading SpeedTest CLI"
Download-SpeedTestZip -downloadLink $downloadLink -destination $zipFilePath
Write-SectionHeader "Extracting Zip File"
Extract-Zip -zipPath $zipFilePath -destination $extractFolderPath
$executablePath = Join-Path $extractFolderPath "speedtest.exe"
Write-SectionHeader "Running SpeedTest"
$Result = Run-SpeedTest -executablePath $executablePath -arguments $ScriptArgs
$DownloadSpeed = [regex]::match(($Result | where-object { $_ -like "*Download:*" }).trim(), '[0-9]+\.?[0-9]*').value
$UploadSpeed = [regex]::match(($Result | where-object { $_ -like "*Upload:*" }).trim(), '[0-9]+\.?[0-9]*').value
#$ISP = ($Result | where-object { $_ -like "*ISP:*" }).trim().split(":")[1].trim()
#$server = ($Result | where-object { $_ -like "*Server:*" }).trim().split(":")[1].trim()
$SpeedTestURL = ($Result | where-object { $_ -like "*Result URL:*" }).trim().split(" ")[2].trim()
Write-DarkGrayLine
Write-Host -ForegroundColor Green "Download: $DownloadSpeed Mbps"
Write-Host -ForegroundColor Green "Upload: $UploadSpeed Mbps"
Write-Host -ForegroundColor Green "Result URL: $SpeedTestURL"
Write-SectionHeader "Cleaning up"
Remove-Files -zipPath $zipFilePath -folderPath $extractFolderPath
Write-SectionSuccess "Done"
} catch {
Write-Error "An error occurred: $_"
}
If you want, you can use the speed test script in a standalone mode as well –> https://speedtest.osdcloud.ch
###################################################################
Regardless of the specific OSD technique or platform you choose – whether it’s OSDCloud, ConfigMgr, or any other solution – one common desire remains: receiving notifications when the deployment process completes.
In my work, I frequently rely on the widely-used OSDCloud PowerShell module. This module provides a powerful set of tools for OSD tasks, including an intriguing function called Get-OSDGather
.
Let’s explore how this function can enhance your OSD workflows and keep you informed about deployment progress.
$module = Import-Module OSD -PassThru -ErrorAction Ignore
if (-not $module) {
Write-Host "Installing OSD module"
Install-Module OSD -Force | Out-Null
}
Import-Module OSD -Force | Out-Null
$OSDGatheringJSON = Get-OSDGather -Full | ConvertTo-Json
If you are using completely OSDCloud technique, the process creates a very informative JSON file on the system drive, which can be used to grab some timestamp information.
$JsonPath = "C:\OSDCloud\Logs\OSDCloud.json"
if (Test-Path $JsonPath){
$JSON= Get-Content -Path $JsonPath -Raw | ConvertFrom-Json
$WinPECompleted = "$($JSON.TimeSpan.Minutes) minutes $($JSON.TimeSpan.Seconds) seconds"
$OSDEnd = Get-Date
$OSDCouldTime = New-TimeSpan -Start $JSON.TimeStart.DateTime -End $OSDEnd
$OSDCouldTimeCompleted = "$($OSDCouldTime.Hours) hour(s) $($OSDCouldTime.Minutes) minutes $($OSDCouldTime.Seconds) seconds"
}
The following values can be an example to receive the computer and OS information.
$ComputerName = $OSDGathering.OperatingSystem.CSName
$ComputerModel = $OSDGathering.ComputerSystemProduct.Name
$OS = $OSDGathering.OperatingSystem.Caption
$OSVersion = $OSDGathering.OperatingSystem.Version
$BiosSerialNumber = $OSDGathering.BIOS.SerialNumber
$BiosVersion = $OSDGathering.BIOS.SMBIOSBIOSVersion
$BiosReleaseDate = $OSDGathering.BIOS.ReleaseDate
$OSDCloudVersion = (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()
$IPAddress = (Get-WmiObject win32_Networkadapterconfiguration | Where-Object{ $_.ipaddress -notlike $null }).IPaddress | Select-Object -First 1
$Connection = Get-NetAdapter -physical | Where-Object status -eq 'up'
$ConnectionName = $connection.Name
$ConnectionDescription = $connection.InterfaceDescription
$LinkSpeed = $connection.LinkSpeed
$SSIDset = (Get-NetConnectionProfile).Name
These values can be then send via webhook to a predefined Teams channel:
$URI = 'XXXX'
$JSON = @{
"@type" = "MessageCard"
"@context" = "<http://schema.org/extensions>"
"title" = 'OSD Information'
"text" = "The following client has been successfully deployed:<br>
Computer Name: **$($ComputerName)**<br>
Model: **$($ComputerModel)**<br>
<br>
OS Version: **$($OS) $($OSVersion)**<br>
<br>
BIOS Serial Number: **$($BiosSerialNumber)**<br>
BIOS Version: **$($BiosVersion)**<br>
BIOS Release Date: **$($BiosReleaseDate)**<br>
<br>
Connection Type: **$($ConnectionName)**<br>
SSID: **$($SSIDset)**<br>
IP Address: **$($IPAddress)**<br>
Connection Description: **$($ConnectionDescription)**<br>
Link Speed: **$($LinkSpeed)**<br>
<br>
OSDCloud Version: **$($OSDCloudVersion)**<br>
WinPE Time Completed: **$($WinPECompleted)**<br>
OSDCloud Time Completed: **$($OSDCouldTimeCompleted)**<br>
<br>
"
} | ConvertTo-JSON
$Params = @{
"URI" = $URI
"Method" = 'POST'
"Body" = $JSON
"ContentType" = 'application/json'
}
Invoke-RestMethod @Params | Out-Null
This script can be executed in the OOBE phase or if you like, you can pack it into a Win32 app, and use it in the ESP phase.
The end result looks in the Teams channel like that:
Happy data collecting and sorting your relevant OSD information!