Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge dev to main #1

Merged
merged 14 commits into from
May 31, 2022
Next Next commit
init
  • Loading branch information
szymonos committed May 15, 2022
commit f53508f8e87be8c3ab6a11eeed47bd3175c5c37e
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# To use this file you must have EditorConfig installed for your editor.
# For Visual Studio Code, install the extension: "EditorConfig for VS Code"
# https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig
root = true

[*]
indent_style = space
insert_final_newline = true
end_of_line = lf

[*.{ps1,psd1,psm1}]
indent_size = 4
trim_trailing_whitespace = true
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# personal
.tmp/
_*.*

# User-specific files
*.rsuser
*.suo
Expand Down
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"aaron-bond.better-comments",
"DavidAnson.vscode-markdownlint",
"EditorConfig.EditorConfig",
"ms-vscode.powershell",
]
}
34 changes: 34 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "PowerShell",
"request": "launch",
"name": "PowerShell Interactive Session",
"cwd": "${workspaceRoot}"
},
{
"type": "PowerShell",
"request": "launch",
"name": "PowerShell Pester Tests",
"script": "Invoke-Pester",
"args": [],
"cwd": "${workspaceRoot}/test"
},
{
"type": "PowerShell",
"request": "launch",
"name": "PowerShell Launch (current file)",
"script": "${file}",
"args": [],
"cwd": "${file}"
},
{
"type": "PowerShell",
"request": "attach",
"name": "PowerShell Attach to Host Process",
"processId": "${command:PickPSHostProcess}",
"runspaceId": 1
}
]
}
33 changes: 33 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"[powershell]": {
"editor.semanticHighlighting.enabled": true
},
"editor.insertSpaces": true,
"editor.rulers": [
120
],
"editor.tabSize": 4,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"powershell.codeFormatting.addWhitespaceAroundPipe": true,
"powershell.codeFormatting.autoCorrectAliases": true,
"powershell.codeFormatting.preset": "OTBS",
"powershell.codeFormatting.trimWhitespaceAroundPipe": true,
"powershell.codeFormatting.useConstantStrings": true,
"powershell.codeFormatting.useCorrectCasing": true,
"powershell.codeFormatting.whitespaceBetweenParameters": true,
"powershell.integratedConsole.showOnStartup": false,
"powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1",
"markdownlint.config": {
"MD013": false,
"MD024": false,
"MD029": false,
"MD033": {
"allowed_elements": [
"kbd"
]
},
"MD034": false,
"MD038": false
}
}
55 changes: 55 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"windows": {
"options": {
"shell": {
"executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-Command"
]
}
}
},
"linux": {
"options": {
"shell": {
"executable": "/usr/bin/pwsh",
"args": [
"-NoProfile",
"-Command"
]
}
}
},
"osx": {
"options": {
"shell": {
"executable": "/usr/local/bin/pwsh",
"args": [
"-NoProfile",
"-Command"
]
}
}
},
// Associate with test task runner
"tasks": [
{
"label": "Test",
"type": "shell",
"command": "Import-Module Pester -MinimumVersion 5.0.0; $c = [PesterConfiguration]::Default; $c.Run.Path = 'test'; $c.Output.Verbosity = 'Detailed'; $c.Debug.WriteVSCodeMarker = $true; Invoke-Pester -Configuration $c",
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": [
"$pester"
]
}
]
}
18 changes: 18 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<#
.SYNOPSIS
Install the module.
.EXAMPLE
./install.ps1
#>
# get module path in user context
$installPath = Join-Path $env:PSModulePath.Split("$($IsWindows ? ';' : ':')")[0] -ChildPath 'ps-szymonos'

# create/cleanup destination directory
if (Test-Path $installPath) {
Remove-Item "$installPath/*" -Recurse -Force
} else {
New-Item -ItemType Directory -Force -Path $installPath | Out-Null
}

# copy module files
Copy-Item -Path "$PSScriptRoot/src/*" -Destination $installPath -Recurse -Force
191 changes: 191 additions & 0 deletions src/Functions.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<#
.SYNOPSIS
Get index of a value in provided array from selection menu.
.PARAMETER Array
Array of strings to get the selection menu.
.PARAMETER Message
Optional menu header to display.
#>
function Get-ArrayIndexMenu {
[CmdletBinding()]
param (
[Alias('a')]
[Parameter(Mandatory)]
[string[]]$Array,

[Alias('m')]
[string]$Message
)

# create selection menu
$msg = (
"`n`e[4m$($Message ? $Message : 'Select option')`e[0m`n " +
$Array.ForEach({ "[$([array]::IndexOf($Array, $_))] - $_`n" })
)

# get selection
do {
$i = Read-Host -Prompt $msg
} until ($i -and $i -in 0..($Array.Count - 1))

# return array index
return $i
}

