Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Dependencies
- # PoshRSJob - Install with : Install-module poshrsjob -force
- # Microsoft deployment tool kit module.
- # Newest dism toolkit
- Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1"
- $env:path = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
- import-module "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
- function Get-LatestUpdate {
- <#
- .SYNOPSIS
- Get the latest Cumulative update for Windows
- .DESCRIPTION
- This script will return the list of Cumulative updates for Windows 10 and Windows Server 2016 from the Microsoft Update Catalog. Optionally download the updates using the -Download parameter.
- .NOTES
- Name: Get-LatestUpdate
- Author: Aaron Parker
- Twitter: @stealthpuppy
- Original script: Copyright Keith Garner, All rights reserved.
- Forked from: https://gist.github.com/keithga/1ad0abd1f7ba6e2f8aff63d94ab03048
- .LINK
- https://support.microsoft.com/en-us/help/4043454
- .EXAMPLE
- Get-LatestUpdate
- Description:
- Get the latest Cumulative Update for Windows 10 x64
- .PARAMETER SearchString
- Specify a specific search string to change the target update behaviour. The default will only download Cumulative updates for x64.
- .EXAMPLE
- Get-LatestUpdate -SearchString 'Cumulative.*x86'
- Description:
- Enumerate the latest Cumulative Update for Windows 10 x86 (Semi-Annual Channel)
- .EXAMPLE
- Get-LatestUpdate.ps1 -SearchString 'Cumulative.*Server.*x64' -Build 14393
- Description:
- Enumerate the latest Cumulative Update for Windows Server 2016
- #>
- [CmdletBinding()]
- Param(
- [Parameter(Mandatory = $False, HelpMessage = "JSON source for the update KB articles.")]
- [string] $StartKB = 'https://support.microsoft.com/app/content/api/content/asset/en-us/4000816',
- [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Windows build number.")]
- [ValidateSet('17134', '16299', '15063', '14393', '10586', '10240')]
- [string] $BUild = '16299',
- [Parameter(Mandatory = $False, Position = 1, HelpMessage = "Windows update Catalog Search Filter.")]
- [ValidateSet('x64', 'x86', 'Cumulative', 'Delta', $null)]
- [string[]] $Filter = @( "Cumulative","x64" )
- )
- #region Support Routine
- Function Select-LatestUpdate {
- [CmdletBinding(SupportsShouldProcess = $True)]
- Param(
- [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
- $Updates
- )
- Begin {
- $MaxObject = $null
- $MaxValue = [version]::new("0.0")
- }
- Process {
- ForEach ( $Update in $updates ) {
- Select-String -InputObject $Update -AllMatches -Pattern "(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)" |
- ForEach-Object { $_.matches.value } |
- ForEach-Object { $_ -as [version] } |
- ForEach-Object {
- if ( $_ -gt $MaxValue ) { $MaxObject = $Update; $MaxValue = $_ }
- }
- }
- }
- End {
- $MaxObject | Write-Output
- }
- }
- #endregion
- #region Find the KB Article Number
- Write-Verbose "Downloading $StartKB to retrieve the list of updates."
- $kbID = Invoke-WebRequest -Uri $StartKB |
- Select-Object -ExpandProperty Content |
- ConvertFrom-Json |
- Select-Object -ExpandProperty Links |
- Where-Object level -eq 2 |
- Where-Object text -match $BUild |
- Select-LatestUpdate |
- Select-Object -First 1
- #endregion
- #region get the download link from Windows Update
- Write-Verbose "Found ID: KB$($kbID.articleID)"
- $kbObj = Invoke-WebRequest -Uri "http://www.catalog.update.microsoft.com/Search.aspx?q=KB$($KBID.articleID)"
- $Available_KBIDs = $kbObj.InputFields |
- Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } |
- Select-Object -ExpandProperty ID
- $Available_KBIDs | out-string | write-verbose
- $kbGUIDs = $kbObj.Links |
- Where-Object ID -match '_link' |
- Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } |
- ForEach-Object { $_.id.replace('_link', '') } |
- Where-Object { $_ -in $Available_KBIDs }
- foreach ( $kbGUID in $kbGUIDs ) {
- Write-Verbose "`t`tDownload $kbGUID"
- $Post = @{ size = 0; updateID = $kbGUID; uidInfo = $kbGUID } | ConvertTo-Json -Compress
- $PostBody = @{ updateIDs = "[$Post]" }
- Invoke-WebRequest -Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $postBody |
- Select-Object -ExpandProperty Content |
- Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" |
- Select-Object -Unique |
- ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } # Output for BITS
- }
- }
- Function Update-WindowsImageFile {
- [cmdletbinding()]
- param(
- # Path to the extracet install.wim
- [Parameter(Mandatory = $true)]
- [string]$Path,
- # Temp folder to hold the install.wim
- [Parameter(Mandatory = $true)]
- [string]$MountPath,
- [string]$UpdateDirectory = "C:\windows\SoftwareDistribution\Download\",
- [Parameter(Mandatory = $true)]
- [int]$ImageIndex
- )
- $WimPath = Resolve-Path $Path
- $MountPath = Resolve-Path $MountPath
- $UpdatePath = Resolve-Path $UpdateDirectory
- Mount-WindowsImage -Path $MountPath -Index $ImageIndex -ImagePath $WimPath
- Get-ChildItem -Path $UpdatePath -Include *.msu, *.cab -Recurse | ForEach-Object {
- $FileName = $_.BaseName
- try {
- #Add-WindowsPackage -PackagePath $_.FullName -Path $MountPath
- Dism.exe /Image:$MountPath /Add-Package "/PackagePath:$($_.FullName)"
- } catch {
- Write-Error "Unable to install $Filename"
- }
- }
- Dismount-WindowsImage -Path $MountPath -Save
- }
- function Create-TempWorkSpace {
- [CmdletBinding()]
- param(
- [Parameter(Mandatory = $true)]
- [string]$Path
- )
- try
- {
- $guid = [system.guid]::newguid().tostring().split('-')[0]
- New-Item -ItemType Directory -Path "$Path\$guid" -Force
- New-Item -ItemType Directory -Path "$Path\$guid\Updates" -Force
- New-Item -ItemType Directory -Path "$Path\$guid\OfflineMount" -Force
- New-Item -ItemType Directory -Path "$Path\$guid\WindowsImage" -Force
- New-Item -ItemType Directory -Path "$Path\$guid\ISOTools" -Force
- New-Item -ItemType Directory -Path "$Path\$guid\ExtractedISO" -Force
- }
- catch
- {
- Write-Error "Cant create folders" -Verbose
- }
- }
- function Set-Readonly {
- [CmdletBinding()]
- param(
- [Parameter(Mandatory = $true)]
- [string]$Path,
- [Parameter(Mandatory = $true)]
- [bool]$ReadOnly
- )
- Get-Childitem -Recurse -Path $Path | Where-Object Psiscontainer -eq $false | Set-ItemProperty -Name IsReadOnly -Value $ReadOnly
- }
- function Get-OSGUID {
- [CmdletBinding()]
- param(
- [Parameter(Mandatory = $true)]
- [string]$Deploymentshare,
- [Parameter(Mandatory = $true)]
- [string]$OSMDTName,
- [Parameter(Mandatory = $true)]
- [string]$OSIndexName
- )
- $OSXML = [xml](Get-Content "$Deploymentshare\Control\OperatingSystems.xml")
- $OSGuid = [string]($OSXML.oss.os | Where-Object -Property name -like "$OSIndexName in $OSMDTName install.wim" | select -expandProperty guid)
- $OSGuid
- }
- function Update-TSOS {
- [CmdletBinding()]
- param(
- [Parameter(Mandatory = $true)]
- [string]$Deploymentshare,
- [Parameter(Mandatory = $true)]
- [string]$TSName,
- [Parameter(Mandatory = $true)]
- [string]$OSGuid
- )
- $TSPath = "$Deploymentshare\Control\$TSName\ts.xml"
- $TSXML = [xml](Get-Content $TSPath)
- $TSXML.sequence.globalVarList.variable | Where {$_.name -eq "OSGUID"} | ForEach-Object {$_."#text" = $OSGuid}
- $TSXML.sequence.group | Where {$_.Name -eq "Install"} | ForEach-Object {$_.step} | Where {
- $_.Name -eq "Install Operating System"} | ForEach-Object {$_.defaultVarList.variable} | Where {
- $_.name -eq "OSGUID"} | ForEach-Object {$_."#text" = $OSGuid}
- $TSXML.Save($TSPath)
- }
- function Remove-PressToBoot{
- [CmdletBinding()]
- param(
- [Parameter(Mandatory = $true)]
- [string]$Deploymentshare,
- [Parameter(Mandatory = $true)]
- [string]$ExtractedISOPath,
- [Parameter(Mandatory = $true)]
- [string]$BootISO,
- [Parameter(Mandatory = $true)]
- [string]$NewBootISO,
- [Parameter(Mandatory = $true)]
- [string]$DeploymentToolKit,
- [Parameter(Mandatory = $true)]
- [string]$ISOToolsPath
- )
- Copy-Item -Path "$DeploymentToolKit\*" -Destination $ISOToolsPath -Recurse -Force
- $setfsboot = "$ISOToolsPath\etfsboot.com"
- $sefisys = "$ISOToolsPath\efisys_noprompt.bin"
- $soscdimg = "$ISOToolsPath\oscdimg.exe"
- $Mount = Mount-DiskImage -ImagePath $BootISO -PassThru
- # get the drive letter assigned to the iso.
- $Drive = ($Mount | Get-Volume).DriveLetter + ':\*'
- # copy the existing iso to the temporary folder.
- Copy-Item -Path $Drive -Destination $ExtractedISOPath -Force -Recurse
- # remove the read-only attribute from the extracted files.
- Get-ChildItem $ExtractedISOPath -Recurse | Where-Object { if (! $_.psiscontainer) { $_.isreadonly = $false } }
- #Copy-Item -Path $DeploymentToolKit -Destination $ISOPath -Force
- # Create a bootable WinPE ISO file (remove the "Press any button key.." message)
- Copy-Item -Path $setfsboot -Destination "$ExtractedISOPath\boot" –Recurse -Force
- Copy-Item -Path $sefisys -Destination "$ExtractedISOPath\EFI\Microsoft\Boot" –Recurse -Force
- # recompile the files to an ISO
- # This is the part from Johan Arwidmark's WinPE creation script:
- $Proc = $null
- $Proc = Start-Process -FilePath "$soscdimg" "-o -u2 -udfver102 -bootdata:2#p0,eb$setfsboot#pEF,e,b$sefisys $ExtractedISOPath $NewBootISO" -PassThru -Wait -NoNewWindow
- if($Proc.ExitCode -ne 0)
- {
- Throw "Failed to generate ISO with exitcode: $($Proc.ExitCode)"
- }
- # remove the extracted content.
- #Remove-Item $WorkSpace -Recurse -Force
- # dismount the iso.
- Dismount-DiskImage -ImagePath "$BootISO"
- }
- # User Variable -------------------------------------------------------------
- # The path to unpacked Source files
- $FullSetSourceFiles = 'E:\'
- # Temporary Files
- $TemporaryFolder = 'C:\temp'
- # Build nr. Options are: '17134', '16299', '15063', '14393', '10586', '10240'
- $OSVersion = '17134'
- # Windows Versions like "Windows 10 Pro" or "Windows 10 Enterprise"
- $OSIndexName = 'Windows 10 Pro'
- # OS name in MDT - It will import with os when the name below and a sufix of the date.
- $OSMDTName = "1803WithUpdates" # 1803WithUpdates-26-05-2018
- # Deploymentshare Path
- $Deploymentshare = 'D:\TestShare'
- # Task Sequences Name
- $TSName = 'TestBuild'
- # Where youre deployment tool kit is installed.
- $DeploymentToolKit = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg'
- # Boot ISO
- $BootISO = 'D:\TestShare\Boot\LiteTouchPE_x64.iso'
- # New Boot ISO # It will check if it exist and skip if it does. And also make a check to see if $BootISO is new then your $NewBootISO, if it is i will delete at create a new one
- $NewBootISO = 'D:\TestShare\Boot\LiteTouchPE_x64-NEW.iso'
- # Log File
- $Log = 'D:\log.txt'
- # VM VARIABLES --------------------------------------------------------------
- # Network path to your Hyperviser to store your boot iso.
- $RemotePath = '\\COMPUTERNAME\C$\temp'
- # Remote Hyperviser host
- $RemoteVMHost = 'COMPUTERNAME'
- # VM Name
- $VMName = 'Test VM'
- # Switch with access to your deploymentshare
- $SwitchName = 'External'
- # Path to VM on the hyperviser
- $VMPath = 'C:\temp'
- # Path to the boot iso on the Hyperviser
- $RemoteBootISOPath = 'C:\temp\LiteTouchPE_x64-NEW.iso'
- # E-Mail Varible
- $From = '[email protected]'
- $To = '[email protected]'
- $SMTP = 'SMTP.MYDOMAIN.Com'
- $Subject= 'Check if youre VM with the newly update images is ok'
- # End User Variable ---------------------------------------------------------
- Start-Transcript $Log
- try
- {
- If (!(Test-Path $FullSetSourceFiles)){
- Write-Verbose "Inset the dvd drive" -Verbose
- break
- }
- Write-Verbose -Message 'Creating the temporary workspace' -Verbose
- $Temp = Create-TempWorkSpace -Path $TemporaryFolder
- $ImagePath = $temp | Where-Object Fullname -Like "*WindowsImage" | select -ExpandProperty fullname
- $UpdatePath = $temp | Where-Object Fullname -Like "*Updates" | select -ExpandProperty fullname
- $OfflineMountPath = $temp | Where-Object Fullname -Like "*OfflineMount" | select -ExpandProperty fullname
- $ISOToolsPath = $temp | Where-Object Fullname -Like "*ISOTools" | select -ExpandProperty fullname
- $ExtractedISOPath = $temp | Where-Object Fullname -Like "*ExtractedISO" | select -ExpandProperty fullname
- $Date = Get-Date -Format d
- Write-Verbose -Message 'Copy Source files' -Verbose
- Start-RSJob -ScriptBlock {Copy-Item -Path $Using:FullSetSourceFiles\* -Destination $Using:ImagePath -Recurse}
- Write-Verbose -Message 'Downloading the lateste KB package' -Verbose
- Start-RSJob -ScriptBlock {Get-LatestUpdate -BUild $Using:OSVersion -Filter 'Cumulative', 'x64' | Select-Object -First 1 | Start-BitsTransfer -Destination $Using:UpdatePath} -FunctionsToImport Get-LatestUpdate
- Write-Verbose -Message 'Waiting on jobs to complete' -Verbose
- Get-RSJob | Wait-RSJob
- Write-Verbose -Message "Remove ReadOnly flag" -Verbose
- Set-Readonly -Path $ImagePath -ReadOnly $false
- Write-Verbose -Message 'Getting index number' -Verbose
- $Index = Get-WindowsImage -ImagePath "$ImagePath\sources\install.wim" | Where-Object ImageName -eq $OSIndexName | Select-Object -ExpandProperty ImageIndex -ErrorAction Stop
- Write-Verbose -Message 'Updating the wim file' -Verbose
- Update-WindowsImageFile -Path "$ImagePath\sources\install.wim" -MountPath $OfflineMountPath -ImageIndex $Index -UpdateDirectory $UpdatePath
- New-PSDrive -Name "DS002" -PSProvider MDTProvider -Root $Deploymentshare
- Import-MDTOperatingSystem -path "DS002:\Operating Systems" -SourcePath $ImagePath -DestinationFolder "$OSMDTName-$Date" -Verbose
- # Import to prod share
- # New-PSDrive -Name "DS001" -PSProvider MDTProvider -Root D:\DeploymentShare
- # Import-MDTOperatingSystem -path "DS001:\Operating Systems" -SourcePath $ImagePath -DestinationFolder "$OSMDTName-$Date" -Verbose
- $ListToRemove = Get-ChildItem -Path "DS002:\Operating Systems" | Where-Object -Property Name -NotLike "$OSIndexName in $OSMDTName-$Date install.wim" | select -ExpandProperty Name
- ForEach ($os in $ListToRemove){
- Remove-Item -Path "DS002:\Operating Systems\$os" -Force -Verbose
- }
- Start-Sleep -Seconds 20
- Write-Verbose -Message 'Getting the GUID of the newly importet os' -Verbose
- $OSGuid = Get-OSGUID -Deploymentshare $Deploymentshare -OSMDTName "$OSMDTName-$Date" -OSIndexName $OSIndexName
- Write-Verbose -Message 'Adding the new OS to the task sequences' -Verbose
- Update-TSOS -Deploymentshare $Deploymentshare -TSName $TSName -OSGuid $OSGuid
- Write-Verbose -Message 'Remove "Press to boot...:' -Verbose
- If (Test-Path $NewBootISO) {
- if ((Get-ItemProperty $BootISO).LastWriteTime -le (Get-ItemProperty $NewBootISO).LastWriteTime) {
- Write-Verbose -Message 'The new ISO is stil ok' -Verbose
- }
- else {
- Write-Verbose -Message 'Creating new boot ISO' -Verbose
- Remove-Item $NewBootISO -Force -Confirm:$false
- Remove-PressToBoot -Deploymentshare $Deploymentshare -ExtractedISOPath $ExtractedISOPath -BootISO $BootISO -NewBootISO $NewBootISO -DeploymentToolKit $DeploymentToolKit -ISOToolsPath $ISOToolsPath
- Write-Verbose -Message 'Copy Boot ISO to the remote locationen' -Verbose
- Start-RSJob -ScriptBlock {Copy-Item -Path $Using:NewBootISO -Destination $Using:RemotePath}
- }
- }
- Else {
- Write-Verbose -Message 'Creating new boot ISO' -Verbose
- Remove-PressToBoot -Deploymentshare $Deploymentshare -ExtractedISOPath $ExtractedISOPath -BootISO $BootISO -NewBootISO $NewBootISO -DeploymentToolKit $DeploymentToolKit -ISOToolsPath $ISOToolsPath
- Write-Verbose -Message 'Copy Boot ISO to the remote locationen' -Verbose
- Start-RSJob -ScriptBlock {Copy-Item -Path $Using:NewBootISO -Destination $Using:RemotePath}
- }
- # Now create the VM
- Invoke-Command -ComputerName $RemoteVMHost -ScriptBlock {
- $VHDXPath = "$Using:VMPath\$Using:VMName\$UsingVMName.vhdx"
- $VHDX = New-VHD -Path $VHDXPath -SizeBytes 40gb -Verbose
- New-VM -VMName $Using:VMName -MemoryStartupBytes 2048mb -SwitchName $Using:SwitchName -Path $Using:VMPath -Generation 2 -VHDPath $VHDXPath -Verbose
- Set-VM -VMName $Using:VMName -ProcessorCount 2 -StaticMemory -Verbose
- $DVD = Add-VMDvdDrive -VMName $Using:VMName -Path $Using:RemoteBootISOPath -Verbose
- Set-VMFirmware -VMName $Using:VMName -FirstBootDevice $DVD -Verbose
- }
- Write-Verbose -Message 'Waiting on jobs to complete' -Verbose
- Get-RSJob | Wait-RSJob
- Invoke-Command -ComputerName $RemoteVMHost -ScriptBlock {
- Start-VM -VMName $Using:VMName
- }
- }
- catch
- {
- $_.Exception.Message
- }
- finally
- {
- Stop-Transcript
- $body = Get-Content -Path $log | Out-String
- Send-MailMessage -Body $body -From $From -To $To -SmtpServer $SMTP -Subject $Subject
- }
Advertisement
Add Comment
Please, Sign In to add comment