Files
Invoke-OpenSSHConfiguration/Invoke-OpenSSHConfiguration.ps1
T
2026-04-30 20:30:01 -04:00

344 lines
20 KiB
PowerShell

#Requires -Version 5
<#
.SYNOPSIS
A short synopsis of of what the script does
.DESCRIPTION
A meaningful description of what the script does
.PARAMETER TaskSequenceVariables
One or more task sequence variable(s) to retrieve during task sequence execution.
If this parameter is not specified, all task sequence variable(s) will be stored into the variable 'TSVariableTable'.
Any task sequence variables that are new or have been updated will be saved back to the task sequence engine for futher usage.
$TSVariable.MyCustomVariableName = "MyCustomVariableValue"
$TSVariable.Make = "MyDeviceModel"
.PARAMETER LogDir
A valid folder path. If the folder does not exist, it will be created. This parameter can also be specified by the alias "LogPath".
.PARAMETER ContinueOnError
Ignore failures.
.EXAMPLE
Use this command to execute a VBSCript that will launch this powershell script automatically with the specified parameters. This is useful to avoid powershell execution complexities.
cscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%"
wscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%"
.EXAMPLE
powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "%FolderPathContainingScript%\%ScriptName%.ps1" -SwitchParameter -ScriptParameter "%ScriptParameterValue%"
.EXAMPLE
powershell.exe -ExecutionPolicy Bypass -NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command "& '%FolderPathContainingScript%\%ScriptName%.ps1' -ScriptParameter1 '%ScriptParameter1Value%' -ScriptParameter2 %ScriptParameter2Value% -SwitchParameter"
.NOTES
Any useful tidbits
.LINK
A useful link
#>
[CmdletBinding(SupportsShouldProcess=$True)]
Param
(
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('TSVars', 'TSVs')]
[String[]]$TaskSequenceVariables,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('TSVD', 'TSVDL')]
[String[]]$TSVariableDecodeList,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('LogDir', 'LogPath')]
[System.IO.DirectoryInfo]$LogDirectory,
[Parameter(Mandatory=$False)]
[Switch]$ContinueOnError
)
Function Test-ProcessElevationStatus
{
$Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = New-Object -TypeName 'System.Security.Principal.WindowsPrincipal' -ArgumentList ($Identity)
$Result = $Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
Write-Output -InputObject ($Result)
}
Switch (Test-ProcessElevationStatus)
{
Default
{
Try
{
#region Define Default Action Preferences
$Script:InformationPreference = 'Continue'
$Script:DebugPreference = 'SilentlyContinue'
$Script:ErrorActionPreference = 'Stop'
$Script:VerbosePreference = 'SilentlyContinue'
$Script:WarningPreference = 'Continue'
$Script:ConfirmPreference = 'None'
$Script:WhatIfPreference = $False
#endregion
#region Set the default exit code for the script (By default, the script will exit with an exit code of 0)
[System.Environment]::ExitCode = 0
#endregion
#region Initialize Toolkit (This operation loads functions, modules, and variables into the current session, so if you do not see a variable defined below, it is because it is defined in the Toolkit)
Try
{
[System.IO.FileInfo]$ToolkitScriptPath = "$([System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition))\Toolkit\Toolkit.ps1"
. "$($ToolkitScriptPath.FullName)" -CallingScriptInvocationInfo ($MyInvocation) -CallingScriptParameterSetName ($PSCmdlet.ParameterSetName)
}
Catch
{
[System.Environment]::ExitCode = 6000
Throw
}
#endregion
#region Set default parameter values
Switch ($True)
{
{([System.String]::IsNullOrEmpty($ExampleVariable) -eq $True) -or ([System.String]::IsNullOrWhiteSpace($ExampleVariable) -eq $True)}
{
}
}
#endregion
#region Perform Script Actions
$WindowsCapabilityInclusionExpression = 'OpenSSH.*'
$WriteLogMessage.Invoke(0, @("Attempting to retreive the list of Windows capabilities matching expression `"$($WindowsCapabilityInclusionExpression)`". Please Wait..."))
$WindowsCapabilityList = Get-WindowsCapability -Online -Name ($WindowsCapabilityInclusionExpression)
$WindowsCapabilityListCount = ($WindowsCapabilityList | Measure-Object).Count
$WriteLogMessage.Invoke(0, @("Located $($WindowsCapabilityListCount) Windows capabilities matching the specified expression."))
Switch ($WindowsCapabilityListCount -gt 0)
{
{($_ -eq $True)}
{
$WindowsCapabilityListCounter = 1
For ($WindowsCapabilityListIndex = 0; $WindowsCapabilityListIndex -lt $WindowsCapabilityListCount; $WindowsCapabilityListIndex++)
{
$WindowsCapabilityListItem = $WindowsCapabilityList[$WindowsCapabilityListIndex]
$WriteLogMessage.Invoke(0, @("Attempting to process windows capability $($WindowsCapabilityListCounter) of $($WindowsCapabilityListCount). Please Wait..."))
$WriteLogMessage.Invoke(0, @("DisplayName: $($WindowsCapabilityListItem.DisplayName)", "Name: $($WindowsCapabilityListItem.Name)", "State: $($WindowsCapabilityListItem.State)", "Description: $($WindowsCapabilityListItem.Description)"))
Switch ($WindowsCapabilityListItem.State -iin @('Installed'))
{
{($_ -eq $True)}
{
Try
{
$WriteLogMessage.Invoke(0, @("Attempting to remove windows capability $($WindowsCapabilityListCounter) of $($WindowsCapabilityListCount). Please Wait..."))
$Null = Remove-WindowsCapability -Online -Name ($WindowsCapabilityListItem.Name)
$WriteLogMessage.Invoke(0, @("Successfully removed windows capability $($WindowsCapabilityListCounter) of $($WindowsCapabilityListCount). Please Wait..."))
}
Catch
{
$WriteLogMessage.Invoke(2, @("Failed to remove windows capability $($WindowsCapabilityListCounter) of $($WindowsCapabilityListCount)."))
}
Finally
{
}
}
Default
{
$WriteLogMessage.Invoke(0, @("Skipping operations on windows capability $($WindowsCapabilityListCounter) of $($WindowsCapabilityListCount). Please Wait..."))
}
}
$WindowsCapabilityListCounter++
}
}
}
$GetInstalledSoftwareList = {Get-InstalledSoftware -FilterExpression {($_.DisplayName -imatch '(.*Open.*SSH.*)') -and ($_.DisplayName -inotmatch '(^.{0,0}$)')} -AllProperties -AllUserProfiles}
$InstalledSoftwareList = $GetInstalledSoftwareList.Invoke()
$InstalledSoftwareListCount = ($InstalledSoftwareList | Measure-Object).Count
Switch ($InstalledSoftwareListCount -eq 0)
{
{($_ -eq $True)}
{
#Determine the OS architecture for asset filtering (Win32, Win64, ARM64)
$OSArchitectureMap = @{
'AMD64' = 'Win64'
'x86' = 'Win32'
'ARM64' = 'ARM64'
'ARM' = 'ARM'
}
$ProcessorArchitecture = [System.Environment]::GetEnvironmentVariable('PROCESSOR_ARCHITECTURE')
$OSArchitectureFilter = $OSArchitectureMap[$ProcessorArchitecture]
#Build regex pattern to match MSI for the current architecture (e.g., "OpenSSH-Win64.*\.msi$")
$AssetPattern = "OpenSSH-$($OSArchitectureFilter).*\.msi$"
$GetGitRepositoryReleaseParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$GetGitRepositoryReleaseParameters.BaseURI = 'https://api.github.com'
$GetGitRepositoryReleaseParameters.RepositoryOwner = "PowerShell"
$GetGitRepositoryReleaseParameters.RepositoryName = "Win32-OpenSSH"
$GetGitRepositoryReleaseParameters.ReleaseTag = "latest"
$GetGitRepositoryReleaseParameters.AssetInclusionExpression = $AssetPattern
$GetGitRepositoryReleaseParameters.DestinationDirectory = [System.IO.Path]::Combine($Env:Windir, 'Temp', $CallingScriptPath.BaseName, 'Downloads')
$GetGitRepositoryReleaseParameters.Download = $True
$GetGitRepositoryReleaseParameters.Force = $False
$GetGitRepositoryReleaseParameters.ContinueOnError = $False
$GetGitRepositoryReleaseParameters.Verbose = $True
$GetGitRepositoryReleaseResult = Get-GitRepositoryRelease @GetGitRepositoryReleaseParameters
$GitRepositoryRelease = $GetGitRepositoryReleaseResult | Select-Object -First 1
$GitRepositoryReleaseAssetList = $GitRepositoryRelease.ReleaseInfo.AssetList | Where-Object {($_.DestinationPath.Extension -iin @('.msi')) -and ($_.DestinationPath.Exists -eq $True)}
$GitRepositoryReleaseAssetListCount = ($GitRepositoryReleaseAssetList | Measure-Object).Count
$GitRepositoryReleaseAssetListCounter = 1
For ($GitRepositoryReleaseAssetListIndex = 0; $GitRepositoryReleaseAssetListIndex -lt $GitRepositoryReleaseAssetListCount; $GitRepositoryReleaseAssetListIndex++)
{
$GitRepositoryReleaseAssetListItem = $GitRepositoryReleaseAssetList[$GitRepositoryReleaseAssetListIndex]
$ExecutionLogPath = [System.IO.FileInfo][System.IO.Path]::Combine($LogDirectory.FullName, "$($GitRepositoryReleaseAssetListItem.DestinationPath.BaseName).log")
$StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$StartProcessWithOutputParameters.FilePath = 'msiexec.exe'
#$StartProcessWithOutputParameters.WorkingDirectory = "$($Env:SystemDrive)\Users\Public\Documents"
$StartProcessWithOutputParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
$StartProcessWithOutputParameters.ArgumentList.Add('/i')
$StartProcessWithOutputParameters.ArgumentList.Add("`"$($GitRepositoryReleaseAssetListItem.DestinationPath.FullName)`"")
$StartProcessWithOutputParameters.ArgumentList.Add('/qn')
$StartProcessWithOutputParameters.ArgumentList.Add('/norestart')
$StartProcessWithOutputParameters.ArgumentList.Add("/l*v `"$($ExecutionLogPath.FullName)`"")
$StartProcessWithOutputParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
$StartProcessWithOutputParameters.AcceptableExitCodeList.Add('0')
$StartProcessWithOutputParameters.AcceptableExitCodeList.Add('3010')
$StartProcessWithOutputParameters.CreateNoWindow = $True
$StartProcessWithOutputParameters.ExecutionTimeout = [System.Timespan]::FromMinutes(15)
$StartProcessWithOutputParameters.ExecutionTimeoutInterval = [System.Timespan]::FromSeconds(5)
$StartProcessWithOutputParameters.SecureArgumentList = $False
$StartProcessWithOutputParameters.LogOutput = $True
$StartProcessWithOutputParameters.ContinueOnError = $False
$StartProcessWithOutputParameters.Verbose = $True
$StartProcessWithOutputParameters.ErrorAction = [System.Management.Automation.Actionpreference]::Stop
$MSIProductInfo = Get-MSIPropertyList -Path ($GitRepositoryReleaseAssetListItem.DestinationPath.FullName)
$WriteLogMessage.Invoke(0, @("Attempting to release binary $($GitRepositoryReleaseAssetListCounter) of $($GitRepositoryReleaseAssetListCount). Please Wait...", "Manufacturer: $($MSIProductInfo.Manufacturer)", "ProductName: $($MSIProductInfo.ProductName)", "ProductVersion: $($MSIProductInfo.ProductVersion)", "Product Language: $($MSIProductInfo.ProductLanguage)"))
$StartProcessWithOutputResult = Start-ProcessWithOutput @StartProcessWithOutputParameters
$GitRepositoryReleaseAssetListCounter++
}
}
}
$InstalledSoftwareList = $GetInstalledSoftwareList.Invoke()
$InstalledSoftwareListCount = ($InstalledSoftwareList | Measure-Object).Count
Switch ($InstalledSoftwareListCount -gt 0)
{
{($_ -eq $True)}
{
###Do configuration stuff here
#PublicKeys - New and from list idepotent
#Certs - From windows store to PEM
#Options etc
}
}
#endregion
}
Catch
{
#region Perform error handling actions
$ErrorHandlingDefinition.Invoke($Error[0], 2, $ContinueOnError.IsPresent)
#endregion
}
Finally
{
#region Perform finalization actions
$FinalizationActions.Invoke()
#endregion
}
}
{($_ -eq $False)}
{
[System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Definition)"
#$CurrentExecutionPolicy = Get-ExecutionPolicy -Scope Process
$CurrentExecutionPolicy = 'Bypass'
$ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$ArgumentList.Add("-ExecutionPolicy $($CurrentExecutionPolicy)")
$ArgumentList.Add('-NonInteractive')
$ArgumentList.Add('-NoProfile')
$ArgumentList.Add('-NoLogo')
$ArgumentList.Add('-NoExit')
$ArgumentList.Add('-Command')
$ArgumentList.Add("`"`& {. `"$($ScriptPath.FullName)`"")
$MyInvocation.UnboundArguments.GetEnumerator() | ForEach-Object {$ArgumentList.Add("-$($_.Key) @($($_.Value | ForEach-Object {`"$($_)`"}))")}
$PSBoundParameters.GetEnumerator() | ForEach-Object {$ArgumentList.Add("-$($_.Key) @($($_.Value | ForEach-Object {`"$($_)`"}))")}
$ArgumentListTargetIndex = $ArgumentList.Count - 1
$ArgumentListIndexItem = $ArgumentList[$ArgumentListTargetIndex]
$Null = $ArgumentList.RemoveAt($ArgumentListTargetIndex)
$Null = $ArgumentList.Insert($ArgumentListTargetIndex, ($ArgumentListIndexItem + ';'))
$ArgumentList.Add("[System.Environment]::Exit((`$LASTEXITCODE -Bor [Int](-Not `$? -And -Not `$LASTEXITCODE)))}`"")
$ScriptInterpreterList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
$ScriptInterpreterList.Add('powershell.exe')
$ScriptInterpreterList.Add('pwsh.exe')
:ScriptInterpreterListLoop ForEach ($ScriptInterpreter In $ScriptInterpreterList)
{
$ScriptInterpreterObject = Try {Get-Command -Name ($ScriptInterpreter) -ErrorAction SilentlyContinue} Catch {$Null}
Switch ($Null -ine $ScriptInterpreterObject)
{
{($_ -eq $True)}
{
$Null = Start-Process -FilePath ($ScriptInterpreterObject.Path) -WorkingDirectory "$($Env:Temp.TrimEnd('\'))" -ArgumentList ($ArgumentList.ToArray()) -WindowStyle Normal -Verb RunAs -PassThru
Break ScriptInterpreterListLoop
}
}
}
}
}