Sometimes I need a zero-install way to interact with Azure. I have no specific Azure utilities at hand, no Python, no nothing. Usually, Azure management is done using PowerShell, the az cli or, if you want raw REST calls, the armclient. But for my customer, even can be too much ceremony.
So the question was how can I get going with purely bash, cURL and jq for JSON parsing, and potentially yq and xq for YAML/XML parsing.
If you're running inside a VM, with Managed Identity enabled, you can easily fetch a token. But unfortunately the VM wasn't authorized to hit the resource I care about.
Next stop service principals. Problem is customer's AD admin team running a tough regime, and don't hand out service principals.
So ultimately, how can I get my actual AAD user identity avail in the shell? In the end, all I need is a bearer token.
Let's dive right in:
A few variables first
I want to authN against 'my' Azure AD tenant, and want to hit the Azure ARM REST API.
Doing a device login (AAD v2)
For the full user login, i.e. device authN, here's what happens under the hood: The code needs to fetch a device code, and then use that code to poll and validate whether the user authenticated.
If you wanna snoop on cURL's requests with something like fiddler, you should add this --proxy http://127.0.0.1:8888/ --insecure to the calls.
Assuming we have a 'real' service principal, we can do this:
#!/bin/bashaadTenant="chgeuerfte.onmicrosoft.com"SAMPLE_SP_APPID="*** put your service principal application ID here ***"SAMPLE_SP_KEY="*** put your service principal application secret here ***"# resource="https://management.azure.com/"resource="https://storage.azure.com/"access_token="$(curl \--silent \--requestPOST \--url "https://login.microsoftonline.com/${aadTenant}/oauth2/token" \--data-urlencode "grant_type=client_credentials" \--data-urlencode "client_id=${SAMPLE_SP_APPID}" \--data-urlencode "client_secret=${SAMPLE_SP_KEY}" \--data-urlencode "resource=${resource}" \|jq-r ".access_token")"resource="https://storage.azure.com/.default"access_token="$(curl \--silent \--requestPOST \--url "https://login.microsoftonline.com/${aadTenant}/oauth2/v2.0/token" \--data-urlencode "response_type=token" \--data-urlencode "grant_type=client_credentials" \--data-urlencode "client_id=${SAMPLE_SP_APPID}" \--data-urlencode "client_secret=${SAMPLE_SP_KEY}" \--data-urlencode "scope=${resource}" \|jq-r ".access_token")"
Even though this says that Adding passwordCredential when creating applications is not supported., and the sample shows an empty "passwordCredentials": [] array, the call to az ad app create --display-name "${display_name}" --password "${client_secret}" exactly populates that property.
Fetching a secret from Azure KeyVault using a managed identity
This little script demonstrates how to fetch a secret from an Azure KeyVault, using a managed identity on an Azure VM. Just adapt key_vault_name and secret_name accordingly, and of course ensure that the managed identity can actually read the secret.
#!/bin/bashjson_identity="$( \curl--silent \--url "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fstorage.azure.com%2F" \--headerMetadata:true \|jq-r ".access_token")"storage_account="tmp1diag889"host="hybris"storageApi="2019-12-12"## Download file#curl \--url"https://${storage_account}.blob.core.windows.net/${host}/${file}" \--header"Authorization: Bearer ${json_identity}" \--header"x-ms-version: ${storageApi}" \--header"x-ms-blob-type: BlockBlob" \--remote-name## List Container## Unfortunately, the REST API returns XML, so 'jq' alone isn't helpful, need to XPath into XML here##curl \--silent \--url"https://${storage_account}.blob.core.windows.net/${host}/?comp=list&restype=container" \--header"Authorization: Bearer ${json_identity}" \--header"x-ms-version: ${storageApi}" \|xq'.EnumerationResults.Blobs[]' \|jq-r'.[] | .Name'## An alternative approach to process the XML response, but ...## XML processing with Regexes guarantees us a place in hell, but we don't need 'pip install yq'#curl \--silent \--url"https://${storage_account}.blob.core.windows.net/${host}/?comp=list&restype=container" \--header"Authorization: Bearer ${json_identity}" \--header"x-ms-version: ${storageApi}" \|sed-e's|<Name>|\n<Name>|g'-e's|</Name>|</Name>\n|g' \|egrep"^<Name>" \|sed-e's|<Name>||g'-e's|</Name>||g'