In the early days of Azure, well before the arrival of Bicep, most engineers grappled with deployment automation. ARM templates were tough going and using PowerShell scripts seemed to be a useful alternative approach. We all learned that supporting a production environment based on hundreds of different functions that were continually changing with every version update was impossible. New Azure services required the newest versions of modules which broke existing deployment and support scripts. This scenario was a modern equivalent of "DLL hell".
At the time, many Azure engineers became convinced that Terraform was the answer to this problem. I went down the path of using REST instead. Everyone agreed that PowerShell cmdlets were a terrible option for working with Azure.
Viewing Cloud as JSON
Working with Azure in REST changed my understanding of what the cloud services are and how simply they could be manged. Any object could be retrieved with a simple GET request using it's ID and most objects deployed with a simple POST. The whole Azure environment could be backed up and redeployed using REST and JSON. This led to my Daily Azure / Sentinel Backup (and Reporting) with GitHub project based on a very simple set of functions I shared: https://github.com/LaurieRhodes/AZRest
This very simple approach to Azure let me retrieve Azure services simply.
... and deploy simply as well.
Using this approach, I was the first person in Australia to be able to template Sentinel deployments through code when Sentinel wasn't supported by ARM. Something I was quite proud of at the time! :)
Rediscovering Az PowerShell
Working with REST has been a great skill development for cloud automation. I'm always finding elements with Azure where REST is critical. Something my scripts aren't great with is the lack of support for Token cache with ad-hoc admin tasks. I'm still loathed to return to using all the Azure cmdlets but Get-AzAccessToken with my simple functions provides that token cache support.
Using Az PowerShell, all I need to specify is the resource scope I want a token for and I can continue to just get and push objects as usual.
An example of getting an object is:
# Set Tenant (needed for Az authentication)
$TenantId = ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxx’
# Set Subscription (needed for obtaining latest API versions for object management)
$SubscriptionId = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
# Check if Laurie's PowerShell module is imported
if (!(Get-Module -ListAvailable -Name AZRest)) {
Import-Module "C:\Users\Laurie\Documents\GitHub\AZRest\AZRest.psm1"
}
# Use PowerShell to Connect to the Tenant and Subscription if context doesn't exist
$context = Get-AzContext
if (!$context)
{
Connect-AzAccount -TenantId $tenantId -SubscriptionId $subscriptionId
}
# Set Resource Scope for Azure Management for obtaining a token
$resource = "https://management.azure.com/"
# Retrieve a token for the nominated scope
$token = Get-AzAccessToken -ResourceUrl $resource -AsSecureString
# Convert the SecureString to a plain text string
$plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($token.Token)
)
# Create header for use with Rest calls
$header = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($plainToken)"
}
# Retrieve an up to date list of namespace versions (once per session)
if (!$AzAPIVersions){$AzAPIVersions = Get-AzureAPIVersions -header $header -SubscriptionID $SubscriptionId}
$id = "/subscriptions/xxxxxxxxxxxxxxxx/resourceGroups/xxxxxx/providers/Microsoft.OperationalInsights/workspaces/xxxxxxxxxx"
$object = $null
$object = Get-Azureobject -AuthHeader $Header -apiversions $AzAPIVersions -id $id
Out-File -FilePath "C:\temp\Rhubarb.json" -InputObject (convertto-json -InputObject $object -Depth 10) -Force
Alternately, an example of deploying objects is:
# Set Tenant (needed for Az authentication)
$TenantId = ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxx’
# Set Subscription (needed for obtaining latest API versions for object management)
$SubscriptionId = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
# Check if Laurie's PowerShell module is imported
if (!(Get-Module -ListAvailable -Name AZRest)) {
Import-Module "C:\Users\Laurie\Documents\GitHub\AZRest\AZRest.psm1"
}
# Use PowerShell to Connect to the Tenant and Subscription if context doesn't exist
$context = Get-AzContext
if (!$context)
{
Connect-AzAccount -TenantId $tenantId -SubscriptionId $subscriptionId
}
# Set Resource Scope for Azure Management for obtaining a token
$resource = "https://management.azure.com/"
# Retrieve a token for the nominated scope
$token = Get-AzAccessToken -ResourceUrl $resource -AsSecureString
# Convert the SecureString to a plain text string
$plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($token.Token)
)
# Create header for use with Rest calls
$header = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($plainToken)"
}
# Retrieve an up to date list of namespace versions (once per session)
if (!$AzAPIVersions){$AzAPIVersions = Get-AzureAPIVersions -header $header -SubscriptionID $SubscriptionId}
$file = "C:\temp\Rhubarb.json"
Get-jsonfile -Path $file | Push-Azureobject -authHeader $header -apiversions $AzAPIVersions
The other nice benefit of working with Azure and REST this way is that the process of getting and pushing JSON representations of Azure services to the cloud uses PowerShell object representations of services along the way. That gives me the ability to do some complex coding against Azure while knowing that I have little exposure to code changes in future releases of Az!
- Log in to post comments