Guest User

Untitled

a guest
Sep 4th, 2018
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.76 KB | None | 0 0
  1. # Dependencies
  2. # PoshRSJob - Install with : Install-module poshrsjob -force
  3. # Microsoft deployment tool kit module.
  4. # Newest dism toolkit
  5.  
  6. Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1"
  7. $env:path = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
  8. import-module "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"
  9.  
  10. function Get-LatestUpdate {
  11. <#
  12. .SYNOPSIS
  13. Get the latest Cumulative update for Windows
  14.  
  15. .DESCRIPTION
  16. 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.
  17.  
  18. .NOTES
  19. Name: Get-LatestUpdate
  20. Author: Aaron Parker
  21. Twitter: @stealthpuppy
  22.  
  23. Original script: Copyright Keith Garner, All rights reserved.
  24. Forked from: https://gist.github.com/keithga/1ad0abd1f7ba6e2f8aff63d94ab03048
  25.  
  26. .LINK
  27. https://support.microsoft.com/en-us/help/4043454
  28.  
  29. .EXAMPLE
  30. Get-LatestUpdate
  31.  
  32. Description:
  33. Get the latest Cumulative Update for Windows 10 x64
  34.  
  35. .PARAMETER SearchString
  36. Specify a specific search string to change the target update behaviour. The default will only download Cumulative updates for x64.
  37.  
  38. .EXAMPLE
  39. Get-LatestUpdate -SearchString 'Cumulative.*x86'
  40.  
  41. Description:
  42. Enumerate the latest Cumulative Update for Windows 10 x86 (Semi-Annual Channel)
  43.  
  44. .EXAMPLE
  45. Get-LatestUpdate.ps1 -SearchString 'Cumulative.*Server.*x64' -Build 14393
  46.  
  47. Description:
  48. Enumerate the latest Cumulative Update for Windows Server 2016
  49. #>
  50. [CmdletBinding()]
  51. Param(
  52. [Parameter(Mandatory = $False, HelpMessage = "JSON source for the update KB articles.")]
  53. [string] $StartKB = 'https://support.microsoft.com/app/content/api/content/asset/en-us/4000816',
  54.  
  55. [Parameter(Mandatory = $False, Position = 0, HelpMessage = "Windows build number.")]
  56. [ValidateSet('17134', '16299', '15063', '14393', '10586', '10240')]
  57. [string] $BUild = '16299',
  58.  
  59. [Parameter(Mandatory = $False, Position = 1, HelpMessage = "Windows update Catalog Search Filter.")]
  60. [ValidateSet('x64', 'x86', 'Cumulative', 'Delta', $null)]
  61. [string[]] $Filter = @( "Cumulative","x64" )
  62.  
  63. )
  64.  
  65. #region Support Routine
  66.  
  67. Function Select-LatestUpdate {
  68. [CmdletBinding(SupportsShouldProcess = $True)]
  69. Param(
  70. [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
  71. $Updates
  72. )
  73. Begin {
  74. $MaxObject = $null
  75. $MaxValue = [version]::new("0.0")
  76. }
  77. Process {
  78. ForEach ( $Update in $updates ) {
  79. Select-String -InputObject $Update -AllMatches -Pattern "(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)" |
  80. ForEach-Object { $_.matches.value } |
  81. ForEach-Object { $_ -as [version] } |
  82. ForEach-Object {
  83. if ( $_ -gt $MaxValue ) { $MaxObject = $Update; $MaxValue = $_ }
  84. }
  85. }
  86. }
  87. End {
  88. $MaxObject | Write-Output
  89. }
  90. }
  91.  
  92. #endregion
  93.  
  94. #region Find the KB Article Number
  95.  
  96. Write-Verbose "Downloading $StartKB to retrieve the list of updates."
  97. $kbID = Invoke-WebRequest -Uri $StartKB |
  98. Select-Object -ExpandProperty Content |
  99. ConvertFrom-Json |
  100. Select-Object -ExpandProperty Links |
  101. Where-Object level -eq 2 |
  102. Where-Object text -match $BUild |
  103. Select-LatestUpdate |
  104. Select-Object -First 1
  105.  
  106. #endregion
  107.  
  108. #region get the download link from Windows Update
  109.  
  110. Write-Verbose "Found ID: KB$($kbID.articleID)"
  111. $kbObj = Invoke-WebRequest -Uri "http://www.catalog.update.microsoft.com/Search.aspx?q=KB$($KBID.articleID)"
  112.  
  113. $Available_KBIDs = $kbObj.InputFields |
  114. Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } |
  115. Select-Object -ExpandProperty ID
  116.  
  117. $Available_KBIDs | out-string | write-verbose
  118.  
  119. $kbGUIDs = $kbObj.Links |
  120. Where-Object ID -match '_link' |
  121. Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } |
  122. ForEach-Object { $_.id.replace('_link', '') } |
  123. Where-Object { $_ -in $Available_KBIDs }
  124.  
  125. foreach ( $kbGUID in $kbGUIDs ) {
  126. Write-Verbose "`t`tDownload $kbGUID"
  127. $Post = @{ size = 0; updateID = $kbGUID; uidInfo = $kbGUID } | ConvertTo-Json -Compress
  128. $PostBody = @{ updateIDs = "[$Post]" }
  129. Invoke-WebRequest -Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $postBody |
  130. Select-Object -ExpandProperty Content |
  131. Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" |
  132. Select-Object -Unique |
  133. ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } # Output for BITS
  134. }
  135. }
  136.  
  137. Function Update-WindowsImageFile {
  138. [cmdletbinding()]
  139. param(
  140. # Path to the extracet install.wim
  141. [Parameter(Mandatory = $true)]
  142. [string]$Path,
  143. # Temp folder to hold the install.wim
  144. [Parameter(Mandatory = $true)]
  145. [string]$MountPath,
  146.  
  147. [string]$UpdateDirectory = "C:\windows\SoftwareDistribution\Download\",
  148.  
  149. [Parameter(Mandatory = $true)]
  150. [int]$ImageIndex
  151.  
  152. )
  153.  
  154. $WimPath = Resolve-Path $Path
  155. $MountPath = Resolve-Path $MountPath
  156. $UpdatePath = Resolve-Path $UpdateDirectory
  157.  
  158. Mount-WindowsImage -Path $MountPath -Index $ImageIndex -ImagePath $WimPath
  159.  
  160. Get-ChildItem -Path $UpdatePath -Include *.msu, *.cab -Recurse | ForEach-Object {
  161. $FileName = $_.BaseName
  162.  
  163. try {
  164. #Add-WindowsPackage -PackagePath $_.FullName -Path $MountPath
  165. Dism.exe /Image:$MountPath /Add-Package "/PackagePath:$($_.FullName)"
  166. } catch {
  167. Write-Error "Unable to install $Filename"
  168. }
  169. }
  170.  
  171. Dismount-WindowsImage -Path $MountPath -Save
  172.  
  173. }
  174.  
  175. function Create-TempWorkSpace {
  176. [CmdletBinding()]
  177. param(
  178. [Parameter(Mandatory = $true)]
  179. [string]$Path
  180. )
  181.  
  182. try
  183. {
  184.  
  185. $guid = [system.guid]::newguid().tostring().split('-')[0]
  186. New-Item -ItemType Directory -Path "$Path\$guid" -Force
  187. New-Item -ItemType Directory -Path "$Path\$guid\Updates" -Force
  188. New-Item -ItemType Directory -Path "$Path\$guid\OfflineMount" -Force
  189. New-Item -ItemType Directory -Path "$Path\$guid\WindowsImage" -Force
  190. New-Item -ItemType Directory -Path "$Path\$guid\ISOTools" -Force
  191. New-Item -ItemType Directory -Path "$Path\$guid\ExtractedISO" -Force
  192. }
  193. catch
  194. {
  195. Write-Error "Cant create folders" -Verbose
  196. }
  197. }
  198.  
  199. function Set-Readonly {
  200. [CmdletBinding()]
  201. param(
  202. [Parameter(Mandatory = $true)]
  203. [string]$Path,
  204.  
  205. [Parameter(Mandatory = $true)]
  206. [bool]$ReadOnly
  207. )
  208.  
  209. Get-Childitem -Recurse -Path $Path | Where-Object Psiscontainer -eq $false | Set-ItemProperty -Name IsReadOnly -Value $ReadOnly
  210. }
  211.  
  212. function Get-OSGUID {
  213. [CmdletBinding()]
  214. param(
  215. [Parameter(Mandatory = $true)]
  216. [string]$Deploymentshare,
  217.  
  218. [Parameter(Mandatory = $true)]
  219. [string]$OSMDTName,
  220.  
  221. [Parameter(Mandatory = $true)]
  222. [string]$OSIndexName
  223. )
  224.  
  225. $OSXML = [xml](Get-Content "$Deploymentshare\Control\OperatingSystems.xml")
  226. $OSGuid = [string]($OSXML.oss.os | Where-Object -Property name -like "$OSIndexName in $OSMDTName install.wim" | select -expandProperty guid)
  227. $OSGuid
  228. }
  229.  
  230. function Update-TSOS {
  231. [CmdletBinding()]
  232. param(
  233. [Parameter(Mandatory = $true)]
  234. [string]$Deploymentshare,
  235.  
  236. [Parameter(Mandatory = $true)]
  237. [string]$TSName,
  238.  
  239. [Parameter(Mandatory = $true)]
  240. [string]$OSGuid
  241. )
  242.  
  243. $TSPath = "$Deploymentshare\Control\$TSName\ts.xml"
  244. $TSXML = [xml](Get-Content $TSPath)
  245. $TSXML.sequence.globalVarList.variable | Where {$_.name -eq "OSGUID"} | ForEach-Object {$_."#text" = $OSGuid}
  246. $TSXML.sequence.group | Where {$_.Name -eq "Install"} | ForEach-Object {$_.step} | Where {
  247. $_.Name -eq "Install Operating System"} | ForEach-Object {$_.defaultVarList.variable} | Where {
  248. $_.name -eq "OSGUID"} | ForEach-Object {$_."#text" = $OSGuid}
  249. $TSXML.Save($TSPath)
  250.  
  251. }
  252.  
  253. function Remove-PressToBoot{
  254. [CmdletBinding()]
  255. param(
  256. [Parameter(Mandatory = $true)]
  257. [string]$Deploymentshare,
  258. [Parameter(Mandatory = $true)]
  259. [string]$ExtractedISOPath,
  260. [Parameter(Mandatory = $true)]
  261. [string]$BootISO,
  262. [Parameter(Mandatory = $true)]
  263. [string]$NewBootISO,
  264. [Parameter(Mandatory = $true)]
  265. [string]$DeploymentToolKit,
  266. [Parameter(Mandatory = $true)]
  267. [string]$ISOToolsPath
  268. )
  269.  
  270. Copy-Item -Path "$DeploymentToolKit\*" -Destination $ISOToolsPath -Recurse -Force
  271. $setfsboot = "$ISOToolsPath\etfsboot.com"
  272. $sefisys = "$ISOToolsPath\efisys_noprompt.bin"
  273. $soscdimg = "$ISOToolsPath\oscdimg.exe"
  274.  
  275. $Mount = Mount-DiskImage -ImagePath $BootISO -PassThru
  276.  
  277. # get the drive letter assigned to the iso.
  278. $Drive = ($Mount | Get-Volume).DriveLetter + ':\*'
  279.  
  280. # copy the existing iso to the temporary folder.
  281. Copy-Item -Path $Drive -Destination $ExtractedISOPath -Force -Recurse
  282.  
  283. # remove the read-only attribute from the extracted files.
  284. Get-ChildItem $ExtractedISOPath -Recurse | Where-Object { if (! $_.psiscontainer) { $_.isreadonly = $false } }
  285.  
  286. #Copy-Item -Path $DeploymentToolKit -Destination $ISOPath -Force
  287.  
  288. # Create a bootable WinPE ISO file (remove the "Press any button key.." message)
  289. Copy-Item -Path $setfsboot -Destination "$ExtractedISOPath\boot" –Recurse -Force
  290. Copy-Item -Path $sefisys -Destination "$ExtractedISOPath\EFI\Microsoft\Boot" –Recurse -Force
  291.  
  292. # recompile the files to an ISO
  293. # This is the part from Johan Arwidmark's WinPE creation script:
  294. $Proc = $null
  295. $Proc = Start-Process -FilePath "$soscdimg" "-o -u2 -udfver102 -bootdata:2#p0,eb$setfsboot#pEF,e,b$sefisys $ExtractedISOPath $NewBootISO" -PassThru -Wait -NoNewWindow
  296.  
  297. if($Proc.ExitCode -ne 0)
  298. {
  299.      Throw "Failed to generate ISO with exitcode: $($Proc.ExitCode)"
  300. }
  301.  
  302. # remove the extracted content.
  303. #Remove-Item $WorkSpace -Recurse -Force
  304.  
  305. # dismount the iso.
  306. Dismount-DiskImage -ImagePath "$BootISO"
  307. }
  308.  
  309.  
  310.  
  311. # User Variable -------------------------------------------------------------
  312.  
  313. # The path to unpacked Source files
  314. $FullSetSourceFiles = 'E:\'
  315. # Temporary Files
  316. $TemporaryFolder = 'C:\temp'
  317. # Build nr. Options are: '17134', '16299', '15063', '14393', '10586', '10240'
  318. $OSVersion = '17134'
  319. # Windows Versions like "Windows 10 Pro" or "Windows 10 Enterprise"
  320. $OSIndexName = 'Windows 10 Pro'
  321. # OS name in MDT - It will import with os when the name below and a sufix of the date.
  322. $OSMDTName = "1803WithUpdates" # 1803WithUpdates-26-05-2018
  323. # Deploymentshare Path
  324. $Deploymentshare = 'D:\TestShare'
  325. # Task Sequences Name
  326. $TSName = 'TestBuild'
  327. # Where youre deployment tool kit is installed.
  328. $DeploymentToolKit = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg'
  329. # Boot ISO
  330. $BootISO = 'D:\TestShare\Boot\LiteTouchPE_x64.iso'
  331. # 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
  332. $NewBootISO = 'D:\TestShare\Boot\LiteTouchPE_x64-NEW.iso'
  333. # Log File
  334. $Log = 'D:\log.txt'
  335.  
  336. # VM VARIABLES --------------------------------------------------------------
  337. # Network path to your Hyperviser to store your boot iso.
  338. $RemotePath = '\\COMPUTERNAME\C$\temp'
  339. # Remote Hyperviser host
  340. $RemoteVMHost = 'COMPUTERNAME'
  341. # VM Name
  342. $VMName = 'Test VM'
  343. # Switch with access to your deploymentshare
  344. $SwitchName = 'External'
  345. # Path to VM on the hyperviser
  346. $VMPath = 'C:\temp'
  347. # Path to the boot iso on the Hyperviser
  348. $RemoteBootISOPath = 'C:\temp\LiteTouchPE_x64-NEW.iso'
  349.  
  350.  
  351. # E-Mail Varible
  352.  
  353. $SMTP = 'SMTP.MYDOMAIN.Com'
  354. $Subject= 'Check if youre VM with the newly update images is ok'
  355.  
  356.  
  357. # End User Variable ---------------------------------------------------------
  358.  
  359. Start-Transcript $Log
  360.  
  361. try
  362. {
  363. If (!(Test-Path $FullSetSourceFiles)){
  364. Write-Verbose "Inset the dvd drive" -Verbose
  365. break
  366. }
  367.  
  368.  
  369.  
  370. Write-Verbose -Message 'Creating the temporary workspace' -Verbose
  371. $Temp = Create-TempWorkSpace -Path $TemporaryFolder
  372. $ImagePath = $temp | Where-Object Fullname -Like "*WindowsImage" | select -ExpandProperty fullname
  373. $UpdatePath = $temp | Where-Object Fullname -Like "*Updates" | select -ExpandProperty fullname
  374. $OfflineMountPath = $temp | Where-Object Fullname -Like "*OfflineMount" | select -ExpandProperty fullname
  375. $ISOToolsPath = $temp | Where-Object Fullname -Like "*ISOTools" | select -ExpandProperty fullname
  376. $ExtractedISOPath = $temp | Where-Object Fullname -Like "*ExtractedISO" | select -ExpandProperty fullname
  377. $Date = Get-Date -Format d
  378.  
  379.  
  380. Write-Verbose -Message 'Copy Source files' -Verbose
  381. Start-RSJob -ScriptBlock {Copy-Item -Path $Using:FullSetSourceFiles\* -Destination $Using:ImagePath -Recurse}
  382.  
  383. Write-Verbose -Message 'Downloading the lateste KB package' -Verbose
  384. Start-RSJob -ScriptBlock {Get-LatestUpdate -BUild $Using:OSVersion -Filter 'Cumulative', 'x64' | Select-Object -First 1 | Start-BitsTransfer -Destination $Using:UpdatePath} -FunctionsToImport Get-LatestUpdate
  385.  
  386. Write-Verbose -Message 'Waiting on jobs to complete' -Verbose
  387. Get-RSJob | Wait-RSJob
  388.  
  389. Write-Verbose -Message "Remove ReadOnly flag" -Verbose
  390. Set-Readonly -Path $ImagePath -ReadOnly $false
  391.  
  392. Write-Verbose -Message 'Getting index number' -Verbose
  393. $Index = Get-WindowsImage -ImagePath "$ImagePath\sources\install.wim" | Where-Object ImageName -eq $OSIndexName | Select-Object -ExpandProperty ImageIndex -ErrorAction Stop
  394.  
  395. Write-Verbose -Message 'Updating the wim file' -Verbose
  396. Update-WindowsImageFile -Path "$ImagePath\sources\install.wim" -MountPath $OfflineMountPath -ImageIndex $Index -UpdateDirectory $UpdatePath
  397.  
  398. New-PSDrive -Name "DS002" -PSProvider MDTProvider -Root $Deploymentshare
  399. Import-MDTOperatingSystem -path "DS002:\Operating Systems" -SourcePath $ImagePath -DestinationFolder "$OSMDTName-$Date" -Verbose
  400.  
  401. # Import to prod share
  402. # New-PSDrive -Name "DS001" -PSProvider MDTProvider -Root D:\DeploymentShare
  403. # Import-MDTOperatingSystem -path "DS001:\Operating Systems" -SourcePath $ImagePath -DestinationFolder "$OSMDTName-$Date" -Verbose
  404.  
  405. $ListToRemove = Get-ChildItem -Path "DS002:\Operating Systems" | Where-Object -Property Name -NotLike "$OSIndexName in $OSMDTName-$Date install.wim" | select -ExpandProperty Name
  406. ForEach ($os in $ListToRemove){
  407. Remove-Item -Path "DS002:\Operating Systems\$os" -Force -Verbose
  408. }
  409.  
  410. Start-Sleep -Seconds 20
  411.  
  412. Write-Verbose -Message 'Getting the GUID of the newly importet os' -Verbose
  413. $OSGuid = Get-OSGUID -Deploymentshare $Deploymentshare -OSMDTName "$OSMDTName-$Date" -OSIndexName $OSIndexName
  414.  
  415. Write-Verbose -Message 'Adding the new OS to the task sequences' -Verbose
  416. Update-TSOS -Deploymentshare $Deploymentshare -TSName $TSName -OSGuid $OSGuid
  417.  
  418. Write-Verbose -Message 'Remove "Press to boot...:' -Verbose
  419. If (Test-Path $NewBootISO) {
  420. if ((Get-ItemProperty $BootISO).LastWriteTime -le (Get-ItemProperty $NewBootISO).LastWriteTime) {
  421. Write-Verbose -Message 'The new ISO is stil ok' -Verbose
  422. }
  423.  
  424. else {
  425. Write-Verbose -Message 'Creating new boot ISO' -Verbose
  426. Remove-Item $NewBootISO -Force -Confirm:$false
  427. Remove-PressToBoot -Deploymentshare $Deploymentshare -ExtractedISOPath $ExtractedISOPath -BootISO $BootISO -NewBootISO $NewBootISO -DeploymentToolKit $DeploymentToolKit -ISOToolsPath $ISOToolsPath
  428. Write-Verbose -Message 'Copy Boot ISO to the remote locationen' -Verbose
  429. Start-RSJob -ScriptBlock {Copy-Item -Path $Using:NewBootISO -Destination $Using:RemotePath}
  430. }
  431. }
  432. Else {
  433. Write-Verbose -Message 'Creating new boot ISO' -Verbose
  434. Remove-PressToBoot -Deploymentshare $Deploymentshare -ExtractedISOPath $ExtractedISOPath -BootISO $BootISO -NewBootISO $NewBootISO -DeploymentToolKit $DeploymentToolKit -ISOToolsPath $ISOToolsPath
  435. Write-Verbose -Message 'Copy Boot ISO to the remote locationen' -Verbose
  436. Start-RSJob -ScriptBlock {Copy-Item -Path $Using:NewBootISO -Destination $Using:RemotePath}
  437. }
  438.  
  439. # Now create the VM
  440.  
  441. Invoke-Command -ComputerName $RemoteVMHost -ScriptBlock {
  442. $VHDXPath = "$Using:VMPath\$Using:VMName\$UsingVMName.vhdx"
  443. $VHDX = New-VHD -Path $VHDXPath -SizeBytes 40gb -Verbose
  444. New-VM -VMName $Using:VMName -MemoryStartupBytes 2048mb -SwitchName $Using:SwitchName -Path $Using:VMPath -Generation 2 -VHDPath $VHDXPath -Verbose
  445. Set-VM -VMName $Using:VMName -ProcessorCount 2 -StaticMemory -Verbose
  446. $DVD = Add-VMDvdDrive -VMName $Using:VMName -Path $Using:RemoteBootISOPath -Verbose
  447. Set-VMFirmware -VMName $Using:VMName -FirstBootDevice $DVD -Verbose
  448. }
  449.  
  450. Write-Verbose -Message 'Waiting on jobs to complete' -Verbose
  451. Get-RSJob | Wait-RSJob
  452.  
  453. Invoke-Command -ComputerName $RemoteVMHost -ScriptBlock {
  454. Start-VM -VMName $Using:VMName
  455. }
  456.  
  457. }
  458. catch
  459. {
  460. $_.Exception.Message
  461. }
  462.  
  463. finally
  464. {
  465. Stop-Transcript
  466. $body = Get-Content -Path $log | Out-String
  467.  
  468. Send-MailMessage -Body $body -From $From -To $To -SmtpServer $SMTP -Subject $Subject
  469. }
Advertisement
Add Comment
Please, Sign In to add comment