<#
.SYNOPSIS
Retry executing command if fails on HttpRequestException.
.PARAMETER Script
Script block of commands to execute.
#>
function Invoke-CommandRetry {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[scriptblock]$Script
)
do {
try {
Invoke-Command -ScriptBlock $Script -ErrorAction Stop
$exit = $true
} catch [System.Net.Http.HttpRequestException] {
Write-Verbose 'Retrying...'
} catch {
Write-Error $_
$exit = $true
}
} until ($exit)
}

<#
.SYNOPSIS
Set Az Context and eventually connect to Azure.
.PARAMETER Subscription
Subscription Name or ID.
#>
function Connect-AzContext {
param (
[Alias('s')]
[string]$Subscription
)
$ctx = Get-AzContext
if ($ctx) {
if ($Subscription -and $Subscription -notin @($ctx.Subscription.Id, $ctx.Subscription.Name)) {
$ctx = Invoke-CommandRetry {
Set-AzContext -Subscription $Subscription -Tenant $ctx.Subscription.TenantId
}
}
} else {
$ctx = Invoke-CommandRetry {
if ($Subscription) {
try {
(Connect-AzAccount -Subscription $Subscription -WarningAction Stop).Context
} catch {
(Connect-AzAccount -Subscription $Subscription -UseDeviceAuthentication).Context
}
} else {
try {
(Connect-AzAccount -WarningAction Stop).Context
} catch {
(Connect-AzAccount -UseDeviceAuthentication).Context
}
}
}
}

return $ctx
}

<#
.SYNOPSIS
Set subscription context from selection menu.
#>
function Set-SubscriptionMenu {
# query graph api for subscriptions
$tenantId = (Get-AzContext).Subscription.TenantId
$query = "ResourceContainers | where type =~ 'microsoft.resources/subscriptions' | project name, subscriptionId"
$subscriptions = Search-AzGraph -Query $query -ManagementGroup $tenantId | Sort-Object name

# select subscription from menu
$i = Get-ArrayIndexMenu -Array $subscriptions.name -Message 'Select subscription'
$sub = (Connect-AzContext $subscriptions[$i].subscriptionId).Subscription

return $sub
}

Set-Alias -Name ssm -Value Set-SubscriptionMenu

<#
.SYNOPSIS
Get Azure token from current context.
#>
function Get-AzAccessToken {
# get current context
$ctx = Connect-AzContext

# get token
$azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azProfile)
$token = $profileClient.AcquireAccessToken($ctx.Subscription.TenantId)

return $token
}

<#
.SYNOPSIS
Send Azure API request.
.PARAMETER ResourceId
Resource ID of the resource to query.
.PARAMETER ApiVersion
API version of the resource to query.
.PARAMETER Output
Output format.
#>
function Get-AzApiRequest {
[CmdletBinding()]
param (
[Alias('r')]
[Parameter(Mandatory, ValueFromPipeline)]
[string]$ResourceId,

[Alias('a')]
[Parameter(Mandatory)]
[string]$ApiVersion,

[Alias('o')]
[ValidateSet('json', 'jsonc', 'object')]
[string]$Output = 'object'
)

begin {
$responseList = [Collections.Generic.List[PSCustomObject]]::new()
$params = @{
Method = 'Get'
Authentication = 'Bearer'
Token = (Get-AzAccessToken).AccessToken | ConvertTo-SecureString -AsPlainText -Force
Headers = @{ 'Content-Type' = 'application/json' }
Body = @{ 'api-version' = $ApiVersion }
}
}

process {
$response = Invoke-CommandLoop {
Invoke-RestMethod @params -Uri "https://management.azure.com$ResourceId"
}
$responseList.Add($response)
}

end {
switch ($Output) {
{ $_ -eq 'object' } {
$responseList
}
{ $_ -in 'json' } {
$responseList | ConvertTo-Json -Depth 5
}
{ $_ -eq 'jsonc' } {
$responseList | ConvertTo-Json -Depth 5 | jq
}
}
}
}
Loading