Public/OSDCloud/OSDCloudRE.ps1
|
function Get-OSDCloudREAzureResources { <# .SYNOPSIS OSDCloudRE: Discovers Azure storage resources used for OSDCloudRE boot images. .DESCRIPTION OSDCloudRE: Queries Azure storage accounts tagged for OSDCloud, discovers BootImage containers, and stores matching ISO blob metadata in global variables for downstream OSDCloudRE workflows. .EXAMPLE Get-OSDCloudREAzureResources Connects to Azure and populates OSDCloudRE Azure storage and boot image variables. .NOTES Author: David Segura - Recast Software 2026-07-10 - Added in-function comment-based help block. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Green "Get-OSDCloudREAzureResources" if ($env:SystemDrive -eq 'X:') { $OSDCloudLogs = "$env:TEMP\osdcloud-logs" if (-not (Test-Path $OSDCloudLogs)) { New-Item $OSDCloudLogs -ItemType Directory -Force | Out-Null } } if ($Global:AzContext) { #Write-Host -ForegroundColor DarkGray 'Storage Accounts: $Global:AzStorageAccounts' $Global:AzStorageAccounts = Get-AzStorageAccount if ($OSDCloudLogs) { #Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $OSDCloudLogs\AzStorageAccounts.json" $Global:AzStorageAccounts | ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzStorageAccounts.json" -Encoding ascii -Width 2000 -Force } #Write-Host -ForegroundColor DarkGray 'OSDCloud Storage Accounts: $Global:AzOSDCloudStorageAccounts' $Global:AzOSDCloudStorageAccounts = Get-AzStorageAccount | Where-Object {$_.Tags.ContainsKey('OSDCloud')} #$Global:AzOSDCloudStorageAccounts = Get-AzResource -ResourceType 'Microsoft.Storage/storageAccounts' #$Global:AzOSDCloudStorageAccounts = Get-AzResource -ResourceType 'Microsoft.Storage/storageAccounts' | Where-Object {$_.Tags.ContainsKey('OSDCloud')} if ($OSDCloudLogs) { #Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $OSDCloudLogs\AzOSDCloudStorageAccounts.json" $Global:AzOSDCloudStorageAccounts | ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzOSDCloudStorageAccounts.json" -Encoding ascii -Width 2000 -Force } $Global:AzStorageContext = @{} $Global:AzOSDCloudBlobBootImage = @() $Global:AzOSDCloudBootImage = @() if ($Global:AzOSDCloudStorageAccounts) { #Write-Host -ForegroundColor DarkGray 'Storage Contexts: $Global:AzStorageContext' #Write-Host -ForegroundColor DarkGray 'Blob Windows Images: $Global:AzOSDCloudBlobImage' #Write-Host '' Write-Host -ForegroundColor Cyan "Searching Azure Storage for OSDCloudRE Resources" foreach ($Item in $Global:AzOSDCloudStorageAccounts) { $Global:AzCurrentStorageContext = New-AzStorageContext -StorageAccountName $Item.StorageAccountName $Global:AzStorageContext."$($Item.StorageAccountName)" = $Global:AzCurrentStorageContext #Get-AzStorageBlobByTag -TagFilterSqlExpression ""osdcloudimage""=""win10ltsc"" -Context $StorageContext #Get-AzStorageBlobByTag -Context $Global:AzCurrentStorageContext $AzOSDCloudStorageContainers = Get-AzStorageContainer -Context $Global:AzCurrentStorageContext if ($OSDCloudLogs) { #Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $OSDCloudLogs\AzOSDCloudStorageContainers.json" $Global:AzOSDCloudStorageContainers | ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzOSDCloudStorageContainers.json" -Encoding ascii -Width 2000 -Force } if ($AzOSDCloudStorageContainers) { foreach ($Container in $AzOSDCloudStorageContainers) { if ($Container.Name -eq 'BootImage') { Write-Host -ForegroundColor DarkGray "BootImage Container: $($Item.StorageAccountName)/$($Container.Name)" $Global:AzOSDCloudBlobBootImage += Get-AzStorageBlob -Context $Global:AzCurrentStorageContext -Container $Container.Name -Blob *.iso -ErrorAction Ignore } } } } if ($OSDCloudLogs) { $Global:AzStorageContext | ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzStorageContext.json" -Encoding ascii -Width 2000 -Force $Global:AzOSDCloudBlobBootImage| ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzOSDCloudBlobDriverPack.json" -Encoding ascii -Width 2000 -Force } if ($null -eq $Global:AzOSDCloudBlobBootImage) { Write-Warning 'Unable to find a Boot Image on any of the OSDCloud Azure Storage Containers' Write-Warning 'Make sure you have a ISO Boot Image in the OSDCloud Azure Storage Container named BootImage' Write-Warning 'Make sure this user has the Azure Storage Blob Data Reader role to the OSDCloud Container' Write-Warning 'You may need to execute Get-OSDCloudAzureResources then Start-OSDCloudAzure' Break } } else { Write-Warning 'Unable to find any Azure Storage Accounts' Write-Warning 'Make sure the OSDCloud Azure Storage Account has an OSDCloud Tag' Write-Warning 'Make sure this user has the Azure Reader role on the OSDCloud Azure Storage Account' Break } } else { Write-Warning 'Unable to connect to AzureAD' Write-Warning 'You may need to execute Connect-OSDCloudAzure then Start-OSDCloudAzure' Break } } function Get-OSDCloudREPartition { <# .SYNOPSIS OSDCloudRE: Gets the OSDCloudRE Partition object .DESCRIPTION OSDCloudRE: Gets the OSDCloudRE Partition object .EXAMPLE Get-OSDCloudREPartition .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([Microsoft.Management.Infrastructure.CimInstance])] [OutputType('Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Partition')] param () Write-Verbose $MyInvocation.MyCommand Get-OSDCloudREVolume | Get-Partition } function Get-OSDCloudREPSDrive { <# .SYNOPSIS OSDCloudRE: Gets the OSDCloudRE PSDrive object .DESCRIPTION OSDCloudRE: Gets the OSDCloudRE PSDrive object .EXAMPLE Get-OSDCloudREPSDrive .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([System.Management.Automation.PSDriveInfo])] param () Write-Verbose $MyInvocation.MyCommand Get-PSDrive | Where-Object {$_.Description -eq 'OSDCloudRE'} } function Get-OSDCloudREVolume { <# .SYNOPSIS OSDCloudRE: Gets the OSDCloudRE Volume object .DESCRIPTION OSDCloudRE: Gets the OSDCloudRE Volume object .EXAMPLE Get-OSDCloudREVolume .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([Microsoft.Management.Infrastructure.CimInstance])] [OutputType('Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Volume')] param () Write-Verbose $MyInvocation.MyCommand Get-Volume | Where-Object {$_.FileSystemLabel -match 'OSDCloudRE'} } function Hide-OSDCloudREDrive { <# .SYNOPSIS OSDCloudRE: Hides the OSDCloudRE Drive .DESCRIPTION OSDCloudRE: Hides the OSDCloudRE Drive .EXAMPLE Hide-OSDCloudREDrive .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([System.Void])] param () Write-Verbose $MyInvocation.MyCommand $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } $OSDCloudREPartition = Get-OSDCloudREPartition if ($OSDCloudREPartition) { $null = @" select disk $($OSDCloudREPartition.DiskNumber) select partition $($OSDCloudREPartition.PartitionNumber) remove set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac" gpt attributes=0x8000000000000001 exit "@ | diskpart.exe } else { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloudRE partition" } } function Invoke-OSDCloudRE { <# .SYNOPSIS This is the master OSDCloudRE Task Sequence .DESCRIPTION This is the master OSDCloudRE Task Sequence .EXAMPLE Invoke-OSDCloudRE Runs the full OSDCloudRE provisioning sequence using current global configuration. .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES and EXAMPLE to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () #================================================= #region Master Parameters $Global:OSDCloudRE = $null $Global:OSDCloudRE = [ordered]@{ AzContext = $Global:AzContext AzOSDCloudBlobBootImage = $Global:AzOSDCloudBlobBootImage AzOSDCloudBootImage = $Global:AzOSDCloudBootImage AzStorageAccounts = $Global:AzStorageAccounts AzStorageContext = $Global:AzStorageContext BootImage = $null BuildName = 'OSDCloudRE' Debug = $false DownloadDirectory = $null DownloadName = $null DownloadFullName = $null Function = $MyInvocation.MyCommand.Name IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') IsOnBattery = $(Test-OSDIsOnBattery) IsVirtualMachine = $(Test-IsVM) MountDiskImage = $null MountDiskImageDriveLetter = $null MountDiskImagePath = $null PSDrive = $null PSDriveRoot = $null Restart = [bool]$false Shutdown = [bool]$false Test = [bool]$false TimeEnd = $null TimeSpan = $null TimeStart = Get-Date Transcript = $null Version = [Version](Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version Volume = $null Volumes = (Get-Volume).Where({$_.DriveLetter}).DriveLetter } #endregion #================================================= #region Merge Parameters if ($Global:StartOSDCloudRE) { foreach ($Key in $Global:StartOSDCloudRE.Keys) { $Global:OSDCloudRE.$Key = $Global:StartOSDCloudRE.$Key } } if ($Global:MyOSDCloudRE) { foreach ($Key in $Global:MyOSDCloudRE.Keys) { $Global:OSDCloudRE.$Key = $Global:MyOSDCloudRE.$Key } } #endregion #================================================= #region Set Post-Merge Defaults $Global:OSDCloudRE.Version = [Version](Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version if ($Global:OSDCloudRE.AzOSDCloudBootImage -eq $false) { Write-Warning "[$(Get-Date -format s)] A Boot Image is required to fun this function" Break } $Global:OSDCloudRE.DownloadDirectory = "$env:SystemDrive\OSDCloud\Azure\$($Global:OSDCloudRE.AzOSDCloudBootImage.BlobClient.AccountName)\$($Global:OSDCloudRE.AzOSDCloudBootImage.BlobClient.BlobContainerName)" $Global:OSDCloudRE.DownloadName = $(Split-Path $Global:OSDCloudRE.AzOSDCloudBootImage.Name -Leaf) $Global:OSDCloudRE.DownloadFullName = "$($Global:OSDCloudRE.DownloadDirectory)\$($Global:OSDCloudRE.DownloadName)" #endregion #================================================= #region Test Admin Rights Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Test Admin Rights" if ($Global:OSDCloudRE.IsAdmin -eq $false) { Write-Warning "[$(Get-Date -format s)] OSDCloudRE requires elevated Admin Rights" Break } #endregion #================================================= #region OSDCloudLogs if ($env:SystemDrive -eq 'X:') { $OSDCloudLogs = "$env:TEMP\osdcloud-logs" if (-not (Test-Path $OSDCloudLogs)) { New-Item $OSDCloudLogs -ItemType Directory -Force | Out-Null } } #endregion #================================================= #region Test PowerShell Execution Policy Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Test PowerShell Execution Policy" if ((Get-ExecutionPolicy) -ne 'RemoteSigned') { Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force } #endregion #================================================= #region OSD Module $InstallModule = $false $PSModuleName = 'OSD' $InstalledModule = Get-Module -Name $PSModuleName -ListAvailable -ErrorAction Ignore | Sort-Object Version -Descending | Select-Object -First 1 $GalleryPSModule = Find-Module -Name $PSModuleName -ErrorAction Ignore -WarningAction Ignore if ($GalleryPSModule) { if (($GalleryPSModule.Version -as [version]) -gt ($InstalledModule.Version -as [version])) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $PSModuleName $($GalleryPSModule.Version) [AllUsers]" Install-Module $PSModuleName -Scope AllUsers -Force -SkipPublisherCheck Import-Module $PSModuleName -Force } } #endregion #================================================= #region Final Warning Write-Warning "OSDCloudRE will be created in 10 seconds" Write-Warning "Press CTRL + C to cancel" Start-Sleep -Seconds 10 #endregion #================================================= #region Azure Storage Image Download if ($Global:OSDCloudRE.AzOSDCloudBootImage) { $GetAzStorageBlobContentParam = @{ CloudBlob = $Global:OSDCloudRE.AzOSDCloudBootImage.ICloudBlob Context = $Global:OSDCloudRE.AzOSDCloudBootImage.Context Destination = $Global:OSDCloudRE.DownloadFullName Force = $true } $NewItemParam = @{ Force = $true ItemType = 'Directory' Path = $Global:OSDCloudRE.DownloadDirectory } Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] OSDCloud Azure Storage Boot Image Download" $Global:OSDCloudRE.AzOSDCloudBootImage | ConvertTo-Json | Out-File -FilePath "$OSDCloudLogs\AzOSDCloudBootImage.json" -Encoding ascii -Width 2000 -Force if (Test-Path $Global:OSDCloudRE.DownloadFullName) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $($Global:OSDCloudRE.DownloadFullName) already exists" $Global:OSDCloudRE.BootImage = Get-Item -Path $Global:OSDCloudRE.DownloadFullName -ErrorAction Stop | Select-Object -First 1 | Select-Object -First 1 if ($Global:OSDCloudRE.AzOSDCloudBootImage.Length -eq ($Global:OSDCloudRE.BootImage.Length)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Destination file size matches Azure Storage, skipping previous download" } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Existing file does not match Azure Storage, downloading updated file" try { Get-AzStorageBlobContent @GetAzStorageBlobContentParam -ErrorAction Stop } catch { Get-AzStorageBlobContent @GetAzStorageBlobContentParam -ErrorAction Stop } } } else { if (-not (Test-Path "$($Global:OSDCloudRE.DownloadDirectory)")) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Creating directory $($Global:OSDCloudRE.DownloadDirectory)" $null = New-Item @NewItemParam -ErrorAction Ignore } try { Get-AzStorageBlobContent @GetAzStorageBlobContentParam -ErrorAction Stop } catch { Get-AzStorageBlobContent @GetAzStorageBlobContentParam -ErrorAction Stop } } $Global:OSDCloudRE.BootImage = Get-Item -Path $Global:OSDCloudRE.DownloadFullName -ErrorAction Stop | Select-Object -First 1 | Select-Object -First 1 } if (! $Global:OSDCloudRE.BootImage) { Break } #endregion #================================================= #region OSDCloudISO downloaded if ($Global:OSDCloudRE.BootImage -and $Global:OSDCloudRE.BootImage.Extension -eq '.iso') { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCloudISO downloaded to $($Global:OSDCloudRE.BootImage.FullName)" } else { Write-Warning "[$(Get-Date -format s)] Unable to download OSDCloudISO" Break } #endregion #============================================ #region Mounting OSDCloudISO Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Mounting OSDCloudISO" $Global:OSDCloudRE.MountDiskImage = Mount-DiskImage -ImagePath $Global:OSDCloudRE.BootImage.FullName Start-Sleep -Seconds 5 $Global:OSDCloudRE.MountDiskImageDriveLetter = (Compare-Object -ReferenceObject $Global:OSDCloudRE.Volumes -DifferenceObject (Get-Volume).Where({$_.DriveLetter}).DriveLetter).InputObject if ($Global:OSDCloudRE.MountDiskImageDriveLetter) { $Global:OSDCloudRE.MountDiskImagePath = "$($Global:OSDCloudRE.MountDiskImageDriveLetter):\" } else { Write-Warning "[$(Get-Date -format s)] Unable to mount $($Global:OSDCloudRE.BootImage.FullName)" Break } #endregion #============================================ #region Suspend BitLocker #https://docs.microsoft.com/en-us/windows/security/information-protection/bitlocker/bcd-settings-and-bitlocker $BitLockerVolumes = Get-BitLockerVolume | Where-Object {($_.ProtectionStatus -eq 'On') -and ($_.VolumeType -eq 'OperatingSystem')} -ErrorAction Ignore if ($BitLockerVolumes) { $BitLockerVolumes | Suspend-BitLocker -RebootCount 1 -ErrorAction Ignore if (Get-BitLockerVolume -MountPoint $BitLockerVolumes | Where-Object ProtectionStatus -eq "On") { Write-Warning "[$(Get-Date -format s)] Unable to suspend BitLocker for next boot" } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] BitLocker is suspended for the next boot" } } #endregion #============================================ #region Creating a new OSDCloudRE volume Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Creating a new OSDCloudRE volume" $Global:OSDCloudRE.Volume = New-OSDCloudREVolume -IsoSize $Global:OSDCloudRE.AzOSDCloudBootImage.Length -ErrorAction Stop #endregion #============================================ #region Test OSDCloudRE PSDrive Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Test OSDCloudRE PSDrive" $Global:OSDCloudRE.PSDrive = Get-OSDCloudREPSDrive if (! $Global:OSDCloudRE.PSDrive) { Write-Error "[$(Get-Date -format s)] Unable to find OSDCloudRE PSDrive" Break } #endregion #============================================ #region Test OSDCloudRE Root Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Test OSDCloudRE Root" $Global:OSDCloudRE.PSDriveRoot = ($Global:OSDCloudRE.PSDrive).Root if (-NOT (Test-Path $Global:OSDCloudRE.PSDriveRoot)) { Write-Error "[$(Get-Date -format s)] Unable to find OSDCloudRE Root at $($Global:OSDCloudRE.PSDriveRoot)" Break } #endregion #============================================ #region Update WinPE Volume if ((Test-Path -Path $Global:OSDCloudRE.MountDiskImagePath) -and (Test-Path -Path $Global:OSDCloudRE.PSDriveRoot)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $($Global:OSDCloudRE.MountDiskImagePath) to OSDCloud WinPE partition at $($Global:OSDCloudRE.PSDriveRoot)" $null = robocopy "$($Global:OSDCloudRE.MountDiskImagePath)" "$($Global:OSDCloudRE.PSDriveRoot)" *.* /e /ndl /njh /njs /np /r:0 /w:0 /b /zb } else { Write-Error "[$(Get-Date -format s)] Unable to copy Media to OSDCloudRE" Break } #endregion #============================================ #region Remove Read-Only Attribute Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Removing Read Only attributes in $($Global:OSDCloudRE.PSDriveRoot)" Get-ChildItem -Path $Global:OSDCloudRE.PSDriveRoot -File -Recurse -Force -ErrorAction Ignore | foreach { Set-ItemProperty -Path $_.FullName -Name IsReadOnly -Value $false -Force -ErrorAction Ignore } #endregion #============================================ #region Dismounting ISO if ($Global:OSDCloudRE.MountDiskImage.ImagePath) { Start-Sleep -Seconds 3 Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Dismounting ISO at $($Global:OSDCloudRE.MountDiskImage.ImagePath)" $null = Dismount-DiskImage -ImagePath $Global:OSDCloudRE.MountDiskImage.ImagePath } #endregion #============================================ #region Get-OSDCloudREVolume Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Testing OSDCloudRE Volume" if (! (Get-OSDCloudREVolume)) { Write-Warning "[$(Get-Date -format s)] Could not create OSDCloudRE" Break } #endregion #============================================ #region Set-OSDCloudREBCD Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Set OSDCloudRE Ramdisk: Set-OSDCloudREBootmgr -SetRamdisk" Set-OSDCloudREBootmgr -SetRamdisk Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Set OSDCloudRE OSLoader: Set-OSDCloudREBootmgr -SetOSloader" Set-OSDCloudREBootmgr -SetOSloader Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Hiding OSDCloudRE volume" Hide-OSDCloudREDrive Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Set OSDCloudRE to restart on next boot: Set-OSDCloudREBootmgr -BootToOSDCloudRE" Set-OSDCloudREBootmgr -BootToOSDCloudRE Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] OSDCloudRE setup is complete" if ($Global:OSDCloudRE.Restart) { Write-Warning "Windows is restarting in 10 seconds" Write-Warning "Press CTRL + C to cancel" Start-Sleep -Seconds 10 Restart-Computer -Force } #endregion #============================================ } function New-OSDCloudREVolume { <# .SYNOPSIS OSDCloudRE: Gets the OSDCloudRE Partition object .DESCRIPTION OSDCloudRE: Gets the OSDCloudRE Partition object .EXAMPLE New-OSDCloudREVolume .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([Microsoft.Management.Infrastructure.CimInstance])] [OutputType('Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Volume')] param ( [int32]$IsoSize = 1038090240 ) Write-Verbose $MyInvocation.MyCommand $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } $WindowsPartition = Get-Partition | Where-Object {$env:SystemDrive -match $_.DriveLetter} $WindowsDiskNumber = $WindowsPartition.DiskNumber $WindowsSizeMax = $WindowsPartition | Get-PartitionSupportedSize | Select-Object -ExpandProperty SizeMax $WindowsShrinkSize = $WindowsSizeMax - $IsoSize - 200MB $OSDCloudREVolume = Get-OSDCloudREVolume #============================================ # Test WindowsPartition #============================================ if ($WindowsPartition) { #============================================ # Test UEFI #============================================ if ((Get-OSDGather -Property IsUEFI)) { #============================================ # Test if OSDCloudRE already exists #============================================ if (! $OSDCloudREVolume) { #============================================ # Shrink Windows Partition #============================================ Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Shrinking Windows partition" $WindowsPartition | Resize-Partition -Size $WindowsShrinkSize #============================================ # Test WindowsPartition # Get Results #============================================ if ($WindowsPartition) { #============================================ # Create NewPartition #============================================ Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Creating OSDCloudRE Partition" $Global:NewPartition = New-Partition -DiskNumber $WindowsDiskNumber -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}' -UseMaximumSize #============================================ # Test NewPartition #============================================ if ($Global:NewPartition) { #============================================ # Test NewPartitionNumber #============================================ $Global:NewPartitionNumber = $Global:NewPartition.PartitionNumber #============================================ # Format Partition #============================================ if ($Global:NewPartitionNumber) { $Global:FormatVolume = Format-Volume -Partition $Global:NewPartition -FileSystem NTFS -NewFileSystemLabel 'OSDCloudRE' -Force $Global:PartitionAccessPath = Add-PartitionAccessPath -AccessPath O: -DiskNumber $Global:NewPartition.DiskNumber -PartitionNumber $Global:NewPartition.PartitionNumber Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Testing OSDCloudRE Volume" #============================================ # Return Results #============================================ if (Get-OSDCloudREVolume) { Get-OSDCloudREVolume } else { Write-Error "[$(Get-Date -format s)] Could not create OSDCloudRE volume" } } else { Write-Error "[$(Get-Date -format s)] Unable to get OSDCloudRE partition DiskNumber" } } else { Write-Error "[$(Get-Date -format s)] Unable to create an OSDCloudRE partition" } } else { Write-Error "[$(Get-Date -format s)] Unable to shink Windows partition" } } else { Write-Error "[$(Get-Date -format s)] Cannot create a second OSDCloudRE instance" } } else { Write-Error "[$(Get-Date -format s)] OSDCloudRE requires UEFI" } } else { Write-Error "[$(Get-Date -format s)] Unable to find the Windows Partition" } } function Set-OSDCloudREBootmgr { <# .SYNOPSIS OSDCloudRE: Configures OSDCloudRE Boot Manager options .DESCRIPTION OSDCloudRE: Configures OSDCloudRE Boot Manager options. Requires ADMIN righs .EXAMPLE Set-OSDCloudREBootmgr -SetRamdisk -SetOSloader Creates or updates the OSDCloudRE Ramdisk and OSLoader Requires boot content in O:\ .EXAMPLE Set-OSDCloudREBootmgr -OSMenuAdd Adds OSDCloudRE to the Boot Manager Operating System selection .EXAMPLE Set-OSDCloudREBootmgr -OSMenuRemove Removes OSDCloudRE from the Boot Manager Operating System selection .EXAMPLE Set-OSDCloudREBootmgr -BootToOSDCloudRE Boots to OSDCloudRE on the next reboot .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([System.Void])] param ( [System.Management.Automation.SwitchParameter] #Creates or updates the OSDCloudRE Ramdisk $SetRamdisk, [System.Management.Automation.SwitchParameter] #Creates or updates the OSDCloudRE OSLoader $SetOSloader, [System.Management.Automation.SwitchParameter] #Adds OSDCloudRE to the Boot Manager Operating System selection $OSMenuAdd, [System.Management.Automation.SwitchParameter] #Removes OSDCloudRE from the Boot Manager Operating System selection $OSMenuRemove, [System.Management.Automation.SwitchParameter] #Boots to OSDCloudRE on the next reboot $BootToOSDCloudRE ) Write-Verbose $MyInvocation.MyCommand $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } if ($SetRamdisk -or $SetOSloader) { $OSDCloudREPartition = Get-OSDCloudREPartition if (! $OSDCloudREPartition) { Write-Warning "[$(Get-Date -format s)] Unable to find OSDCloudRE Partition" } } if ($SetRamdisk) { if ($OSDCloudREPartition) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /create '{4f534452-616d-6469-736b-536567757261}' /d OSDRamdisk /device" $null = bcdedit /create '{4f534452-616d-6469-736b-536567757261}' /d "OSDRamdisk" /device Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534452-616d-6469-736b-536567757261}' ramdisksdidevice partition=O:" $null = bcdedit /set '{4f534452-616d-6469-736b-536567757261}' ramdisksdidevice partition=O: Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534452-616d-6469-736b-536567757261}' ramdisksdipath \boot\boot.sdi" $null = bcdedit /set '{4f534452-616d-6469-736b-536567757261}' ramdisksdipath \boot\boot.sdi } } if ($SetOSloader) { if ($OSDCloudREPartition) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /create '{4f534443-6c6f-7564-5245-536567757261}' /d OSDCloudRE /application osloader" $null = bcdedit /create '{4f534443-6c6f-7564-5245-536567757261}' /d "OSDCloudRE" /application osloader Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' device ramdisk=[O:]\sources\boot.wim,'{4f534452-616d-6469-736b-536567757261}'" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' device ramdisk=[O:]\sources\boot.wim,'{4f534452-616d-6469-736b-536567757261}' Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' osdevice ramdisk=[O:]\sources\boot.wim,'{4f534452-616d-6469-736b-536567757261}'" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' osdevice ramdisk=[O:]\sources\boot.wim,'{4f534452-616d-6469-736b-536567757261}' Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' path \windows\system32\winload.efi" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' path \windows\system32\winload.efi Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' systemroot \Windows" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' systemroot \Windows Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' detecthal Yes" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' detecthal Yes Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' winpe Yes" $null = bcdedit /set '{4f534443-6c6f-7564-5245-536567757261}' winpe Yes } } if ($OSMenuAdd) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /displayorder '{4f534443-6c6f-7564-5245-536567757261}' /addlast" $null = bcdedit /displayorder '{4f534443-6c6f-7564-5245-536567757261}' /addlast } if ($OSMenuRemove) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /displayorder '{4f534443-6c6f-7564-5245-536567757261}' /remove" $null = bcdedit /displayorder '{4f534443-6c6f-7564-5245-536567757261}' /remove } if ($BootToOSDCloudRE) { Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] bcdedit /bootsequence '{4f534443-6c6f-7564-5245-536567757261}'" try { $null = bcdedit /bootsequence '{4f534443-6c6f-7564-5245-536567757261}' Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloudRE set for next boot" } catch { Write-Warning "[$(Get-Date -format s)] OSDCloudRE could not be set for next boot" } } } function Show-OSDCloudREDrive { <# .SYNOPSIS OSDCloudRE: Shows the OSDCloudRE Drive .DESCRIPTION OSDCloudRE: Shows the OSDCloudRE Drive .EXAMPLE Show-OSDCloudREDrive .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] [OutputType([System.Void])] param () Write-Verbose $MyInvocation.MyCommand $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } $OSDCloudREPartition = Get-OSDCloudREPartition if ($OSDCloudREPartition) { $null = @" select disk $($OSDCloudREPartition.DiskNumber) select partition $($OSDCloudREPartition.PartitionNumber) set id="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" gpt attributes=0x0000000000000000 assign letter=o rescan exit "@ | diskpart.exe } else { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloudRE partition" } } function Start-OSDCloudREAzure { <# .SYNOPSIS OSDCloudRE: Creates a new OSDCloudRE Volume from Azure .DESCRIPTION OSDCloudRE: Creates a new OSDCloudRE Volume from Azure .EXAMPLE Start-OSDCloudREAzure .NOTES Author: David Segura - Recast Software 2026-07-10 - Added NOTES to align with OSD help standards. .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param ( [System.Management.Automation.SwitchParameter] #Clears previous variables $Force ) Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Green "Start-OSDCloudREAzure" if ($env:SystemDrive -ne 'X:') { if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { Invoke-Expression -Command (Invoke-RestMethod -Uri functions.osdcloud.com) Connect-OSDCloudAzure Get-OSDCloudREAzureResources if ($Global:AzOSDCloudBlobBootImage) { & "$($MyInvocation.MyCommand.Module.ModuleBase)\Projects\OSDCloudREAzure\MainWindow.ps1" Start-Sleep -Seconds 2 if ($Global:StartOSDCloudRE.AzOSDCloudBootImage) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Green "Invoke-OSDCloudRE" Invoke-OSDCloudRE } else { Write-Warning "Unable to get an ISO Boot Image from Start-OSDCloudREAzure" } } else { Write-Warning 'Start-OSDCloudREAzure could not find any Boot Images in Azure' Break } } else { Write-Warning 'Start-OSDCloudREAzure must be run with Admin Rights' Break } } else { Write-Warning "Start-OSDCloudREAzure must be run from Windows" Break } } |