Helpful Microsoft Azure cloud tips.
The cloud metaphor usually evokes a feeling of something that is experienced without understanding what it is or how it works, and Microsoft Azure cloud computing if no different.
To avoid the hype, let’s approach the Azure Cloud as revolving around two major realms: Resource and Security. This is not how Microsoft typically documents Azure, but it’s a more helpful perspective on how it is put together and how it works. In essence, the Azure cloud can be summarized by its two fundamental realms: Azure Resource Services and Azure Security Services.
What we call Azure Resource Services here is essentially the comprehensive suite of Products and tools to host applications, store data, and enable various services on Microsoft’s Azure cloud. Institutions can access these wide array of Azure Services which include virtual machines (VM), databases, storage solutions, machine learning, artificial intelligence (AI), serverless computing, content delivery networks (CDN), and much more. The functions of these services are primarily managed via the Azure Resource Manager API at https://management.azure.com, and are detailed at https://learn.microsoft.com/en-us/azure/azure-resource-manager. Azure services are the core of the Azure ecosystem.
See below pages for specific tips on specific services:
What we call Azure Security Services here is essentially what Microsoft calls its Microsoft Identity Platform, and it plays a fundamental role in the Azure ecosystem, that permeates all of Azure. Like Azure Resources, it is a complementary suite of services that provides several essential cloud identity components, allowing institutions to control and manage access to services and applications that they host on Azure.
To manage access into Azure resource or security services you must first understand that these are indeed two (2) separate realms, but they are very closely intertwined. The Resource Realm is where Azure service objects live, and the Security Realm is where, well, security objects live. Luckily, these realms share the same hierarchy, which starts at the top, within an organization’s Azure tenant.
Note
Stack Overflow has this really good entry, highlighting the relation of these objects within an Azure tenant.
Important:
To do work in azure, whether to create resource or security objects, or to manage them, you have to access Azure in one of the following ways:
Method | Secret | Typical Use |
---|---|---|
Azure CLI | Yes, secured via browser popup | Interactive command line |
OIDC | No | CI/CD automation |
SP | Yes | Services and Application automated access |
MI | No | Services and Application automated access |
Typical use column only describes the most common reason for using the specific method, but actual usage can vary depending on context.
The Azure VM Comparison page provides a quick and dirty view of all current VM instances types that Azure offers, and includes latest princing.
curl -sH "Metadata:true" --noproxy "*" http://169.254.169.254/metadata/instance?api-version=2021-02-01 | jq
Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -NoProxy -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | ConvertTo-Json -Depth 64
The Azure CLI tool (az
) is a cross-platform utility that allows you to connect to Azure and execute administrative commands on Azure resources. Follow below table to install and use in the respective OS or environment:
OS | Command |
---|---|
macOS | brew update && brew install azure-cli |
Ubuntu Linux | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash |
Windows | <== Click on link |
Docker | docker run -it mcr.microsoft.com/azure-cli |
Create a sample Azure VM using the az
utility.
az vm create --name myvm01 --resource-group myrs --admin-username admin --admin-password secure --image "OpenLogic:CentoOS:7.6:7.6.20190402" --vnet name myvnet --subnet mysubnet --size Standard_B1ms
az vm create --name myvm01 --resource-group myrs --admin-username admin --admin-password secure --image "OpenLogic:CentoOS:7.6:7.6.20190402" --subnet "/subscriptions/long-ass-name" --size Standard_B1ms
This is not generally recommended, but there are time, while experimenting in a lower environment when you may need to SSH into an ASK cluster node:
Set account: az account set -s sub1000
az aks show -n myakscluster --resource-group myresgroup --query nodeResourceGroup -o tsv
MC_myakscluster1_resgroup_eastus2
az vmss list --resource-group MC_myakscluster1_resgroup_eastus2 --query [0].name -o tsv
aks-agentx-18680543-vmss
cat /home/user1/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGdF94RyNcx7hrLqFrcymWrYhOUivuuGn3frnT1/g7g+ user1@example.com
az vmss extension set --resource-group MC_myakscluster1_resgroup_eastus2 --vmss-name aks-agentx-18680543-vmss --name VMAccessForLinux --publisher Microsoft.OSTCExtensions --version 1.4 --protected-settings "{\"username\":\"azureuser\", \"ssh_key\":\"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGdF94RyNcx7hrLqFrcymWrYhOUivuuGn3frnT1/g7g+ user1@example.com\"}"
Sync key across the set
az vmss update-instances --instance-ids "*" --resource-group MC_myakscluster1_resgroup_eastus2 --name aks-agentx-18680543-vmss
View Connected Devices in subnet to locate the IP address of the specific AKS node, usually the 1st is .4
, and SSH to it:
ssh azureuser@10.197.165.4
az login
az account set -s MYACCOUNT
NodeResGroup=$(az aks show -n CLUSTER-NAME -g CLUSTER-RES-GRP --query nodeResourceGroup -o tsv)
VmssList=$(az vmss list -g $NodeResGroup --query [0].name -otsv)
az vmss extension list -g $NodeResGroup --vmss-name $VmssList -o tsv
az vmss extension delete -g $NodeResGroup --vmss-name $VmssList -n VMAccessForLinux
az vmss update-instances --instance-ids "*" -g $NodeResGroup -n $VmssList
Try SSH’ing to node to confirm there’s no longer access.
Install-Module Az
Import-Module Az
Connect-AzAccount blah blah blah
# Find the way to install `pwsh` for your version of Linux
pwsh
# Then run same above commands
brew install powershell
pwsh
# Then same above commands
docker run -it mcr.microsoft.com/azure-powershell pwsh
# Then run same above commands
# List PS version
$PSversionTable
# Logon to Azure (see above section too)
Login-AzAccount
# Get RBAC role definition(s)
$r = Get-AzRoleDefinition "Monitoring Contributor"
$r
$r | ? {$_.IsCustom -eq $true} | FT Name, IsCustom
# Get all role definitions in tenant and store as UTF8 text (makes easier Unix grep parsing, etc)
Get-AzRoleDefinition | ? {$_.IsCustom -eq $true} | ConvertTo-JSON | jq | Out-File -Encoding utf8 custom-roles.json
# Get/Select Specific Azure Subscription
Get-AzSubscription
Select-AzSubscription
Set-AzSubscription
# Update existing role
Get-AzRoleDefinition -Name "Existing Role" | ConvertTo-Json | Out-File existing-role.json
# Modify JSON file accordingly, then
Set-AzRoleDefinition -InputFile "existing-role.json"
# Create new custom role, based on existing
Get-AzRoleDefinition -Name "Existing Role" | ConvertTo-Json | Out-File azure-support-basic.json
# Modify JSON file accordingly, then
New-AzRoleDefinition -InputFile ./azure-support-basic.json
# Install YAML module
Install-Module -Name powershell-yaml -Force -Repository PSGallery -Scope CurrentUser
# Grab from file:
$r = Get-Content -Raw -Path file-path.json | ConvertFrom-Json
# Or query Azure
$r = Get-AzRoleDefinition "Monitoring Contributor"
$j = $r | ConvertTo-Json
$y = $r | ConvertTo-YAML
# Get existing role assignment (to then create one)
$a = Get-AzRoleAssignment [-RoleDefinitionName <String>] -Scope <String> | ConvertTo-Json
# Create role assignment
$id = (Get-AzAdGroup -DisplayName MYGROUP).Id
New-AzRoleAssignment -RoleDefinition "azure-support-basic" -ObjectId $id
# Remove role
Get-AzureRmRoleDefinition -Name "Virtual Machine Operator" | Remove-AzureRmRoleDefinition
# Remove role assignment
$id = (Get-AzAdGroup -DisplayName MYGROUP).Id
Remove-AzRoleAssignment -ObjectId $id -RoleDefinitionName "azure-support-basic"
# Use the application ID as the username, and the secret as password
$credentials = Get-Credential
Connect-AzAccount -ServicePrincipal -Credential $credentials -Tenant <tenant ID>
To quickly create an Azure service principal from the CLI, and grant it RBAC role Contributor
at a specific subscription scope, run the following:
az ad sp create-for-rbac -n sp-cli-mgmt --role="Contributor" --scopes="/subscriptions/<SubscriptionId>"
{
"appId": "0c96e907-3875-4989-9e94-ed2f163629cf",
"displayName": "sp-cli-mgmt",
"password": "EF-a0RixjI2FlrZbYGBbPR.qBqr1G70AEt",
"tenant": "e6cbf564-8faa-455b-b339-7cee0c829cf2"
}
If you omit the -n <Name>
option, Azure will automatically pick a name like azure-cli-2022-04-03-23-15-00
for you.
Using the built-in Microsoft Graph PowerShell Service Principal to logon to Azure APIs is highly discouraged.
Connect-MgGraph -AccessToken $generated_token
The functionalities of [Azure Resource Manager] (https://management.azure.com) and [MS Graph] (https://graph.microsoft.com) are very closely intertwined. Will these planes ever be bridged, integrated, or unified into a single platform and API?
Will ARM ever get its own internal infrastructure as code (Iac) framework? Is that what Bicep was meant for? Why not use Bicep (or whatever declarative/configuration DSL) to drive the Azure Portal Web UI? That way everything one does via the Portal UI is natively and immediately kept as an IaC footprint?
OIDC allows workflows to authenticate and interact with Azure using short-lived tokens. This eliminates the need for long-lived personal access tokens (PAT) or service principaa with a secrets, providing a more secure and manageable approach to accessing cloud resources directly from GitHub Actions.
Configuration: You configure your Azure AD App Registration to trust an external identity provider by setting up a federation with that IdP. This involves specifying details about the IdP, such as the issuer URL, and possibly uploading metadata documents for SAML-based federations.
Authentication Flow: A user or service attempts to access an application protected by Azure AD and is redirected to sign in. Instead of presenting Azure AD credentials, the user or service presents credentials from the federated IdP. The federated IdP authenticates the user or service and issues a token. This token is presented to Azure AD, which validates it based on the trust configuration. Upon successful validation, Azure AD issues its own token to the user or service, granting access to the application.
jobs:
task_leveraging_oidc_login:
runs-on: ubuntu-latest
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
steps:
- name: checkout_github_action_code
uses: actions/checkout@v4
with:
ref: main
- name: azure_oidc_login
uses: azure/login@v1
with:
client-id: $
tenant-id: $
allow-no-subscriptions: true
- name: capture_azure_tokens
run: |
# https://learn.microsoft.com/en-us/cli/azure/account?view=azure-cli-latest#az-account-get-access-token
# [--resource-type {aad-graph, arm, batch, data-lake, media, ms-graph, oss-rdbms}]
export MG_TOKEN="$(az account get-access-token --resource-type ms-graph --query accessToken -o tsv)"
export AZ_TOKEN="$(az account get-access-token --resource-type arm --query accessToken -o tsv)"
echo "MG_TOKEN=$MG_TOKEN" >> $GITHUB_ENV # To use in another step
echo "AZ_TOKEN=$AZ_TOKEN" >> $GITHUB_ENV
curl -sH "Content-Type: application/json" -H "Authorization: Bearer ${AZ_TOKEN}" -X GET "https://management.azure.com/subscriptions?api-version=2022-12-01" | jq
- name: some_other_step
run: |
curl -sH "Content-Type: application/json" -H "Authorization: Bearer $" -X GET "https://graph.microsoft.com/v1.0/users" | jq
- name: capture_azure_tokens
shell: pwsh
run: |
# PowerShell equivalent of az account get-access-token command
$env:MG_TOKEN = (az account get-access-token --resource-type ms-graph --query accessToken -o tsv)
$env:AZ_TOKEN = (az account get-access-token --resource-type arm --query accessToken -o tsv)
echo "MG_TOKEN=$env:MG_TOKEN" | Out-File -Append -FilePath $Env:GITHUB_ENV # To use in another step
echo "AZ_TOKEN=$env:AZ_TOKEN" | Out-File -Append -FilePath $Env:GITHUB_ENV
$headers = @{
"Content-Type" = "application/json"
"Authorization" = "Bearer $($env:AZ_TOKEN)"
}
Invoke-RestMethod -Uri "https://management.azure.com/subscriptions?api-version=2022-12-01" -Method Get -Headers $headers | ConvertTo-Json
- name: some_other_step
shell: pwsh
run: |
$headers = @{
"Content-Type" = "application/json"
"Authorization" = "Bearer $env:MG_TOKEN" # Assuming you want to use the Microsoft Graph token here
}
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" -Method Get -Headers $headers | ConvertTo-Json