diff options
Diffstat (limited to 'AzureHelpers/Public')
-rw-r--r-- | AzureHelpers/Public/Find-AzVm.ps1 | 69 | ||||
-rw-r--r-- | AzureHelpers/Public/Get-AzAccountList.ps1 | 22 | ||||
-rw-r--r-- | AzureHelpers/Public/List-AzGroups.ps1 | 23 | ||||
-rw-r--r-- | AzureHelpers/Public/List-AzVms.ps1 | 25 | ||||
-rw-r--r-- | AzureHelpers/Public/Login-AzSubscription.ps1 | 138 | ||||
-rw-r--r-- | AzureHelpers/Public/Show-AzGroup.ps1 | 57 | ||||
-rw-r--r-- | AzureHelpers/Public/Show-AzVm.ps1 | 64 | ||||
-rw-r--r-- | AzureHelpers/Public/Start-AzVm.ps1 | 44 | ||||
-rw-r--r-- | AzureHelpers/Public/Stop-AzVm.ps1 | 57 |
9 files changed, 499 insertions, 0 deletions
diff --git a/AzureHelpers/Public/Find-AzVm.ps1 b/AzureHelpers/Public/Find-AzVm.ps1 new file mode 100644 index 0000000..73467c0 --- /dev/null +++ b/AzureHelpers/Public/Find-AzVm.ps1 @@ -0,0 +1,69 @@ +function Find-AzVm {
+ <#
+ .SYNOPSIS
+ Return an Azure VM object if we find exactly one match for a given VM name string.
+
+ .DESCRIPTION
+ We'll search for all VMs inside the subsctription we are logged into containing
+ the input string. If we find exactly one match, we will output an object as
+ delivered by `az vm list`.
+
+ .EXAMPLE
+ Find-AzVm -VmName "substring-01"
+
+ .INPUTS
+ String. The VM name we want to investigate. Part of the name is sufficient if
+ unambiguous inside the active subscription.
+
+ .OUTPUTS
+ PSCustomObject. `az vm list` object of the resulting Azure virtual machine.
+ #>
+ [OutputType([PSCustomObject])]
+ [Alias(
+ 'azvmidentify',
+ 'azvmf',
+ 'azvmi'
+ )]
+ Param (
+ [Parameter(
+ Mandatory=$true,
+ ValueFromPipeline=$true,
+ HelpMessage="String that is a VM name or is part of one unambiguous VM",
+ Position=0
+ )
+ ]
+ [ValidateLength(1,64)]
+ [string]
+ $VmName
+ )
+ $ErrorActionPreference = 'Stop'
+ $count = 0
+ $result = @()
+ foreach ($myvm in (List-AzVms | ConvertFrom-Json) ) {
+ if ($myvm.name.Contains($VmName)) {
+ $count++
+ $result += $myvm
+ }
+ }
+ switch ($result.Count) {
+ 0 {
+ throw [System.ArgumentNullException]::New("No VM found with its name containing `"$($VmName)`"")
+ }
+ 1 {
+ $true | Out-Null
+ }
+ Default {
+ throw [System.ArgumentException]::New("More than one VM found with their names containing `"$($VmName)`"")
+ }
+ }
+ # We throw exceptions whenever we don't have exactly one element - here we return the first element as there
+ # should be only one at this stage.
+ # Also, Powershell (again!): If you "return" something from a function anything else that produces output
+ # will also be returned. Why, Microsoft, why ლ(ಠ_ಠლ)
+ # > PowerShell has really wacky return semantics - at least when viewed from a more traditional programming perspective. There are two main ideas to wrap your head around:
+ # > * All output is captured, and returned
+ # > * The return keyword really just indicates a logical exit point
+ # ( https://stackoverflow.com/a/10288256 )
+ # This sorry state of a "shell" or "programming language" ...
+ return $result[0]
+}
\ No newline at end of file diff --git a/AzureHelpers/Public/Get-AzAccountList.ps1 b/AzureHelpers/Public/Get-AzAccountList.ps1 new file mode 100644 index 0000000..d49d8e4 --- /dev/null +++ b/AzureHelpers/Public/Get-AzAccountList.ps1 @@ -0,0 +1,22 @@ +function Get-AzAccountList { + <# + .SYNOPSIS + Show details about all subscriptions the Azure account we are logged into has access to. + + .INPUTS + None. + + .OUTPUTS + String. A coloured JSON output showing a more or less terse list of subscriptions. + #> + [Alias( + 'getazacclist', + 'azacclist', + 'azalist' + )] + # PowerShell will throw an exception "Unexpected attribute 'Alias'." if you don't define Param() below. If you do, everything is fine. + # POWERSHELL IS SO SOPHISTICATED AND GOOD, the number of times I've heard this bollocks definitely equals the quality + Param( + ) + az account list -o jsonc --query '[].{name: name, id: id, state: state, homeTenantId: homeTenantId, tenantId: tenantId, user: user}' +}
\ No newline at end of file diff --git a/AzureHelpers/Public/List-AzGroups.ps1 b/AzureHelpers/Public/List-AzGroups.ps1 new file mode 100644 index 0000000..a280388 --- /dev/null +++ b/AzureHelpers/Public/List-AzGroups.ps1 @@ -0,0 +1,23 @@ +function List-AzGroups { + <# + .SYNOPSIS + List all resource groups inside the Azure subscription we are logged into. + + .DESCRIPTION + This simply uses `az group list` with a few parameters. Main purpose: coloured + and terse output. + + .INPUTS + None. (Also, no parameters.) + + .OUTPUTS + String. A coloured JSON output showing a more or less terse list of VMs. + #> + [Alias( + 'azgrouplist', + 'azrgl' + )] + Param ( + ) + az group list -o jsonc --query '[].{id: id, location: location, managedBy: managedBy, properties: properties, tags: tags}' +}
\ No newline at end of file diff --git a/AzureHelpers/Public/List-AzVms.ps1 b/AzureHelpers/Public/List-AzVms.ps1 new file mode 100644 index 0000000..8c88a63 --- /dev/null +++ b/AzureHelpers/Public/List-AzVms.ps1 @@ -0,0 +1,25 @@ +function List-AzVms { + <# + .SYNOPSIS + List all VMs inside the Azure subscription we are logged into. + + .DESCRIPTION + This simply uses `az vm list` with a few parameters. Main purpose: coloured + and terse output. + + .INPUTS + None. (Also, no parameters.) + + .OUTPUTS + String. A coloured JSON output showing a more or less terse list of VMs. + #> + [Alias( + 'azvmlist', + 'azvml' + )] + # PowerShell will throw an exception "Unexpected attribute 'Alias'." if you don't define Param() below. If you do, everything is fine. + # POWERSHELL IS SO SOPHISTICATED AND GOOD, the number of times I've heard this bollocks definitely equals the quality + Param( + ) + az vm list -o jsonc --query '[].{name: name,resourceGroup: resourceGroup,tenantId: identity.tenantId,principalId: identity.principalId}' +}
\ No newline at end of file diff --git a/AzureHelpers/Public/Login-AzSubscription.ps1 b/AzureHelpers/Public/Login-AzSubscription.ps1 new file mode 100644 index 0000000..b02229f --- /dev/null +++ b/AzureHelpers/Public/Login-AzSubscription.ps1 @@ -0,0 +1,138 @@ +function Login-AzSubscription { + <# + .SYNOPSIS + Log in to a predefined set of tenants+subscription combinations. + + .DESCRIPTION + The sets define abstract subscription "shortnames" which will also + yield its tenant. To find out which just use tabulator expansion. + + This function eases the login procedure. While Azure and AzureCLI + are still beta implementations, we need to work around a lot of + stuff, which is why this function has grown quite some in size. + Its usage will remain easy, however :-) + + We expect ./private/vars.ps1 to be populated. It consists of: + - [Hashtable]$tenantMap = @{ subscrName = tenantUUID; ... } + - [Hashtable]$subscrMap = @{ subscrName = subscriptionUUID; ... } + (subscrName is a name chosen by you, it can be the actual name or + some mnemonic, whatever you prefer. The UUID(GUID) is the one in Azure.) + + .EXAMPLE + Login-AzSubscription mysubscription + + .PARAMETER subscrName + The subscription name. Its mapping has to exist in ./private/vars.ps1 + (the module containing this function is delivered with an example). + #> + [Alias( + 'Login-AzTenant', + 'azlogin' + )] + Param ( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + HelpMessage="The name (our alias) of the subscription you intend to login to.", + Position=0 + ) + ] + [ValidateLength(1,64)] + [string] + # [azSubscriptions] + $subscrName + # $subscrEnum + ) + # # Since PowerShell Enum doesn't give a fuck about sub-types, we better cast to string here. + # # Before, we tried to access $tenantMap[$subscrEnum] below and failed, so better keep it that way :-) + # $subscrName = [String]$subscrEnum + [regex]$uuidRex = '(?im)^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$' + if ( -not $tenantMap.Contains($subscrName) ) { + Write-Host "Exception. Did you populate /private/vars.ps1?" + throw [System.ArgumentException]::New("Known tenants do not include `"$($SubscrName)`"!") + } + if ( -not $subscrMap.Contains($subscrName) ) { + Write-Host "Exception. Did you populate /private/vars.ps1?" + throw [System.ArgumentException]::New("Known subscriptionss do not include `"$($SubscrName)`"!") + } + # PowerShell again: The below simple statement in any other language would work. Here you receive + # "System.Collections.Hashtable[learn]" instead of the value string assigned to key $TenantName: + # az login --tenant $tenantMap[$TenantName] + # ...here, we need an additional detour and assignment (WiP): + $tuuid = [String]$tenantMap[$subscrName] + if ( -not ($tuuid -match $uuidRex) ) { + # PowerShell again. Cannot just combine strings with strings directly (like "value of variable: ${variable}, right here"). + # Most languages can. PS can't. + $throwstr = "Tenant ID string is not a UUID! (" + $throwstr += $tuuid + $throwstr += ")" + throw [System.ArgumentException]::New($throwstr) + } + $suuid = [String]$subscrMap[$subscrName] + if ( -not ($suuid -match $uuidRex) ) { + # PowerShell again. Cannot just combine strings with strings directly (like "value of variable: ${variable}, right here"). + # Most languages can. PS can't. + $throwstr = "Subscription ID string is not a UUID! (" + $throwstr += $suuid + $throwstr += ")" + throw [System.ArgumentException]::New($throwstr) + } + if ( ((az account list --only-show-errors -o json) | ConvertFrom-Json).Count -ne 0 ) { + Write-Host "Already logged in to an Azure subscription." + # For some very odd reason (bad coding in PS?* :-) ), this output would appear LAST. meaning after the last call of the function. (wtf?) + # * or do the round brackets do some kind of subshelling? other than what people told me? to be researched. + # (az account show --only-show-errors -o json | ConvertFrom-Json) | Select-Object tenantId,name + # --- + # Here's the thing with AzureCLI as a whole: Microsoft don't even adhere to their own frickin standards. If "az something"* fails, they won't throw an exception, and + # "az account" does not know about "-ErrorAction" (which is a de-facto standard!). Finally, PowerShell itself does not use error codes like everybody else, so you're stuck + # on some output to interpret. Morons. We hence just try to assign a variable to this command, and if the variable is empty, tadaaa error. Lel. + # * no, seriously, try "az something". "something" is unknown, and even then there is no darn exception**, just red text. No try/catch possible. Hilarious. + # ** Even better, "az something" does not complain about "-ErrorAction", it simply ignores it in this case. Absolute pros at work :D ("az account" at least states it does not adhere to that...) + $mytok = (az account get-access-token -o json 2>$null | ConvertFrom-Json ) + if ( $mytok -eq $null ) { + [Console]::ForegroundColor = 'red' + Write-Host "Token possibly invalid, AzureCLI has no means of renewing tokens (sic)," + Write-Host "hence logging you out." + [Console]::ResetColor() + az logout + Write-Host "...done. Please log in again." + } else { + # Do not have sensitive data lingering about: + Clear-Variable mytok + # Set subscription: + Write-Host "Setting active subscription to $($suuid)...`n" + az account set -o jsonc --subscription $suuid + } + } else { + $loginex = $("" | Out-String) + $loginex += "Not logged in, trying to log in.`n" + Write-Host $loginex + Write-Host "Tenant: $tuuid" + Write-Host "Subscription: `"$SubscrName`" = $tuuid" + if ( $tuuid -ne $null ) { + # TODO: research --use-device-code + # We cannot log in stating we are azure user user@whatnot, this would lead to not using MFA. + # Great design, Microsoft, I just want to enter my creds but not the frickin username every time. -.-' + # (Also, what about user-based vaulting (incl. an Enterprise Vault) and then using a TOTP? + # Microsoft won't even do a proper TOTP, they hide that behind their "Authenticator" (which is EFFING MANDATORY). + # Also, Microsoft take a huge dump on established standards like the aforementioned, or browser plugins for vaults. + # And many people still think this is good design. ಠ_ಠ (Do you?) + # ) + # az login --username (Get-ConsolutAzLoginName) --tenant $tuuid + # ...so back to using that UI designed for noobs (its existence isn't bad - dictating it is, and makes devs more inefficient.) + # TODO: Can we work around with managed identities? And should we consider this at all, from a security perspective? + Write-Host "Logging in..." + az login -o jsonc --tenant $tuuid + Write-Host "...logged in." + Write-Host "Setting active subscription to $($suuid)..." + az account set -o jsonc --subscription $suuid + } else { + # PowerShell is just sitting on the zombie hydra that .NET is. --> https://powershellexplained.com/2017-04-07-all-dotnet-exception-list/#systemargumentnullexception + throw [System.ArgumentNullException]::New("UUID for tenant not found: $SubscrName") + } + } + Write-Host "Access token:" + az account get-access-token -o jsonc --query '{expiresOn: expiresOn, expires_on: expires_on, subscription: subscription, tenant: tenant, tokenType: tokenType}' + Write-Host "Active subscription:" + az account show -o jsonc --query '{name: name, id: id, tenantId: tenantId, user: user}' +}
\ No newline at end of file diff --git a/AzureHelpers/Public/Show-AzGroup.ps1 b/AzureHelpers/Public/Show-AzGroup.ps1 new file mode 100644 index 0000000..7de8bfa --- /dev/null +++ b/AzureHelpers/Public/Show-AzGroup.ps1 @@ -0,0 +1,57 @@ +function Show-AzGroup { + <# + .SYNOPSIS + List details of an Azure resource group containing an input string. + + .DESCRIPTION + We'll find out all resource group inside the subscription we are logged into + which contain the string $GroupName (i.e. 'yGrou' will yield 'myGroup'). + + If we find more than one result, we will throw an exception telling this. + + If there is one match, a more or less terse output will be generated displaying + the group details. + + .INPUTS + String. The resource group name we want to investigate. Part of the name is + sufficient if unambiguous inside the active subscription. + + .OUTPUTS + String. A coloured JSON output showing a more or less terse list of + the resource group's parameters. + #> + [Alias( + 'azgroup', + 'azrg' + )] + Param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + HelpMessage="String that is a resource group name or is part of one unambiguous RG", + Position=0 + ) + ] + [ValidateLength(1,64)] + [string] + $GroupName + ) + $groups = @() + foreach ($group in (List-AzGroups)) { + if ($group.name.Contains($GroupName)) { + $groups += $group + } + } + switch ($groups.Count) { + 0 { + throw [System.ArgumentNullException]::New("No resource group found with its name containing `"$($GroupName)`"") + } + 1 { + $true | Out-Null + } + Default { + throw [System.ArgumentException]::New("More than one resource group found with their names containing `"$($GroupName)`"") + } + } + az group show -g $group[0].name --query '{id: id, location: location, managedBy: managedBy, properties: properties, tags: tags}' +}
\ No newline at end of file diff --git a/AzureHelpers/Public/Show-AzVm.ps1 b/AzureHelpers/Public/Show-AzVm.ps1 new file mode 100644 index 0000000..6021015 --- /dev/null +++ b/AzureHelpers/Public/Show-AzVm.ps1 @@ -0,0 +1,64 @@ +function Show-AzVm { + <# + .SYNOPSIS + List all details of an Azure VM whose name contains an input string. + + .DESCRIPTION + We'll find out all VMs inside the subscription we are logged into + which contain the string $VmName (i.e. 'yVirtua' will yield 'myVirtualMachine'). + + If we find more than one result, we will throw an exception telling this. + + If there is one match, a more or less terse output will be generated displaying + the VM details. + + .INPUTS + String. The VM name we want to investigate. Part of the name is sufficient if + unambiguous inside the active subscription. + + .OUTPUTS + String. A coloured JSON output showing a more or less terse list of + the virtual machine's parameters. + #> + [Alias( + 'azvmdeets', + 'azvmdetails', + 'azvmd' + )] + Param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + HelpMessage="Exact name of a VM", + Position=0 + ) + ] + [ValidateLength(1,64)] + [string] + $VmName + ) + foreach ($myvm in $VmName) { + # az vm list -d -o jsonc --query "[?name == `'$myvm`']" ` + # | ConvertFrom-Json ` + # | Select-Object name,powerState,privateIps,publicIps,resourceGroup,tags + # Something like a zone also doesn't come with list -d, we need show for THAT, + # and show needs the resource group. + # also, az vm list -d is raging slow (5+ seconds at times). + # ALSO, az vm show -d is raging slow. + # We need one of these to see BASIC stuff like IPs or power state. Cheerio. + # So: list the VM with the name, extract resource group, switch to az vm show. + $resvms = (az vm list -d -o jsonc --query "[?name == `'$myvm`']" | ConvertFrom-Json) ` + | Select-Object name,resourceGroup,powerState,privateIps,publicIps + if ( [String]$resvms.GetType() -ne 'System.Management.Automation.PSCustomObject' ) { + foreach ($resvm in $resvms) { + az vm show -d -g $resvm.resourceGroup -n $resvm.name -o jsonc ` + --query '{name: name, id: id, powerState: powerState, resourceGroup: resourceGroup, zones: zones, privateIps: privateIps, publicIps: publicIps, tags: tags}' + # $resvm | Select-Object powerState,privateIps,publicIps + } + } else { + az vm show -d -g $resvms.resourceGroup -n $resvms.name -o jsonc ` + --query '{name: name, id: id, powerState: powerState, resourceGroup: resourceGroup, zones: zones, privateIps: privateIps, publicIps: publicIps, tags: tags}' + # $resvms | Select-Object powerState,privateIps,publicIps + } + } +}
\ No newline at end of file diff --git a/AzureHelpers/Public/Start-AzVm.ps1 b/AzureHelpers/Public/Start-AzVm.ps1 new file mode 100644 index 0000000..408fb0c --- /dev/null +++ b/AzureHelpers/Public/Start-AzVm.ps1 @@ -0,0 +1,44 @@ +function Start-AzVm { + # Supply a VM name (or an unambiguous part of it) and start the machine. + <# + .SYNOPSIS + Start an Azure VM whose name contains the input string. + + .DESCRIPTION + If we can unambiguously determine a single VM name inside the subscription + we are logged into through the input string, we will stop and deallocate + the machine. + + This function was built as we want to have terse input and a counterpart + to Stop-AzVm. + + .INPUTS + String. The name or unambiguous part of the name of the VM we intend to + start. + + .OUTPUTS + String. Status messages and the actual AzureCLI outputs. + #> + Param ( + [Parameter( + Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + HelpMessage="String that is a VM name or is part of one unambiguous VM", + Position=0 + ) + ] + [ValidateLength(1,64)] + [string] + $VmName + ) + $ErrorActionPreference = 'Stop' + $myvm = azvmidentify -VmName $VmName + # Since az vm start is taking quite its time even when the machine is started, we should check ourselves whether the machine is running: + if ( (az vm show -d -g $myvm.resourceGroup -n $myvm.name -o json | COnvertFrom-Json).powerState.Contains('running')) { + Write-Host "VM $($myvm.name) (RG: $($myvm.resourceGroup)) is already running." + } else { + Write-Host "Starting $($myvm.name) (RG: $($myvm.resourceGroup)):" + az vm start -g $myvm.resourceGroup -n $myvm.name + Write-Host "...done." + } +}
\ No newline at end of file diff --git a/AzureHelpers/Public/Stop-AzVm.ps1 b/AzureHelpers/Public/Stop-AzVm.ps1 new file mode 100644 index 0000000..1b3bd07 --- /dev/null +++ b/AzureHelpers/Public/Stop-AzVm.ps1 @@ -0,0 +1,57 @@ +function Stop-AzVm { + # Supply a VM name (or an unambiguous part of it), stop, and deallocate the machine. + <# + .SYNOPSIS + Stop **and** deallocate an Azure VM whose name contains the input string. + + .DESCRIPTION + If we can unambiguously determine a single VM name inside the subscription + we are logged into through the input string, we will stop and deallocate + the machine. + + This function was built as we want to have terse input and we do not want to + be billed for machines we stop, the latter requiring two actual commands. + + .INPUTS + String. The name or unambiguous part of the name of the VM we intend to + stop and deallocate. + + .OUTPUTS + String. Status messages and the actual AzureCLI outputs. + #> + [Alias( + 'azvmstop', + 'azvmd', + 'Deallocate-AzVm' + )] + Param ( + [Parameter( + Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + HelpMessage="String that is a VM name or is part of one unambiguous VM", + Position=0 + ) + ] + [ValidateLength(1,64)] + [string] + $VmName + ) + $ErrorActionPreference = 'Stop' + $myvm = azvmidentify -VmName $VmName + # Since az vm stop is taking quite its time even when the machine is started, we should check ourselves whether the machine is running: + $myPowerState = (az vm show -d -g $myvm.resourceGroup -n $myvm.name -o json | ConvertFrom-Json).powerState + if ( ($myPowerState -match '(stopped|deallocated)$') ) { + Write-Host "VM $($myvm.name) (RG: $($myvm.resourceGroup)) is already stopped." + } else { + Write-Host "Stopping $($myvm.name) (RG: $($myvm.resourceGroup)):" + az vm stop -g $myvm.resourceGroup -n $myvm.name + Write-Host "...done." + } + if ( ($myPowerState -match 'deallocated$') ) { + Write-Host "VM $($myvm.name) (RG: $($myvm.resourceGroup)) is already deallocated." + } else { + Write-Host "Deallocating $($myvm.name) (RG: $($myvm.resourceGroup)):" + az vm deallocate -g $myvm.resourceGroup -n $myvm.name + Write-Host "...done." + } +}
\ No newline at end of file |