Powershell
Powershell Template
Writing a powershell script just looks much prettier if you start it with this template:
<#
.SYNOPSIS
.DESCRIPTION
.INPUTS
.OUTPUTS
.NOTES
.EXAMPLE
#>
#---------------------------------------------------------[Initialisations]--------------------------------------------------------
#region parameters
#endregion parameters
$ErrorActionPreference = "Stop"
#----------------------------------------------------------[Declarations]----------------------------------------------------------
#region variables
#endregion variables
#-----------------------------------------------------------[Functions]------------------------------------------------------------
#region functions
#endregion functions
#-----------------------------------------------------------[Execution]------------------------------------------------------------
#region script main
#endregion script main
Powershell Module Template
It is by far more preferable to use Powershell modules instead of individual scripts. Modules can easily be installed on a developer workstation or as part of a pipeline run which means all the functions that you add to the module becomes available to you during that pipeline. Ideally you should publish a versioned copy of the module to an artifact store (Azure DevOps has a great one.) and then use that instead. Below is a great example of a template for a powershell module. Using modules helps keeping your code DRY
<#
.SYNOPSIS
Module for whatever you are making a module for.
.EXAMPLE
Import-Module <MODULE_NAME>
.DESCRIPTION
Some text to explain the main theme for the module
.NOTES
Some notes about the module
.LINK
https://docs.driesventer.com
#>
#region Invoke-FunctionA
function Invoke-FunctionA
<#
.SYNOPSIS
Module for whatever you are making the Function for.
.EXAMPLE
Invoke-FunctionA -hello <insert string>
.DESCRIPTION
Some text to explain the main inner workings of the function
.OUTPUT
Describe the output of the function including type
.NOTES
Some notes about the function
#>
{
[CmdletBinding(
# SupportsShouldProcess=$True # enable if you need to support '-whatif'
)]
param (
[parameter(Mandatory=$true)] [string] $hello
)
# return
return Invoke-FunctionA -hello $hello
}
#endregion Invoke-FunctionA
#region Invoke-FunctionB
function Invoke-FunctionB
<#
.SYNOPSIS
Module for whatever you are making the Function for.
.EXAMPLE
Invoke-FunctionA -hello <insert string>
.DESCRIPTION
Some text to explain the main inner workings of the function
.OUTPUT
Describe the output of the function including type
.NOTES
Some notes about the function
#>
{
[CmdletBinding(
# SupportsShouldProcess=$True # enable if you need to support '-whatif'
)]
param (
[parameter(Mandatory=$true)] [string] $hello
)
# return
return Invoke-FunctionB -hello $hello
}
#endregion Invoke-FunctionB
#region Exported Functions
# public (exported)
Export-ModuleMember -function Invoke-FunctionA
Export-ModuleMember -function Invoke-FunctionB
#endregion Exported Functions
General Commands
Tee-Object -Variable
Very few engineers use tee-object -variable command, it is super useful when running scripts that you want to see the output of in script output. Variables commonly get assigned as follows:
$someVariable = "something"
-or
$process = get-process
In the process example above you don't get to see the processes displayed in the output with out explicitly writing the object to the output in a separate command. Like this:
$process = get-process
$process
With the Tee-object you can see the output and capture it as a variable in one go. Like this:
Get-Process | Tee-Object -Variable process
Give it a go.
Arrays
Having this as a reference really helps:
#ARRAY Example
# Reference: https://ss64.com/ps/syntax-arrays.html
$arrayObjects = @("a", "b", "c")
$arrayObjects.GetType()
foreach-object -process {
clear-host
write-host $objectkey
start-sleep -Seconds 2
}
#Add and remove from an array
$Fruits.Add("Kiwi")
$Fruits.Remove("Apple")
Hash Table
$hash = @{}
$hash.Add("Key", "Value")
Azure
Authentication
Create a new Service Principal on Azure
Todo: add some powershell
Login as service principal into Azure
Logon with SP is particularly useful if you want to test if a certain ADO service connection has the correct right to perform a certain action. You will need to create a temp secret on the Azure App Registration object first. Then using the application ID as the user and the secret as the password :
$pscredential = Get-Credential -UserName 60df9dae-1ebf-4b8f-a01b-cab536ed1e7a<Principal_ID>
#Output
PS C:\repos\XLAB_Docs> $pscredential = Get-Credential -UserName 60df9dae-1ebf-4b8f-a01b-cab536ed1e7a
PowerShell credential request
Enter your credentials.
Password for user 60df9dae-1ebf-4b8f-a01b-cab536ed1e7a: ***********************
Then execute the following to get the tenant Id and connect as a service principal:
(Get-AzContext).Tenant.Id | tee-object -variable tenantId
Connect-AzAccount -ServicePrincipal -Credential $pscredential -Tenant $tenantId
Generate a new self signed certificate for Azure VNet Gateway Point to Site
#Create the root cert
$cert = New-SelfSignedCertificate -Type Custom -KeySpec Signature `
-Subject "CN=XlabP2SRootCert" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyUsageProperty Sign -KeyUsage CertSign
# When you export this cert don't export the private key and export as base 64
# Create Client Cert
New-SelfSignedCertificate -Type Custom -DnsName P2SChildCert -KeySpec Signature `
-Subject "CN=XlabP2SClientCert1" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Signer $cert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2")
#when exporting this cert includes the private key and export to pfx
#Create Client Cert from an existing root certificate
$cert =get-ChildItem -Path Cert:\CurrentUser\My\029EE5C879B45753BF70572BC08EBB5B4EEC5B1F # you should be able to tab out the certificate thumbprint this should be the root cert
#Then create a new client certificate that is signed by the root
New-SelfSignedCertificate -Type Custom -DnsName P2SChildCert -KeySpec Signature `
-Subject "CN=XlabP2SClientCert1" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Signer $cert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2")
CodeSnippets
Generate Passwords
function New-RandomPassword {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)] [bool] $sqlFriendly = $false,
[Parameter(Mandatory=$false)] [int] $passwordLength = 16
)
if ($sqlFriendly -ne $false) {
# Generates a complex password that has a limited set of special characters
Write-output "Generating SQL Friendly Complex Password that contains a subset of characters"
$password = ([char[]]([char]33) + [char[]]([char]35..[char]38) + [char[]]([char]35..[char]38) + [char[]]([char]35..[char]38) + [char[]]([char]65..[char]90) + [char[]]([char]97..[char]122) + 0..9 | sort {Get-Random})[0..$passwordLength] -join ''
return $password
}else {
# Generates a complex password
Write-output "Generating Random Complex Password"
$password = ([char[]]([char]33..[char]95) + ([char[]]([char]97..[char]126)) + 0..9 | sort {Get-Random})[0..$passwordLength] -join ''
return $password
}
}
Get all Role Definitions export to CSV
# Get all Azure role definitions
$roles = Get-AzRoleDefinition
# Create an array to hold the data
$data = @()
# Loop through each action and add the data to the array
foreach ($action in $roles.Actions) {
$roleData = [ordered]@{
"Action" = $action
}
# Loop through each role and add it to the role data
foreach ($role in $roles) {
$roleData[$role.Name] = if ($role.Actions -contains $action) { "Yes" } else { "No" }
}
# Add the role data to the array
$data += New-Object PSObject -Property $roleData
}
# Export the data to a CSV file
$data | Export-Csv -Path ".\RolePermissions.csv" -NoTypeInformation
Get Roles for specific actions
function Get-ActionRoles {
param(
[Parameter(Mandatory = $true)][string]$Action
)
<#
.SYNOPSIS
Get all Azure roles that have a specified action
.DESCRIPTION
This function will return all Azure roles that have a specified action.
.PARAMETER Action
The action to search for. This can be a single action or a wildcard.
.EXAMPLE
Get-ActionRoles -Action "Microsoft.Compute/virtualMachines/*"
This example will return all Azure roles that have the action "Microsoft.Compute/virtualMachines/*"
#>
# Get all Azure role definitions
$roles = Get-AzRoleDefinition
# Create an array to hold the data
$data = @()
# Loop through each role and add it to the role data if it has the specified action
foreach ($role in $roles) {
if ($role.Actions -contains $Action) {
$roleData = [ordered]@{
"Role Name" = $role.Name
"Role ID" = $role.Id
"Description" = $role.Description
}
# Add the role data to the array
$data += New-Object PSObject -Property $roleData
}
}
# Output the data
$data | Format-Table -AutoSize
}