Powershell
Nobody’s favorite scripting language
PowerShell
(.ps1
) is bash for Windows (Though ironically now available for Unix variants as well). If you think of PowerShell
as bash
for Windows you won’t go far wrong, but it passes dotnet objects round, not text files.
- Origins
- Use in Azure
- Granting privilage
- TODO: tfsspy
- Reference John. T. Cook
One Liners
get-module -ListAvailable
Import-module pester
Remove-module pester
Running Powershell in iTerm
/usr/local/microsoft/powershell/6/pwsh ; exit;
Version compatability
Always specify the minimum version of PowerShell needed to run
#requires -version 2.0
#
# Always specify the minimum version necessary to run.
#
Null loops
# Instead of
#
{ }
# Use
#
{ $null }
# That way people know you are deliberatly doing nothing.
#
Testing Parameters
function Test-Parameters
{
<#
.SYNOPSIS
Validates an xml file against an xsd file.
.DESCRIPTION
.EXAMPLE
$answer = Validate-Xml $xmlPath $xsdPath $optionalThirdParameter
#>
param
(
[Parameter(Mandatory=$true)]
[string]$xml,
[Parameter(Mandatory=$true)]
[string]$xsdFilePath,
[Parameter(Mandatory=$true)]
[ValidateScript({ Test-Path $_ })]
[string]$DataPath,
[Parameter(Mandatory=$false)]
[ValidateScript({ Test-Path $_ })]
[string]$targetNamespace
)
if(!(Test-Path $xsdFilePath))
{
throw New-Object System.IO.FileNotFoundException "xsd file $xsdFilePath not found."
}
# Do some real stuff.
#
}
Display Objects
Write-Host ($obj | Format-List | Out-String)
or
Write-Host ($obj | Format-List | Out-String)
Who Am I - in Powershell
$rik = ("rik.watson" -eq [Environment]::UserName)
if ($rik)
{
Write-Output "Environment configured for Rik Watson"
$SsdtBiDev = "C:\BuildAndDeploy\Unstable\SsdtBiDev"
$SSISSolutions = "C:\SSISSolutions-TFS2013" # "\SSISSolutions"
# "C:\tfs\C_COMPANY_.Systems\Test\ALM-SN-SSDT-BI\SSISSolutions-TFS2013"
}
else
{
throw "SsdtBiDev & SSISSolutions not set"
}
Modules
if (Get-Module -ListAvailable -Name AzureRM) {
Write-Host "AzureRM Module exists"
}
else {
Write-Host "AzureRM Module doesn't exist, installing"
Install-Module AzureRM -Force -Verbose
}
Ternary Operator
(PowerShell 7.0)
c$ = ($a -eq $b) ? $true : $false
$keyStoreFlags = "PersistKeySet"
if ($privateKeyExportable) {
$keyStoreFlags = "Exportable,PersistKeySet"
}
$keyStoreFlags = ($privateKeyExportable) ? "Exportable,PersistKeySet" : "PersistKeySet"
Pester
Pester is a testing framework for Powershell.
Have a Build.ps1
which looks a little like this:
Debugging in PowerShell
Misc PowerShell scripts
- Get-Assemblies.ps1
Get a list of assemblies available in the runspace - PSClass.ps1
Classes in Powershell - using-powershell-classes-via-PSClass.ps1
Example of using
PSClass.ps1
- Base64.ps1
Base64 decoding - ValidateXmlSchema.ps1
Validates an XML file against its inline provided schema reference - Set-WallpaperText.ps1
Create a new desktop wallpaper from various sources and optionally overlay some text - Demo.ps1
Begin, Process, End
example
TODO: Document this further - Approve-PowershellScripts.ps1
A script to check (approve) basic comformance to coding standards for PowerShell - 101
A Powershell dumping ground
ScriptCop
https://gallery.technet.microsoft.com/ScriptCop-0896dd1e
Perhaps use it’s capabilities to do things like automated testing of scripts.
Run via > Test-Module ScriptCop
Basic
# Comment
<#
Multi-line comment
#>
function Array-Test {
$fruits = @('Apples','Oranges','Bananas')
$fruits += 'Strawberries'
foreach ($fruit in $fruits) {
$fruit
}
}
function Get-TimesResult {
Param ([int]$a,[int]$b)
$c = $a * $b
return $c
}
function Get-TimesResult-Test {
$r = Get-TimesResult -a 5 -b 5
$r
}
Enable remote execution
Set-ExecutionPolicy RemoteSigned
Run a script, script.ps1
via
.\path_to_script\script.ps1
List of available colours
[enum]::GetValues([System.ConsoleColor]) | Foreach-Object {Write-Host $_ -ForegroundColor $_ }
And using one of them
$enabled = $false
if (! $enabled)
{
Write-Host ("Disabled") -ForegroundColor Yellow
return
}
Measureing things
docker images --filter "dangling=true" -q --no-trunc | measure-object
Disk space
Get-WmiObject -Class Win32_logicaldisk
Get-Process | Get-Member
Get-Something | Get-Member
Get-Something | Select-Object -Property *
Get-Something | Select-Object -Property name
Azure & PowerShell
cd $HOME
Get-AzureRmSubscription
Select-AzureRmSubscription –SubscriptionID deadbeef-7515-49db-a399-ae041518faea
Get-AzureRmResourceGroupDeployment -ResourceGroupName default-eun-sizing-sizemanagement-RG | measure | select Count
tidy-deployments.ps1
Param(
[string]
[Parameter(Mandatory = $true)]
$subscriptionId,
[string]
[Parameter(Mandatory = $true)]
$tenantId,
[string]
[Parameter(Mandatory = $true)]
$resourceGroupName,
[int]
[Parameter(Mandatory = $true)]
$numberOfDeploymentsToKeep,
[int]
[Parameter(Mandatory = $true)]
$batchSize
)
try {
$c = Get-AzContext
}
catch {
$c = $null
}
if (!$c -or !$c.Account) {
Connect-AzAccount -Subscription $subscriptionId -Tenant $tenantId
} else {
Select-AzSubscription -Subscription $subscriptionId -Tenant $tenantId
}
# ----------------------------------
# Get Deployments
# ----------------------------------
#$dateBeforeDeleteDeployments = Get-Date -Year 2018 -Month 06 -Day 30
#$deploymentsToDelete = Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName | Where-Object { $_.Timestamp -le $dateBeforeDeleteDeployments }
$currentDeployments = Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName
$currentNumberOfDeployments = ($currentDeployments | Measure-Object).Count
$numberOfDeploymentsToRemove = $currentNumberOfDeployments - $numberOfDeploymentsToKeep
if ($numberOfDeploymentsToRemove -lt 0) {
throw "Number of deployments to remove is < 0..."
}
if ($numberOfDeploymentsToRemove -eq 0) {
Write-Host "Number of deployments to remove is 0..."
return
}
Write-Host "Number of Deployments to remove: '$numberOfDeploymentsToRemove'..."
$deploymentsToDelete = $currentDeployments | Sort-Object -Property Timestamp | Select-Object -First $numberOfDeploymentsToRemove
$deploymentsToDelete | ForEach-Object {$i=0; $j=0; $deploymentsToDeleteBatched=@{}} {
if($i -ne $batchSize -and $deploymentsToDeleteBatched["Batch $j"]) {
$deploymentsToDeleteBatched["Batch $j"]+=$_
$i+=1
}
else {
$i=1
$j+=1
$deploymentsToDeleteBatched["Batch $j"]=@($_)
}
}
Write-Host "Created $($deploymentsToDeleteBatched.Count) batches..."
# ----------------------------------
# Execute deletion in parallel
# ----------------------------------
$jobNames = @()
foreach ($batchkey in $deploymentsToDeleteBatched.Keys) {
$deploymentsToDeleteBatch = $deploymentsToDeleteBatched.$batchkey
$logic = {
Param(
[object]
[Parameter(Mandatory = $true)]
$ctx,
[object]
[Parameter(Mandatory = $true)]
$deploymentsToDeleteBatch,
[string]
[Parameter(Mandatory = $true)]
$resourceGroupName
)
foreach ($deploymentToDelete in $deploymentsToDeleteBatch) {
$deploymentName = $deploymentToDelete.DeploymentName
Remove-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -DefaultProfile $ctx -ErrorAction Stop
Write-Host "Deleted Deployment '$deploymentName' from '$($deploymentToDelete.Timestamp)'..."
}
}
$jobName = ([System.Guid]::NewGuid()).Guid
$jobNames += $jobName
$jobObject = Start-Job $logic -Name $jobName -ArgumentList (Get-AzContext), $deploymentsToDeleteBatch, $resourceGroupName
}
while (Get-Job -State "Running") {
Write-Host "---------------------------------------------------------------"
Write-Host "Jobs still running..."
Get-Job | Format-Table
Write-Host "---------------------------------------------------------------"
Start-Sleep -Seconds 10
}
Write-Host "Jobs completed, getting output..."
Write-Host "---------------------------------------------------------------"
foreach ($jobName in $jobNames) {
Write-Host "Output of Job '$jobName'..."
Receive-Job -Name $jobName
Write-Host "---------------------------------------------------------------"
}
Write-Host "Done..."
Smaller number for ` -batchSize 10 = more batches created each with
batchSize` jobs to complete
az group list --subscription AssetNonProd | jq -r ".[].name"
./tidy-deployments.ps1 –SubscriptionID deadbeef-7515-49db-a399-ae041518faea -tenantId deadbeef-80ee-4819-a9ce-863d5afbea1c -resourceGroupName test-eun-sizing-sizemanagement-RG -numberOfDeploymentsToKeep 100 -batchSize 20
./tidy-deployments.ps1 –SubscriptionID deadbeef-7515-49db-a399-ae041518faea -tenantId deadbeef-80ee-4819-a9ce-863d5afbea1c -resourceGroupName test-euw-sizing-sizemanagement-RG -numberOfDeploymentsToKeep 100 -batchSize 10
Get-AzureRmResourceGroupDeployment -ResourceGroupName test-eun-sizing-sizemanagement-RG | measure