Created
April 16, 2020 13:34
-
-
Save milichev/0334845581f92911d8e45e4fb5b8becf to your computer and use it in GitHub Desktop.
PowerShell utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region Scripting | |
function Test-IsConsoleHost { | |
@('ConsoleHost', 'Visual Studio Code Host') -contains ($host.name) | |
} | |
function Get-PromptParam { | |
[CmdletBinding()] | |
param ( | |
[string] $name, | |
[string] $defaultValue = '', | |
[bool] $mandatory = $true | |
) | |
# return $defaultValue | |
$value = Read-Host "$name [$defaultValue]" | |
if (-not $value) { | |
$value = $defaultValue | |
} | |
if (-not $value -and $mandatory) { | |
throw "Cannot proceed without mandatory parameter '$name'" | |
} | |
return $value | |
} | |
function Get-Choices { | |
[CmdletBinding()] | |
param( | |
[object[]] $labels | |
) | |
$letters = New-Object string[] $labels.Length | |
$labels | | |
ForEach-Object { [convert]::ToString($_) } | | |
ForEach-Object { $i = -1 } { | |
$i++ | |
$char = @($_ | Select-String -Pattern '\&(\w)' | ForEach-Object { $_.matches.groups[1].value })[0] | |
if ($char) { | |
$letters[$i] = $char | |
$_ | |
} | |
else { | |
[string]$char = ($_ | ForEach-Object { ($_ -replace '\W', '') -split '' } | Where-Object { $_ -and $letters -notcontains $_ })[0] | |
if ($char) { | |
$letters[$i] = $char | |
([regex]$char).replace($_, "&$($char)", 1) | |
} | |
else { | |
$_ | |
} | |
} | |
} | | |
ForEach-Object { New-Object System.Management.Automation.Host.ChoiceDescription $_ } | |
} | |
$defaultConfirmOptions = Get-Choices @('Yes', 'No') | |
function Read-OptionChoice { | |
[CmdletBinding()] | |
param ( | |
[string] $caption = "Please confirm", | |
[string] $message = "Are you sure you want to proceed:", | |
[int]$defaultChoice = -1, | |
[System.Management.Automation.Host.ChoiceDescription[]] $options = $defaultConfirmOptions | |
) | |
if (Test-IsConsoleHost) { | |
$caption = "`n$caption`n" | |
$message = "`n$message`n" | |
} | |
return $host.ui.PromptForChoice($caption, $message, $options, $defaultChoice) | |
} | |
function Read-SelectedFolder { | |
[CmdletBinding()] | |
param( | |
[string] $InitialFolder, | |
[string] $Description = "Select a Folder", | |
[switch] $ShowNewFolderButton = $false | |
) | |
Add-Type -AssemblyName System.Windows.Forms | |
$folderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ | |
SelectedPath = $InitialFolder | |
ShowNewFolderButton = $ShowNewFolderButton | |
Description = $Description | |
} | |
if ($folderBrowserDialog.ShowDialog((Get-MainWindowHandle)) -eq "Cancel") { | |
break | |
} | |
$folderBrowserDialog.SelectedPath | |
} | |
function Get-CheckedCredential { | |
[CmdletBinding()] | |
param( | |
[string] $purpose, | |
[string] $userName = "$($env:USERDOMAIN)\$($env:USERNAME)" | |
) | |
$creds = Get-Credential -Message "Please enter username and password for $purpose" -UserName $userName | |
$password = SecureStringToString $creds.Password | |
if (-not $creds -or -not $creds.UserName -or -not $password) { | |
throw "Valid credentials required for $purpose" | |
} | |
return @{ | |
UserName = $creds.UserName | |
Password = $password | |
} | |
} | |
function ToArray { | |
[CmdletBinding()] | |
param ( | |
[parameter(mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName)] | |
[object[]] $input | |
) | |
begin { $output = @(); } | |
process { $output += $input; } | |
end { return , $output; } | |
} | |
function ExtendScriptBlock { | |
[CmdletBinding()] | |
param( | |
[parameter(mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName)] | |
[scriptblock] $block, | |
[parameter(mandatory = $true)] | |
[string] $name, | |
[object[]] $boundArgs = $null | |
) | |
$block | | |
Add-Member -MemberType NoteProperty -Name 'Name' -Value $name -PassThru | | |
Add-Member -MemberType NoteProperty -Name 'Args' -Value $boundArgs -PassThru | |
} | |
function SecureStringToString { | |
[CmdletBinding()] | |
param ( | |
[parameter(mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName)] | |
[System.Security.SecureString] $secureString | |
) | |
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) | |
$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) | |
$plaintext | |
} | |
$progressId = 1 | |
function RunActions { | |
[CmdletBinding()] | |
param( | |
[parameter()] | |
[string] $activity, | |
[parameter(mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName)] | |
[object[]] $actions, | |
[parameter()] | |
[object[]] $boundArgs | |
) | |
begin { | |
$script:progressId++ | |
$i = 0 | |
} | |
process { | |
foreach ($f in $actions) { | |
$name = $f.Name | |
if (-not $name) { | |
$name = '<no name>' | |
} | |
try { | |
Write-Progress ` | |
-Activity $activity ` | |
-CurrentOperation $name ` | |
-PercentComplete ($i / $actions.Length * 100) ` | |
-Status "$($i + 1) of $($actions.Length)" ` | |
-Id $progressId | |
$allArgs = $boundArgs | |
if ($f -is [System.Management.Automation.ScriptBlock]) { | |
$allArgs += $f.Args | |
} | |
if ($f -is [System.Management.Automation.FunctionInfo]) { | |
$f = ([System.Management.Automation.FunctionInfo]$f).ScriptBlock | |
} | |
Invoke-Command -ScriptBlock $f -ArgumentList $allArgs | Write-Output | |
} | |
catch { | |
Write-Host " | |
ERROR while executing [$($activity)]: $name ($($i + 1) of $($actions.Length)): | |
$($_.Exception.Message) | |
" -ForegroundColor Red | |
exit 1 | |
} | |
$i++ | |
} | |
} | |
end { | |
Write-Progress -Activity $activity -Completed -Id $progressId | |
$script:progressId-- | |
} | |
} | |
#endregion Scripting | |
#region Windows Interop | |
$_MainWindowHandle = $null | |
function Get-MainWindowHandle { | |
if (-not $Script:_MainWindowHandle) { | |
$win32WindowDefinition = @" | |
using System; | |
using System.Windows.Forms; | |
public class Win32Window : IWin32Window | |
{ | |
public Win32Window(IntPtr handle) | |
{ | |
Handle = handle; | |
} | |
public IntPtr Handle { get; private set; } | |
} | |
"@ | |
Add-Type -TypeDefinition $win32WindowDefinition -ReferencedAssemblies System.Windows.Forms.dll | |
$hwnd = $null | |
$process = [System.Diagnostics.Process]::GetCurrentProcess() | |
while ($null -ne $process -and 0 -eq ($hwnd = $process.MainWindowHandle)) { | |
$process = $process.Parent | |
} | |
$Script:_MainWindowHandle = New-Object Win32Window -ArgumentList $hwnd | |
} | |
$Script:_MainWindowHandle | |
} | |
#endregion Windows Interop | |
#region System | |
function Test-IsAdmin { | |
[CmdletBinding()] | |
$isAdmin = ([Security.Principal.WindowsPrincipal] ` | |
[Security.Principal.WindowsIdentity]::GetCurrent() ` | |
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
if (-not $isAdmin) { | |
throw "Run the script as Administrator" | |
} | |
} | |
function Test-PsVersion { | |
[CmdletBinding()] | |
$minPsVersion = 7 | |
if ($PSVersionTable.PSVersion.Major -lt $minPsVersion) { | |
if ((Read-OptionChoice "System Check" "You PowerShell runtime is too old. Do you want to update it now?" ) -ne 0) { | |
throw "You PowerShell runtime is too old. Please update it first" | |
} | |
Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI -Quiet" -ErrorAction Stop | |
Install-PackageProvider Nuget –Force -ErrorAction Stop | |
Install-Module –Name PowerShellGet –Force -ErrorAction Stop | |
Write-Host " | |
PowerShell, Nuget, and PowerShellGet were updated successfully. | |
Please restert the script again. | |
Exiting... | |
" -ForegroundColor Green | |
exit 2 | |
} | |
} | |
function Test-WindowsFeatures { | |
[CmdletBinding()] | |
param( | |
# Array of Windows feature names | |
[Parameter(Mandatory = $true)] | |
[string[]] $features | |
) | |
$features | | |
ForEach-Object { ExtendScriptBlock { param($name) Get-WindowsOptionalFeature -Online -FeatureName $name } $_ $_ } | | |
ToArray | | |
RunActions -activity 'Checking Windows Features' | |
$disabled = $features | | |
Where-Object { $_.State -ne 'Enabled' } | | |
ForEach-Object { $_.FeatureName } | | |
ToArray | |
if ($disabled.Length -eq 0) { | |
return | |
} | |
if ((Read-OptionChoice -caption 'Windows Features' -message "There are $($disabled.Length) missing Windows Features. Do you want to set up them now?" ) -ne 0) { | |
throw "Required Windows Features missing: $disabled" | |
} | |
$disabled | | |
ForEach-Object { ExtendScriptBlock { param($name) Enable-WindowsOptionalFeature -Online -FeatureName $name | Out-Null } $_ $_ } | | |
ToArray | | |
RunActions -activity 'Enabling Windows Features' | |
} | |
function Test-MSOLEDBSQL { | |
[CmdletBinding()] | |
$msoledbsqlMinVersion = '18.3.0.0' | |
if (Test-Path "C:\WINDOWS\system32\msoledbsql.dll" -PathType Leaf) { | |
$ver = [System.Version](get-item C:\WINDOWS\system32\msoledbsql.dll).VersionInfo.ProductVersion | |
if ($ver -ge [System.Version]$msoledbsqlMinVersion) { | |
return | |
} | |
} | |
if ((Read-OptionChoice "System Check" "A new version of MSOLEDBSQL Driver is required. Do you want to install it now?" ) -ne 0) { | |
throw "You PowerShell runtime is too old. Please update it first" | |
} | |
if ([System.Environment]::Is64BitOperatingSystem) { | |
$OS = 'x64' | |
} | |
else { | |
$OS = 'x86' | |
} | |
$uri = "https://download.microsoft.com/download/A/9/8/A98CF446-A38E-4B0A-A967-F93FAB474AE0/en-US/$($msoledbsqlMinVersion)/$OS/msoledbsql.msi" | |
$out = Join-Path $env:TEMP 'msoledbsql.msi' | |
Invoke-WebRequest -uri $uri -OutFile $out | |
Install-MSI -file $out -quiet | |
} | |
function Install-MSI { | |
[CmdletBinding()] | |
param( | |
[System.IO.FileInfo] $file, | |
[switch] $quiet, | |
[object[]] $MSIArguments | |
) | |
if ($quiet) { | |
$DataStamp = get-date -Format yyyy-MM-ddTHH.mm.ss | |
$logFile = '{0}-{1}.log' -f $file.fullname, $DataStamp | |
Write-Verbose "Quiet Install: $file" | |
Write-Verbose "Log file: $logFile" | |
$ArgumentList = @( | |
"/i" | |
('"{0}"' -f $file.fullname) | |
"/qn" | |
"/norestart" | |
"/L*v" | |
$logFile | |
) | |
if ($MSIArguments) { | |
$ArgumentList += $MSIArguments | |
} | |
$process = Start-Process "msiexec.exe" -ArgumentList $ArgumentList -Wait -NoNewWindow -PassThru | |
if ($process.exitcode -ne 0) { | |
throw "Quiet install failed. Please rerun installation of '$file' without -Quiet switch or ensure you have administrator rights" | |
} | |
} | |
else { | |
if ($MSIArguments) { | |
Start-Process $file.fullname -ArgumentList $MSIArguments -Wait | |
} | |
else { | |
Start-Process $file.fullname -Wait | |
} | |
} | |
} | |
function Enable-ComInternetServices { | |
[CmdletBinding()] | |
$comAdmin = New-Object -com ("COMAdmin.COMAdminCatalog.1") | |
$localhost = $comAdmin.Connect("localhost") | |
$localComputerCollection = $localhost.GetCollection("LocalComputer", $localhost.Name) | |
$localComputerCollection.Populate() | |
$LocalComputer = $localComputerCollection.Item(0) | |
$LocalComputer.Value("CISEnabled") = $true | |
$localComputerCollection.SaveChanges() | Out-Null | |
} | |
function Enable-TLS1 { | |
[CmdletBinding()] | |
$path = "HKCU:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0" | |
@( | |
'Server' | |
'Client' | |
) | | |
ForEach-Object { "$path\$_" } | | |
ForEach-Object { | |
New-Item -Path $_ -Force | Out-Null | |
New-ItemProperty -Path $_ -Force -PropertyType DWORD -Name Enabled -Value 1 | Out-Null | |
New-ItemProperty -Path $_ -Force -PropertyType DWORD -Name DisabledByDefault -Value 0 | Out-Null | |
} | |
} | |
function SignMe { | |
[CmdletBinding()] | |
$cert = @(Get-ChildItem cert:\CurrentUser\My -codesign | Where-Object { $_.Subject.Contains('Vadym_Milichev@epam.com') })[0] | |
if (-not $cert) { | |
throw "Could not find a certificate" | |
} | |
Write-Output "`nSigning $($PSCommandPath)`n" | |
Set-AuthenticodeSignature $PSCommandPath $cert | |
} | |
#endregion System | |
#region IIS | |
function Get-WwwrootDir { | |
Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Microsoft\InetStp -Name 'PathWWWRoot' -ErrorAction SilentlyContinue | |
} | |
function Get-IisInstallationDir { | |
Get-ItemPropertyValue -Path HKLM:\SOFTWARE\Microsoft\InetStp -Name 'InstallPath' -ErrorAction SilentlyContinue | |
} | |
function Test-WwwrootDir { | |
if (-not (Test-Path (Get-WwwrootDir) -PathType Container)) { | |
throw "IIS wwwroot directory not found" | |
} | |
} | |
function Test-AspCgiRestriction { | |
$aspCgiPath = Join-Path (Get-IisInstallationDir) "asp.dll" | |
if (-not (Test-Path -Path $aspCgiPath -PathType Leaf)) { | |
throw "ASP CGI module is not found: $aspCgiPath" | |
} | |
$cgiFilter = 'system.webServer/security/isapiCgiRestriction' | |
$cgi = Get-WebConfiguration "$cgiFilter/add" | Where-Object { [System.Environment]::ExpandEnvironmentVariables($_.path) -like $aspCgiPath } | |
if ($cgi) { | |
if (-not $cgi.allowed) { | |
if ((Read-OptionChoice ` | |
-caption 'Checking Prerequisites' ` | |
-message "Classic ASP CGI Restriction is disabled. Do you want to turn it on now?" ) -ne 0) { | |
throw 'Classic ASP CGI Restriction is disabled' | |
} | |
Set-WebConfigurationProperty ` | |
-pspath 'MACHINE/WEBROOT/APPHOST' ` | |
-filter "$cgiFilter/add[@path='$($cgi.path)']" ` | |
-name "." ` | |
-value @{ | |
Description = $cgi.description | |
Path = $cgi.path | |
Allowed = 'True' | |
} | Out-Null | |
} | |
return | |
} | |
if ((Read-OptionChoice ` | |
-caption 'Checking Prerequisites' ` | |
-message "Classic ASP CGI Restriction missing. Do you want to set up it now?" ) -ne 0) { | |
throw "Classic ASP CGI not installed" | |
} | |
$cgi = @{ | |
Description = 'Active Server Pages' | |
Path = $aspCgiPath | |
Allowed = 'True' | |
} | |
Add-WebConfiguration -pspath 'MACHINE/WEBROOT/APPHOST' -filter $cgiFilter -value $cgi | Out-Null | |
} | |
#endregion IIS |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment