Public/OSDCloud/OSDCloudWorkspace.ps1
|
function Get-OSDCloudWorkspace { <# .SYNOPSIS Gets information returned by Get-OSDCloudWorkspace. .DESCRIPTION Provides the implementation for Get-OSDCloudWorkspace. .EXAMPLE Get-OSDCloudWorkspace Runs Get-OSDCloudWorkspace with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () #region Block Block-WinPE Block-WindowsVersionNe10 Block-PowerShellVersionLt5 #endregion #region workspace.json if (Test-Path "$env:ProgramData\OSDCloud\workspace.json") { $WorkspaceSettings = Get-Content -Path "$env:ProgramData\OSDCloud\workspace.json" | ConvertFrom-Json $WorkspacePath = $WorkspaceSettings.WorkspacePath $WorkspacePath } else { Write-Warning "[$(Get-Date -format s)] Unable to locate $env:ProgramData\OSDCloud\workspace.json" } #endregion } function New-OSDCloudWorkspace { <# .SYNOPSIS Creates resources by using New-OSDCloudWorkspace. .DESCRIPTION Provides the implementation for New-OSDCloudWorkspace. .PARAMETER WorkspacePath Specifies the value for WorkspacePath. .PARAMETER fromIsoFile Specifies the value for fromIsoFile. .PARAMETER fromIsoUrl Specifies the value for fromIsoUrl. .PARAMETER fromUsbDrive Indicates whether to enable fromUsbDrive. .PARAMETER Public Indicates whether to enable Public. .EXAMPLE New-OSDCloudWorkspace -WorkspacePath <WorkspacePath> -fromIsoFile <fromIsoFile> Runs New-OSDCloudWorkspace with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding(DefaultParameterSetName='fromTemplate')] param ( [Parameter(ParameterSetName='fromTemplate',Position=0,ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName='fromIsoFile',Position=0,ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName='fromIsoUrl',Position=0,ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName='fromUsbDrive',Position=0,ValueFromPipelineByPropertyName)] [System.String] #Directory for the OSDCloud Workspace to create or update. Default is $env:SystemDrive\OSDCloud $WorkspacePath = "$env:SystemDrive\OSDCloud", [Parameter(ParameterSetName='fromIsoFile',Mandatory)] [System.IO.FileInfo] #Path to an OSDCloud ISO #This file will be mounted and the contents will be copied to the OSDCloud Workspace $fromIsoFile, [Parameter(ParameterSetName='fromIsoUrl',Mandatory)] [System.String] #Path to an OSDCloud ISO saved on the internet #This file will be downloaded and mounted and the contents will be copied to the OSDCloud Workspace $fromIsoUrl, [Parameter(ParameterSetName='fromUsbDrive',Mandatory)] [System.Management.Automation.SwitchParameter] #Searches for an OSDCloud USB #The OSDCloud USB contents will be copied to the OSDCloud Workspace $fromUsbDrive, [System.Management.Automation.SwitchParameter] #Prevents the copying of Private Config files $Public ) #region Block Block-NoCurl Block-PowerShellVersionLt5 $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 } Block-WindowsVersionNe10 Block-WinPE #endregion #region Preferences $ErrorActionPreference = 'Stop' $WinpeSourcePath = $null #endregion #region Initialize Workspace from Template if ($PSCmdlet.ParameterSetName -eq 'fromTemplate') { #================================================= # OSDCloudTemplate #================================================= $OSDCloudTemplate = Get-OSDCloudTemplate -ErrorAction Stop if (-NOT ($OSDCloudTemplate)) { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud Template at $OSDCloudTemplate" Break } if (-NOT (Test-Path $OSDCloudTemplate)) { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud Template at $OSDCloudTemplate" Break } #================================================= # Remove Old Autopilot Content #================================================= if (Test-Path "$(Get-OSDCloudTemplate)\Autopilot") { Write-Warning "[$(Get-Date -format s)] Move all your Autopilot Profiles to $(Get-OSDCloudTemplate)\Config\AutopilotJSON" Write-Warning "[$(Get-Date -format s)] You will be unable to create or update an OSDCloud Workspace until $(Get-OSDCloudTemplate)\Autopilot is manually removed" Break } if (Test-Path "$WorkspacePath\Autopilot") { Write-Warning "[$(Get-Date -format s)] Move all your Autopilot Profiles to $WorkspacePath\Config\AutopilotJSON" Write-Warning "[$(Get-Date -format s)] You will be unable to create or update an OSDCloud Workspace until $WorkspacePath\Autopilot is manually removed" Break } } #endregion #region Initialize Workspace fromIsoFile if ($PSCmdlet.ParameterSetName -eq 'fromIsoFile') { $fromIsoFileGetItem = Get-Item -Path $fromIsoFile -ErrorAction Ignore $fromIsoFileFullName = $fromIsoFileGetItem.FullName if ($fromIsoFileGetItem -and $fromIsoFileGetItem.Extension -eq '.iso') { #Do nothing } else { Write-Warning "[$(Get-Date -format s)] Unable to get the properties of $fromIsoFile" Write-Warning "[$(Get-Date -format s)] Something went very very wrong in this process" Break } #================================================= # Mount fromIsoFile #================================================= $Volumes = (Get-Volume).Where({$_.DriveLetter}).DriveLetter Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Mounting OSDCloudISO at $fromIsoFileFullName" $MountDiskImage = Mount-DiskImage -ImagePath $fromIsoFileFullName Start-Sleep -Seconds 3 $MountDiskImageDriveLetter = (Compare-Object -ReferenceObject $Volumes -DifferenceObject (Get-Volume).Where({$_.DriveLetter}).DriveLetter).InputObject if ($MountDiskImageDriveLetter) { $WinpeSourcePath = "$($MountDiskImageDriveLetter):\" } else { Write-Warning "[$(Get-Date -format s)] Unable to mount $MountDiskImage" Write-Warning "[$(Get-Date -format s)] Something went very very wrong in this process" Break } } #endregion #region Initialize Workspace fromIsoUrl if ($PSCmdlet.ParameterSetName -eq 'fromIsoUrl') { $ResolveUrl = Invoke-WebRequest -Uri $fromIsoUrl -Method Head -MaximumRedirection 0 -UseBasicParsing -ErrorAction SilentlyContinue if ($ResolveUrl.StatusCode -eq 302) { $fromIsoUrl = $ResolveUrl.Headers.Location } $fromIsoFileGetItem = Save-WebFile -SourceUrl $fromIsoUrl -DestinationDirectory (Join-Path $HOME 'Downloads') $fromIsoFileFullName = $fromIsoFileGetItem.FullName if ($fromIsoFileGetItem -and $fromIsoFileGetItem.Extension -eq '.iso') { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDCloudISO downloaded to $fromIsoFileFullName" } else { Write-Warning "[$(Get-Date -format s)] Unable to download OSDCloudISO" Write-Warning "[$(Get-Date -format s)] Something went very very wrong in this process" Break } #================================================= # Mount OSDCloudISO #================================================= $Volumes = (Get-Volume).Where({$_.DriveLetter}).DriveLetter Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Mounting OSDCloudISO" $MountDiskImage = Mount-DiskImage -ImagePath $fromIsoFileFullName Start-Sleep -Seconds 3 $MountDiskImageDriveLetter = (Compare-Object -ReferenceObject $Volumes -DifferenceObject (Get-Volume).Where({$_.DriveLetter}).DriveLetter).InputObject if ($MountDiskImageDriveLetter) { $WinpeSourcePath = "$($MountDiskImageDriveLetter):\" } else { Write-Warning "[$(Get-Date -format s)] Unable to mount $MountDiskImage" Write-Warning "[$(Get-Date -format s)] Something went very very wrong in this process" Break } } #endregion #region Initialize fromUsbDrive if ($PSCmdlet.ParameterSetName -eq 'fromUsbDrive') { #================================================= # USB Volumes #================================================= $UsbVolumes = Get-USBVolume if ($UsbVolumes) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] USB volumes found" } else { Write-Warning "[$(Get-Date -format s)] Unable to find any USB Volumes" Get-Help New-OSDCloudUSB -Examples Break } } #endregion #region Validate if (Test-Path $WorkspacePath) { Write-Warning "[$(Get-Date -format s)] OSDCloud Workspace already exists at $WorkspacePath" Write-Warning "[$(Get-Date -format s)] Content will be merged and overwritten" Write-Warning "[$(Get-Date -format s)] Press Ctrl+C to cancel in the next 5 seconds" Start-Sleep -Seconds 5 } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] New-Item $WorkspacePath" try { $null = New-Item -Path $WorkspacePath -ItemType Directory -Force -ErrorAction Stop } catch { Write-Warning "[$(Get-Date -format s)] Unable to create an OSDCloud Workspace at $WorkspacePath" Break } } #endregion #region Logs and Transcript $WorkspaceLogs = "$WorkspacePath\Logs\Workspace" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Creating OSDCloud Workspace Logs at $WorkspaceLogs" if (Test-Path $WorkspaceLogs) { $null = Remove-Item -Path "$WorkspaceLogs\*" -Recurse -Force -ErrorAction Ignore | Out-Null } if (-NOT (Test-Path $WorkspaceLogs)) { $null = New-Item -Path $WorkspaceLogs -ItemType Directory -Force | Out-Null } $Transcript = "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-New-OSDCloudWorkspace.log" $null = Start-Transcript -Path (Join-Path $WorkspaceLogs $Transcript) -ErrorAction Ignore #endregion #region Build Workspace if ($PSCmdlet.ParameterSetName -eq 'fromTemplate') { #================================================= # Copy WorkspacePath #================================================= Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying from OSDCloud Template at $OSDCloudTemplate" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Source: $OSDCloudTemplate" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Destination: $WorkspacePath" $null = robocopy "$OSDCloudTemplate" "$WorkspacePath" *.* /e /b /ndl /np /r:0 /w:0 /xj /xf workspace.json /LOG+:$WorkspaceLogs\Robocopy.log #================================================= # Mirror Media #================================================= Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Mirroring OSDCloud Template Media using Robocopy" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Mirroring will replace any previous WinPE with a new Template WinPE" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Directories named OSDCloud are updated" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Source: $OSDCloudTemplate\Media" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Destination: $WorkspacePath\Media" $null = robocopy "$OSDCloudTemplate\Media\OSDCloud" "$WorkspacePath\Media\OSDCloud" *.* /e /b /ndl /np /r:0 /w:0 /xj /LOG+:$WorkspaceLogs\Robocopy.log $null = robocopy "$OSDCloudTemplate\Media" "$WorkspacePath\Media" *.* /mir /b /ndl /np /r:0 /w:0 /xj /LOG+:$WorkspaceLogs\Robocopy.log /XD OSDCloud } elseif ($PSCmdlet.ParameterSetName -eq 'fromUsbDrive') { #================================================= # WinPE Volume #================================================= $WinpeVolumes = $UsbVolumes | Where-Object {($_.FileSystemLabel -eq 'USBBOOT') -or ($_.FileSystemLabel -eq 'OSDBOOT') -or ($_.FileSystemLabel -eq 'USB BOOT') -or ($_.FileSystemLabel -eq 'WinPE')} if ($WinpeVolumes) { foreach ($WinpeVolume in $WinpeVolumes) { if (Test-Path -Path "$($WinPEVolume.DriveLetter):\") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying OSDCloud WinPE volume at $($WinPEVolume.DriveLetter):\ to $WorkspacePath\Media" robocopy "$($WinPEVolume.DriveLetter):\" "$WorkspacePath\Media" *.* /e /ndl /njh /njs /np /r:0 /w:0 /xd "$RECYCLE.BIN" "System Volume Information" } } } else { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud USB WinPE volume" Break } $OSDCloudVolumes = Get-USBVolume | Where-Object {($_.FileSystemLabel -eq 'OSDCloud') -or ($_.FileSystemLabel -eq 'OSDCloudUSB')} if ($OSDCloudVolumes) { foreach ($OSDCloudVolume in $OSDCloudVolumes) { if (! $Public) { if (Test-Path "$($OSDCloudVolume.DriveLetter):\OSDCloud\Config") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $($OSDCloudVolume.DriveLetter):\OSDCloud\Config to OSDCloud Workspace $WorkspacePath\Config" robocopy "$($OSDCloudVolume.DriveLetter):\OSDCloud\Config" "$WorkspacePath\Config" *.* /e /mt /ndl /njh /njs /r:0 /w:0 /xd "$RECYCLE.BIN" "System Volume Information" } } if (Test-Path "$($OSDCloudVolume.DriveLetter):\OSDCloud\DriverPacks") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $($OSDCloudVolume.DriveLetter):\OSDCloud\DriverPacks to OSDCloud Workspace $WorkspacePath\DriverPacks" robocopy "$($OSDCloudVolume.DriveLetter):\OSDCloud\DriverPacks" "$WorkspacePath\DriverPacks" *.* /e /mt /ndl /njh /njs /r:0 /w:0 /xd "$RECYCLE.BIN" "System Volume Information" } if (Test-Path "$($OSDCloudVolume.DriveLetter):\OSDCloud\OS") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $($OSDCloudVolume.DriveLetter):\OSDCloud\OS to OSDCloud Workspace $WorkspacePath\OS" robocopy "$($OSDCloudVolume.DriveLetter):\OSDCloud\OS" "$WorkspacePath\OS" *.* /e /mt /ndl /njh /njs /r:0 /w:0 /xd "$RECYCLE.BIN" "System Volume Information" } if (Test-Path "$($OSDCloudVolume.DriveLetter):\OSDCloud\PowerShell") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $($OSDCloudVolume.DriveLetter):\OSDCloud\PowerShell to OSDCloud Workspace $WorkspacePath\PowerShell" robocopy "$($OSDCloudVolume.DriveLetter):\OSDCloud\PowerShell" "$WorkspacePath\PowerShell" *.* /e /mt /ndl /njh /njs /r:0 /w:0 /xd "$RECYCLE.BIN" "System Volume Information" } } } else { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud USB volume" Break } } else { #================================================= # Test WinpeSourcePath #================================================= if (-NOT ($WinpeSourcePath)) { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud Media" Break } if (-NOT (Test-Path $WinpeSourcePath)) { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud Media at $WinpeSourcePath" Break } if (-NOT (Test-Path "$WinpeSourcePath\sources\boot.wim")) { Write-Warning "[$(Get-Date -format s)] Unable to find an OSDCloud WinPE at $WinpeSourcePath\sources\boot.wim" Break } $WinpeDestinationPath = "$WorkspacePath\Media" if (-NOT (Test-Path $WinpeDestinationPath)) { $null = New-Item -Path $WinpeDestinationPath -ItemType Directory -Force | Out-Null } #================================================= # Update WinPE Volume #================================================= if ((Test-Path -Path "$WinpeSourcePath") -and (Test-Path -Path "$WinpeDestinationPath")) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Copying $WinpeSourcePath to $WinpeDestinationPath" robocopy "$WinpeSourcePath" "$WinpeDestinationPath" *.* /e /ndl /njh /njs /np /r:0 /w:0 /b /zb } } #endregion #region Remove Read-Only Attribute if ($WinpeDestinationPath) { Get-ChildItem -Path $WinpeDestinationPath -File -Recurse -Force | foreach { Set-ItemProperty -Path $_.FullName -Name IsReadOnly -Value $false -Force -ErrorAction Ignore } } #endregion #region Dismount OSDCloudISO if ($MountDiskImage) { Start-Sleep -Seconds 3 Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Dismounting $($MountDiskImage.ImagePath)" $null = Dismount-DiskImage -ImagePath $MountDiskImage.ImagePath } #endregion #region Set WorkspacePath Set-OSDCloudWorkspace -WorkspacePath $WorkspacePath -ErrorAction Stop | Out-Null #endregion #region Complete Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Yellow "Find your current OSDCloud Workspace: " -NoNewline Write-Host -ForegroundColor Gray "Get-OSDCloudWorkspace" Write-Host -ForegroundColor Yellow "Set a default OSDCloud Workspace: " -NoNewline Write-Host -ForegroundColor Gray "Set-OSDCloudWorkspace C:\OSDCloud2" Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] New-OSDCloudWorkspace created at $WorkspacePath" $null = Stop-Transcript -ErrorAction Ignore #endregion } function Set-OSDCloudWorkspace { <# .SYNOPSIS Sets configuration values by using Set-OSDCloudWorkspace. .DESCRIPTION Provides the implementation for Set-OSDCloudWorkspace. .PARAMETER WorkspacePath Specifies the value for WorkspacePath. .EXAMPLE Set-OSDCloudWorkspace -WorkspacePath <WorkspacePath> Runs Set-OSDCloudWorkspace with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param ( [Parameter(Position=0)] [System.String]$WorkspacePath = "$env:SystemDrive\OSDCloud" ) #region Block $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 } Block-PowerShellVersionLt5 Block-WinPE #endregion #region Set-OSDCloudWorkspace $WorkspaceSettings = [PSCustomObject]@{ WorkspacePath = $WorkspacePath } if (-not (Test-Path "$env:ProgramData\OSDCloud")) { $null = New-Item -Path "$env:ProgramData\OSDCloud" -ItemType Directory -Force -ErrorAction Stop } $WorkspaceSettings | ConvertTo-Json | Out-File "$env:ProgramData\OSDCloud\workspace.json" -Encoding ascii -Width 2000 -Force $WorkspacePath #endregion } function New-OSDCloudWorkspaceSetupCompleteTemplate { <# .SYNOPSIS Creates resources by using New-OSDCloudWorkspaceSetupCompleteTemplate. .DESCRIPTION Provides the implementation for New-OSDCloudWorkspaceSetupCompleteTemplate. .EXAMPLE New-OSDCloudWorkspaceSetupCompleteTemplate Runs New-OSDCloudWorkspaceSetupCompleteTemplate with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> $OSDCloudWS = Get-OSDCloudWorkspace $SetupCompletePath = "$OSDCloudWS\Config\Scripts\SetupComplete" $ScriptsPath = $SetupCompletePath if (!(Test-Path -Path $ScriptsPath)){New-Item -Path $ScriptsPath -ItemType Directory} $RunScript = @(@{ Script = "SetupComplete"; BatFile = 'SetupComplete.cmd'; ps1file = 'SetupComplete.ps1';Type = 'Setup'; Path = "$ScriptsPath"}) Write-Output "Creating $($RunScript.Script) Files" $BatFilePath = "$($RunScript.Path)\$($RunScript.batFile)" $PSFilePath = "$($RunScript.Path)\$($RunScript.ps1File)" #Create Batch File to Call PowerShell File if (Test-Path -Path $PSFilePath){ copy-item $PSFilePath -Destination "$ScriptsPath\SetupComplete.ps1.bak" } New-Item -Path $BatFilePath -ItemType File -Force $CustomActionContent = New-Object system.text.stringbuilder [void]$CustomActionContent.Append('%windir%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy ByPass -File C:\OSDCloud\Scripts\SetupComplete\SetupComplete.ps1') Add-Content -Path $BatFilePath -Value $CustomActionContent.ToString() #Create PowerShell File to do actions New-Item -Path $PSFilePath -ItemType File -Force Add-Content -path $PSFilePath 'Write-Output "========================================================="' Add-Content -path $PSFilePath 'Write-Output "Calling Custom Setup Complete File: $($PSCommandPath)"' Add-Content -path $PSFilePath 'Write-Output ""' Add-Content -path $PSFilePath 'Write-Output "CONFIRMED THIS RAN FROM FILE COPIED VIA FLASH DRIVE"' Add-Content -path $PSFilePath 'Write-Output ""' Add-Content -path $PSFilePath 'Write-Output "Completed Custom Setup Complete File: $($PSCommandPath)"' Add-Content -path $PSFilePath 'Write-Output "========================================================="' } |