mirror of
https://github.com/Grace-Solutions/Invoke-OpenSSHConfiguration.git
synced 2026-07-26 11:38:14 +00:00
bb1d0d7420
- Query Win32_UserProfile by SID instead of Test-Path on registry - Win32_UserProfile.Loaded property explicitly indicates hive status - Added -Verbose:$False to suppress CIM query output - Improved logging to show Loaded property value
1040 lines
74 KiB
PowerShell
1040 lines
74 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('M')]
|
|
[ValidateSet('Client', 'Server')]
|
|
[String]$Mode,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias('PKL')]
|
|
[String[]]$PublicKeyList,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Switch]$GenerateKeyPair,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('rsa', 'ecdsa', 'ed25519')]
|
|
[String]$KeyType = 'ed25519',
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet(256, 384, 521, 2048, 3072, 4096)]
|
|
[Int]$KeyBits,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Switch]$EnablePubkeyAuthentication,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Switch]$DisablePasswordAuthentication,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Switch]$EnableCertificateAuthentication,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Switch]$TrustWindowsCertificateAuthorities,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[String]$CAInclusionExpression,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[String]$CAExclusionExpression,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[String]$SFTPSubsystemPath,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Int]$SSHPort = 22,
|
|
|
|
[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($Mode) -eq $True) -or ([System.String]::IsNullOrWhiteSpace($Mode) -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.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$StartProcessWithOutputParameters.ArgumentList.Add('/i')
|
|
$StartProcessWithOutputParameters.ArgumentList.Add("`"$($GitRepositoryReleaseAssetListItem.DestinationPath.FullName)`"")
|
|
|
|
Switch ($True)
|
|
{
|
|
{([System.String]::IsNullOrEmpty($Mode) -eq $False) -and ([System.String]::IsNullOrWhiteSpace($Mode) -eq $False)}
|
|
{
|
|
$StartProcessWithOutputParameters.ArgumentList.Add("ADDLOCAL=$($Mode)")
|
|
}
|
|
}
|
|
|
|
$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 install 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)}
|
|
{
|
|
#region Define OpenSSH Paths and Settings
|
|
[System.IO.DirectoryInfo]$OpenSSHProgramDataDirectory = [System.IO.Path]::Combine($Env:ProgramData, 'ssh')
|
|
[System.IO.FileInfo]$SSHDConfigPath = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, 'sshd_config')
|
|
[System.IO.FileInfo]$AdministratorsAuthorizedKeysPath = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, 'administrators_authorized_keys')
|
|
[System.Text.Encoding]$OpenSSHFileEncoding = [System.Text.Encoding]::ASCII
|
|
#endregion
|
|
|
|
#region Generate Key Pair
|
|
Switch ($GenerateKeyPair.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to generate SSH key pair. Please Wait..."))
|
|
|
|
[System.IO.DirectoryInfo]$SSHKeyDirectory = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, 'keys')
|
|
|
|
Switch ($SSHKeyDirectory.Exists)
|
|
{
|
|
{($_ -eq $False)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to create directory `"$($SSHKeyDirectory.FullName)`". Please Wait..."))
|
|
|
|
$Null = [System.IO.Directory]::CreateDirectory($SSHKeyDirectory.FullName)
|
|
}
|
|
}
|
|
|
|
$KeyFileName = "ssh_host_$($KeyType)_key"
|
|
|
|
[System.IO.FileInfo]$PrivateKeyPath = [System.IO.Path]::Combine($SSHKeyDirectory.FullName, $KeyFileName)
|
|
|
|
[System.IO.FileInfo]$PublicKeyPath = [System.IO.Path]::Combine($SSHKeyDirectory.FullName, "$($KeyFileName).pub")
|
|
|
|
Switch ($PrivateKeyPath.Exists)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("SSH key pair already exists at `"$($PrivateKeyPath.FullName)`". Skipping generation."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$SSHKeygenArgumentList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$SSHKeygenArgumentList.Add('-t')
|
|
$SSHKeygenArgumentList.Add($KeyType)
|
|
|
|
Switch (($Null -ine $KeyBits) -and ($KeyBits -gt 0))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SSHKeygenArgumentList.Add('-b')
|
|
$SSHKeygenArgumentList.Add($KeyBits.ToString())
|
|
}
|
|
}
|
|
|
|
$SSHKeygenArgumentList.Add('-f')
|
|
$SSHKeygenArgumentList.Add("`"$($PrivateKeyPath.FullName)`"")
|
|
$SSHKeygenArgumentList.Add('-N')
|
|
$SSHKeygenArgumentList.Add('""')
|
|
$SSHKeygenArgumentList.Add('-C')
|
|
$SSHKeygenArgumentList.Add("`"$($Env:COMPUTERNAME)`"")
|
|
|
|
$StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$StartProcessWithOutputParameters.FilePath = 'ssh-keygen.exe'
|
|
$StartProcessWithOutputParameters.ArgumentList = $SSHKeygenArgumentList
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList.Add('0')
|
|
$StartProcessWithOutputParameters.CreateNoWindow = $True
|
|
$StartProcessWithOutputParameters.ExecutionTimeout = [System.Timespan]::FromMinutes(2)
|
|
$StartProcessWithOutputParameters.ExecutionTimeoutInterval = [System.Timespan]::FromSeconds(5)
|
|
$StartProcessWithOutputParameters.LogOutput = $True
|
|
$StartProcessWithOutputParameters.ContinueOnError = $False
|
|
$StartProcessWithOutputParameters.Verbose = $True
|
|
|
|
$WriteLogMessage.Invoke(0, @("Generating $($KeyType) key pair at `"$($PrivateKeyPath.FullName)`". Please Wait..."))
|
|
|
|
$StartProcessWithOutputResult = Start-ProcessWithOutput @StartProcessWithOutputParameters
|
|
|
|
$WriteLogMessage.Invoke(0, @("SSH key pair generated successfully."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Add Public Keys
|
|
Switch (($Null -ine $PublicKeyList) -and ($PublicKeyList.Count -gt 0))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to add $($PublicKeyList.Count) public key(s) to authorized_keys. Please Wait..."))
|
|
|
|
$ExistingAuthorizedKeys = New-Object -TypeName 'System.Collections.Generic.HashSet[System.String]'
|
|
|
|
Switch ($AdministratorsAuthorizedKeysPath.Exists)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ExistingContent = [System.IO.File]::ReadAllLines($AdministratorsAuthorizedKeysPath.FullName)
|
|
|
|
ForEach ($Line In $ExistingContent)
|
|
{
|
|
Switch (([System.String]::IsNullOrEmpty($Line) -eq $False) -and ([System.String]::IsNullOrWhiteSpace($Line) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$Null = $ExistingAuthorizedKeys.Add($Line.Trim())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$KeysToAdd = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
|
|
$PublicKeyListCounter = 1
|
|
|
|
ForEach ($PublicKey In $PublicKeyList)
|
|
{
|
|
$PublicKeyTrimmed = $PublicKey.Trim()
|
|
|
|
Switch ($ExistingAuthorizedKeys.Contains($PublicKeyTrimmed))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Public key $($PublicKeyListCounter) of $($PublicKeyList.Count) already exists. Skipping."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Adding public key $($PublicKeyListCounter) of $($PublicKeyList.Count)."))
|
|
$KeysToAdd.Add($PublicKeyTrimmed)
|
|
}
|
|
}
|
|
|
|
$PublicKeyListCounter++
|
|
}
|
|
|
|
Switch ($KeysToAdd.Count -gt 0)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Appending $($KeysToAdd.Count) new public key(s) to `"$($AdministratorsAuthorizedKeysPath.FullName)`". Please Wait..."))
|
|
|
|
$Null = [System.IO.File]::AppendAllLines($AdministratorsAuthorizedKeysPath.FullName, $KeysToAdd, $OpenSSHFileEncoding)
|
|
|
|
$WriteLogMessage.Invoke(0, @("Public keys added successfully."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Export Windows CA Certificates to PEM
|
|
Switch ($TrustWindowsCertificateAuthorities.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to export Windows CA certificates to PEM format. Please Wait..."))
|
|
|
|
$CertificateExpirationThreshold = Get-Date
|
|
|
|
#Include LocalMachine CA stores
|
|
$CertificateStorePathList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$CertificateStorePathList.Add('Cert:\LocalMachine\Root')
|
|
$CertificateStorePathList.Add('Cert:\LocalMachine\CA')
|
|
|
|
#Include CurrentUser CA stores from session user (not process user)
|
|
#Note: Cert:\CurrentUser resolves to PROCESS identity, not session user
|
|
#To access session user's certs, we read directly from HKEY_USERS\{SID}
|
|
$UserCertificates = New-Object -TypeName 'System.Collections.Generic.List[System.Security.Cryptography.X509Certificates.X509Certificate2]'
|
|
|
|
Switch (($Null -ine $CurrentUserSession) -and ([System.String]::IsNullOrEmpty($CurrentUserSession.SID) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
#Check if user's profile/registry hive is loaded via Win32_UserProfile
|
|
$UserProfile = Get-CimInstance -ClassName 'Win32_UserProfile' -Filter "SID = '$($CurrentUserSession.SID)'" -ErrorAction SilentlyContinue -Verbose:$False
|
|
|
|
Switch (($Null -ine $UserProfile) -and ($UserProfile.Loaded -eq $True))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$UserRegistryBasePath = "Registry::HKEY_USERS\$($CurrentUserSession.SID)"
|
|
|
|
$WriteLogMessage.Invoke(0, @("Accessing certificate store for session user: $($CurrentUserSession.NTAccount) [SID: $($CurrentUserSession.SID)] [Profile Loaded: $($UserProfile.Loaded)]"))
|
|
|
|
#Read certificates directly from user's registry hive
|
|
$UserCertStoreNames = @('Root', 'CA')
|
|
|
|
ForEach ($StoreName In $UserCertStoreNames)
|
|
{
|
|
$UserCertRegistryPath = "$($UserRegistryBasePath)\Software\Microsoft\SystemCertificates\$($StoreName)\Certificates"
|
|
|
|
Switch (Test-Path -Path $UserCertRegistryPath -ErrorAction SilentlyContinue)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CertSubKeys = Get-ChildItem -Path $UserCertRegistryPath -ErrorAction SilentlyContinue
|
|
|
|
ForEach ($CertSubKey In $CertSubKeys)
|
|
{
|
|
Try
|
|
{
|
|
$CertBlob = Get-ItemProperty -Path $CertSubKey.PSPath -Name 'Blob' -ErrorAction SilentlyContinue
|
|
|
|
Switch ($Null -ine $CertBlob.Blob)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$X509Cert = New-Object -TypeName 'System.Security.Cryptography.X509Certificates.X509Certificate2' -ArgumentList @(,[System.Byte[]]$CertBlob.Blob)
|
|
$UserCertificates.Add($X509Cert)
|
|
}
|
|
}
|
|
}
|
|
Catch
|
|
{
|
|
#Skip invalid certificate blobs
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(0, @("Found $($UserCertificates.Count) certificate(s) in session user's store."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(1, @("User profile not loaded for SID: $($CurrentUserSession.SID) (Win32_UserProfile.Loaded = $($UserProfile.Loaded)). Skipping user certificates."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$CACertificates = New-Object -TypeName 'System.Collections.Generic.List[System.Security.Cryptography.X509Certificates.X509Certificate2]'
|
|
|
|
#Add certificates from LocalMachine stores
|
|
$LocalMachineCerts = Get-ChildItem -Path $CertificateStorePathList -ErrorAction SilentlyContinue | Where-Object {($_.NotAfter -gt $CertificateExpirationThreshold) -and ($_.Extensions | Where-Object {($_.Oid.FriendlyName -iin @('Basic Constraints')) -and ($_.CertificateAuthority -eq $True)})}
|
|
|
|
Switch ($Null -ine $LocalMachineCerts)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CACertificates.AddRange([System.Security.Cryptography.X509Certificates.X509Certificate2[]]$LocalMachineCerts)
|
|
}
|
|
}
|
|
|
|
#Add user certificates (filter for CA certs)
|
|
Switch ($UserCertificates.Count -gt 0)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$FilteredUserCerts = $UserCertificates | Where-Object {($_.NotAfter -gt $CertificateExpirationThreshold) -and ($_.Extensions | Where-Object {($_.Oid.FriendlyName -iin @('Basic Constraints')) -and ($_.CertificateAuthority -eq $True)})}
|
|
|
|
Switch ($Null -ine $FilteredUserCerts)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CACertificates.AddRange([System.Security.Cryptography.X509Certificates.X509Certificate2[]]$FilteredUserCerts)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Dedupe by thumbprint
|
|
$CACertificates = $CACertificates | Sort-Object -Property @('Thumbprint') -Unique
|
|
|
|
#region Apply CA Inclusion Filter
|
|
Switch (([System.String]::IsNullOrEmpty($CAInclusionExpression) -eq $False) -and ([System.String]::IsNullOrWhiteSpace($CAInclusionExpression) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Applying CA inclusion filter: `"$($CAInclusionExpression)`""))
|
|
$CACertificates = $CACertificates | Where-Object {($_.Subject -imatch $CAInclusionExpression) -or ($_.Thumbprint -imatch $CAInclusionExpression)}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Apply CA Exclusion Filter
|
|
Switch (([System.String]::IsNullOrEmpty($CAExclusionExpression) -eq $False) -and ([System.String]::IsNullOrWhiteSpace($CAExclusionExpression) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Applying CA exclusion filter: `"$($CAExclusionExpression)`""))
|
|
$CACertificates = $CACertificates | Where-Object {($_.Subject -inotmatch $CAExclusionExpression) -and ($_.Thumbprint -inotmatch $CAExclusionExpression)}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
$CACertificatesCount = ($CACertificates | Measure-Object).Count
|
|
|
|
$WriteLogMessage.Invoke(0, @("Found $($CACertificatesCount) CA certificate(s) matching criteria."))
|
|
|
|
Switch ($CACertificatesCount -gt 0)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ConvertFromCertificateToPEMParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$ConvertFromCertificateToPEMParameters.CertificateList = $CACertificates
|
|
$ConvertFromCertificateToPEMParameters.ExportRootDirectory = $OpenSSHProgramDataDirectory.FullName
|
|
$ConvertFromCertificateToPEMParameters.CreateCABundle = $True
|
|
$ConvertFromCertificateToPEMParameters.CABundleFileName = 'OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem'
|
|
$ConvertFromCertificateToPEMParameters.ContinueOnError = $False
|
|
$ConvertFromCertificateToPEMParameters.Verbose = $True
|
|
|
|
$ConvertFromCertificateToPEMResult = ConvertFrom-CertificateToPEM @ConvertFromCertificateToPEMParameters
|
|
|
|
$CABundleResult = $ConvertFromCertificateToPEMResult | Where-Object {($_.Type -ieq 'CABundle')}
|
|
|
|
Switch ($Null -ine $CABundleResult)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("CA bundle exported successfully to `"$($CABundleResult.Path)`" with $($CABundleResult.CertificateCount) certificate(s)."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Configure sshd_config
|
|
Switch ($SSHDConfigPath.Exists)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to configure sshd_config at `"$($SSHDConfigPath.FullName)`". Please Wait..."))
|
|
|
|
#region Backup Existing Configuration
|
|
$SSHDConfigBackupPath = [System.IO.FileInfo][System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, "sshd_config.backup.$($GetCurrentDateTimeFileFormat.Invoke())")
|
|
|
|
$WriteLogMessage.Invoke(0, @("Creating backup of sshd_config to `"$($SSHDConfigBackupPath.FullName)`". Please Wait..."))
|
|
|
|
$Null = [System.IO.File]::Copy($SSHDConfigPath.FullName, $SSHDConfigBackupPath.FullName, $True)
|
|
|
|
$WriteLogMessage.Invoke(0, @("Backup created successfully."))
|
|
|
|
#region Cleanup Old Backups (Keep Last 3)
|
|
$BackupFilePattern = 'sshd_config.backup.*'
|
|
$BackupsToKeep = 3
|
|
|
|
$ExistingBackups = Get-ChildItem -Path $OpenSSHProgramDataDirectory.FullName -Filter $BackupFilePattern -File -ErrorAction SilentlyContinue | Sort-Object -Property @('LastWriteTime') -Descending
|
|
|
|
Switch (($ExistingBackups | Measure-Object).Count -gt $BackupsToKeep)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$BackupsToRemove = $ExistingBackups | Select-Object -Skip $BackupsToKeep
|
|
|
|
ForEach ($BackupToRemove In $BackupsToRemove)
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Removing old backup `"$($BackupToRemove.Name)`"."))
|
|
$Null = [System.IO.File]::Delete($BackupToRemove.FullName)
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
$SSHDConfigContent = [System.IO.File]::ReadAllText($SSHDConfigPath.FullName)
|
|
$SSHDConfigModified = $False
|
|
|
|
#region Enable PubkeyAuthentication
|
|
Switch ($EnablePubkeyAuthentication.IsPresent -eq $True)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$PubkeyAuthPattern = '(?m)^#?\s*PubkeyAuthentication\s+.*$'
|
|
$PubkeyAuthReplacement = 'PubkeyAuthentication yes'
|
|
|
|
Switch ($SSHDConfigContent -imatch $PubkeyAuthPattern)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $PubkeyAuthPattern, $PubkeyAuthReplacement)
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Enabled PubkeyAuthentication."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$SSHDConfigContent = $SSHDConfigContent + "`r`n$($PubkeyAuthReplacement)"
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Added PubkeyAuthentication setting."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Disable PasswordAuthentication
|
|
Switch ($DisablePasswordAuthentication.IsPresent -eq $True)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$PasswordAuthPattern = '(?m)^#?\s*PasswordAuthentication\s+.*$'
|
|
$PasswordAuthReplacement = 'PasswordAuthentication no'
|
|
|
|
Switch ($SSHDConfigContent -imatch $PasswordAuthPattern)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $PasswordAuthPattern, $PasswordAuthReplacement)
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Disabled PasswordAuthentication."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$SSHDConfigContent = $SSHDConfigContent + "`r`n$($PasswordAuthReplacement)"
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Added PasswordAuthentication setting (disabled)."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Configure SFTP Subsystem (Default)
|
|
$SFTPPath = $SFTPSubsystemPath
|
|
|
|
Switch (([System.String]::IsNullOrEmpty($SFTPPath) -eq $True) -or ([System.String]::IsNullOrWhiteSpace($SFTPPath) -eq $True))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SFTPPath = 'sftp-server.exe'
|
|
}
|
|
}
|
|
|
|
$SubsystemPattern = '(?m)^#?\s*Subsystem\s+sftp\s+.*$'
|
|
$SubsystemReplacement = "Subsystem sftp $($SFTPPath)"
|
|
|
|
Switch ($SSHDConfigContent -imatch $SubsystemPattern)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CurrentSubsystemMatch = [System.Text.RegularExpressions.Regex]::Match($SSHDConfigContent, $SubsystemPattern)
|
|
|
|
Switch ($CurrentSubsystemMatch.Value -ine $SubsystemReplacement)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $SubsystemPattern, $SubsystemReplacement)
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Configured SFTP subsystem: $($SFTPPath)"))
|
|
}
|
|
}
|
|
}
|
|
|
|
Default
|
|
{
|
|
$SSHDConfigContent = $SSHDConfigContent + "`r`n$($SubsystemReplacement)"
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Added SFTP subsystem configuration."))
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Configure TrustedUserCAKeys for Certificate Authentication
|
|
Switch (($EnableCertificateAuthentication.IsPresent -eq $True) -and ($TrustWindowsCertificateAuthorities.IsPresent -eq $True))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CABundleFileName = 'OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem'
|
|
[System.IO.FileInfo]$CABundlePath = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, $CABundleFileName)
|
|
|
|
#Use __PROGRAMDATA__ variable for sshd_config (portable path)
|
|
$CABundleConfigPath = "__PROGRAMDATA__/ssh/$($CABundleFileName)"
|
|
|
|
Switch ($CABundlePath.Exists)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$TrustedCAKeysPattern = '(?m)^#?\s*TrustedUserCAKeys\s+.*$'
|
|
$TrustedCAKeysReplacement = "TrustedUserCAKeys $($CABundleConfigPath)"
|
|
|
|
Switch ($SSHDConfigContent -imatch $TrustedCAKeysPattern)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $TrustedCAKeysPattern, $TrustedCAKeysReplacement)
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Configured TrustedUserCAKeys to `"$($CABundleConfigPath)`"."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$SSHDConfigContent = $SSHDConfigContent + "`r`n$($TrustedCAKeysReplacement)"
|
|
$SSHDConfigModified = $True
|
|
$WriteLogMessage.Invoke(0, @("Added TrustedUserCAKeys setting."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Write sshd_config if Modified
|
|
Switch ($SSHDConfigModified -eq $True)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Writing updated sshd_config. Please Wait..."))
|
|
[System.IO.File]::WriteAllText($SSHDConfigPath.FullName, $SSHDConfigContent, $OpenSSHFileEncoding)
|
|
$WriteLogMessage.Invoke(0, @("sshd_config updated successfully."))
|
|
|
|
#region Validate Configuration
|
|
$WriteLogMessage.Invoke(0, @("Validating sshd_config with sshd -t. Please Wait..."))
|
|
|
|
$SSHDExePath = [System.IO.FileInfo][System.IO.Path]::Combine($Env:ProgramFiles, 'OpenSSH', 'sshd.exe')
|
|
|
|
Switch ($SSHDExePath.Exists -eq $True)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ValidateConfigParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$ValidateConfigParameters.FilePath = $SSHDExePath.FullName
|
|
$ValidateConfigParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$ValidateConfigParameters.ArgumentList.Add('-t')
|
|
$ValidateConfigParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$ValidateConfigParameters.AcceptableExitCodeList.Add('0')
|
|
$ValidateConfigParameters.CreateNoWindow = $True
|
|
$ValidateConfigParameters.ExecutionTimeout = [System.Timespan]::FromSeconds(30)
|
|
$ValidateConfigParameters.ExecutionTimeoutInterval = [System.Timespan]::FromSeconds(5)
|
|
$ValidateConfigParameters.LogOutput = $True
|
|
$ValidateConfigParameters.ContinueOnError = $True
|
|
$ValidateConfigParameters.Verbose = $True
|
|
|
|
$ValidateConfigResult = Start-ProcessWithOutput @ValidateConfigParameters
|
|
|
|
Switch ($ValidateConfigResult.ExitCode -eq 0)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("sshd_config validation passed."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Attempting to restart sshd service to apply configuration changes. Please Wait..."))
|
|
$Null = Restart-Service -Name 'sshd' -Force -ErrorAction SilentlyContinue
|
|
$WriteLogMessage.Invoke(0, @("sshd service restarted."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("sshd_config validation failed. Restoring backup..."))
|
|
$Null = [System.IO.File]::Copy($SSHDConfigBackupPath.FullName, $SSHDConfigPath.FullName, $True)
|
|
$WriteLogMessage.Invoke(0, @("Backup restored. Configuration changes reverted."))
|
|
}
|
|
}
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(1, @("sshd.exe not found at `"$($SSHDExePath.FullName)`". Skipping validation."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Attempting to restart sshd service to apply configuration changes. Please Wait..."))
|
|
$Null = Restart-Service -Name 'sshd' -Force -ErrorAction SilentlyContinue
|
|
$WriteLogMessage.Invoke(0, @("sshd service restarted."))
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("No changes required to sshd_config."))
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Service Management (Automatic)
|
|
$SSHDService = Get-Service -Name 'sshd' -ErrorAction SilentlyContinue
|
|
|
|
Switch ($Null -ine $SSHDService)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
#region Configure Automatic Startup
|
|
Switch ($SSHDService.StartType -ine 'Automatic')
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Configuring sshd service to start automatically. Please Wait..."))
|
|
$Null = Set-Service -Name 'sshd' -StartupType Automatic -ErrorAction SilentlyContinue
|
|
$WriteLogMessage.Invoke(0, @("sshd service configured for automatic startup."))
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Start Service
|
|
Switch ($SSHDService.Status -ine 'Running')
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Starting sshd service. Please Wait..."))
|
|
$Null = Start-Service -Name 'sshd' -ErrorAction SilentlyContinue
|
|
$WriteLogMessage.Invoke(0, @("sshd service started."))
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Windows Firewall Configuration (Automatic)
|
|
$FirewallRuleName = 'OpenSSH-Server-In-TCP'
|
|
$FirewallProfiles = @('Domain', 'Private', 'Public')
|
|
|
|
ForEach ($FirewallProfile In $FirewallProfiles)
|
|
{
|
|
$ExistingRule = Get-NetFirewallRule -Name "$($FirewallRuleName)-$($FirewallProfile)" -ErrorAction SilentlyContinue
|
|
|
|
Switch ($Null -eq $ExistingRule)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Creating Windows Firewall rule for profile `"$($FirewallProfile)`". Please Wait..."))
|
|
|
|
$FirewallRuleParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$FirewallRuleParameters.Add('Name', "$($FirewallRuleName)-$($FirewallProfile)")
|
|
$FirewallRuleParameters.Add('DisplayName', "OpenSSH SSH Server ($($FirewallProfile))")
|
|
$FirewallRuleParameters.Add('Description', "Inbound rule for OpenSSH SSH Server on TCP port $($SSHPort) for $($FirewallProfile) profile")
|
|
$FirewallRuleParameters.Add('Enabled', 'True')
|
|
$FirewallRuleParameters.Add('Direction', 'Inbound')
|
|
$FirewallRuleParameters.Add('Protocol', 'TCP')
|
|
$FirewallRuleParameters.Add('LocalPort', $SSHPort)
|
|
$FirewallRuleParameters.Add('Profile', $FirewallProfile)
|
|
$FirewallRuleParameters.Add('Action', 'Allow')
|
|
$FirewallRuleParameters.Add('ErrorAction', 'SilentlyContinue')
|
|
|
|
$Null = New-NetFirewallRule @FirewallRuleParameters
|
|
$WriteLogMessage.Invoke(0, @("Windows Firewall rule created for profile `"$($FirewallProfile)`"."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
Switch (($ExistingRule.Enabled -eq $False) -or ($ExistingRule.LocalPort -ne $SSHPort))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Updating Windows Firewall rule for profile `"$($FirewallProfile)`". Please Wait..."))
|
|
$Null = Set-NetFirewallRule -Name "$($FirewallRuleName)-$($FirewallProfile)" -Enabled 'True' -LocalPort $SSHPort -ErrorAction SilentlyContinue
|
|
$WriteLogMessage.Invoke(0, @("Windows Firewall rule updated for profile `"$($FirewallProfile)`"."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
#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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |