Skip to content

Instantly share code, notes, and snippets.

@milichev
Created April 16, 2020 13:34
Show Gist options
  • Save milichev/0334845581f92911d8e45e4fb5b8becf to your computer and use it in GitHub Desktop.
Save milichev/0334845581f92911d8e45e4fb5b8becf to your computer and use it in GitHub Desktop.
PowerShell utilities
#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