Chapter 19Lesson 04~235 minutes

Azure, AWS, Microsoft Graph, and Cloud PowerShell Modules

Choose between cloud CLIs and PowerShell modules deliberately, understand Azure/AWS/Graph identity and target context, handle scale concerns, and normalize provider-specific objects into one inventory contract without requiring real cloud accounts.

AzureAWSMicrosoft GraphCloud context

Learning objectives

  • Compare native cloud CLIs with PowerShell modules.
  • Identify the supported Az, AWS.Tools, and Microsoft Graph module families.
  • Treat tenant/account/subscription/region and permissions as explicit inputs.
  • Explain pagination, throttling, retries, and module-version pinning.
  • Normalize provider-specific objects into a stable inventory contract.
  • Complete a credential-free multi-cloud mock lab.

1. Cloud CLI or PowerShell module? Choose for the operating environment

Cloud platforms expose HTTP APIs. A native cloud CLI and a PowerShell module are two client surfaces over those APIs. Neither is automatically “more DevOps.” Choose based on team skill, runner images, required service coverage, object handling, startup/module footprint, and the environment where the automation must run.

Client surfaceStrengthTradeoff
Native CLIOften compact, language-neutral, easy in mixed-shell teamsReturns text/JSON and requires native process handling
PowerShell moduleReturns PowerShell/.NET objects and composes naturally with pipeline/functionsModule dependency footprint and version compatibility
Direct REST/APISmallest abstraction and precise controlYou own auth headers, pagination, API versions, retries, schemas

2. Azure: Az is the supported PowerShell module family

Microsoft's supported Azure PowerShell family is Az; AzureRM is deprecated. The verified current Az rollup is 16.0.0. Authentication creates an Azure context containing account, tenant, subscription, and token information. If an identity has access to more than one subscription, explicitly select the intended context.

# Read-only capability/context inspection. These commands run only if Az is installed.
if (Get-Module -ListAvailable Az.Accounts) {
    Import-Module Az.Accounts

    Get-AzContext -ListAvailable |
        Select-Object Name,@{N='Subscription';E={$_.Subscription.Id}},@{N='Tenant';E={$_.Tenant.Id}}

    # In an authenticated lab, select deliberately:
    # Set-AzContext -Subscription '<subscription-id>' -Tenant '<tenant-id>'
} else {
    'Az.Accounts is not installed; use the mock inventory path below.'
}

Interactive Connect-AzAccount is useful for an operator session, but unattended automation should use an approved workload identity/service principal/managed identity model rather than embedding a password.

3. AWS: AWS.Tools V5 is the current modular recommendation

AWS publishes several PowerShell packaging styles. For current production use, AWS documentation recommends modular AWS.Tools, where you install only the service modules you need plus common infrastructure. The current documentation line is V5.

$awsModules = Get-Module -ListAvailable 'AWS.Tools.*' |
    Sort-Object Name,Version -Unique |
    Select-Object Name,Version,Path

$awsModules | Format-Table

# Region/credential context should be explicit in real automation.
# Example read-only shape if AWS.Tools.EC2 is installed and authenticated:
# Get-EC2Instance -Region 'us-east-1' ...

AWS credentials can come from supported credential providers, profiles, environment variables, role-based metadata, SSO, or workload identity patterns. The lesson deliberately does not ask for real AWS credentials.

4. Microsoft Graph PowerShell is the supported Graph/Entra SDK path

Microsoft Graph PowerShell wraps Microsoft Graph APIs and is the supported replacement for the older Azure AD PowerShell and MSOnline modules. Authentication can be delegated or app-only. Always request only the scopes/permissions the task requires.

if (Get-Module -ListAvailable Microsoft.Graph.Authentication) {
    Import-Module Microsoft.Graph.Authentication

    # Inspect an existing authenticated context only; no sign-in is forced by this lab.
    $ctx = Get-MgContext
    if ($ctx) {
        $ctx | Select-Object ClientId,TenantId,AuthType,Scopes
    } else {
        'Microsoft Graph module is available, but no Graph context is active.'
    }
}

For unattended scripts, app-only authentication or managed identity may be appropriate depending on host and service. Permissions are a security design decision, not a convenience flag.

5. Account, tenant, subscription, and region are first-class inputs

Cloud automation mistakes are often target-selection mistakes. Make the identity and target context observable before any change. Azure emphasizes tenant/subscription context; AWS uses account/role and region; Microsoft Graph uses tenant plus permission scopes.

function New-CloudTargetRecord {
    param(
        [ValidateSet('Azure','AWS','MicrosoftGraph')][string]$Provider,
        [string]$Account,
        [string]$Tenant,
        [string]$Subscription,
        [string]$Region
    )

    [pscustomobject]@{
        Provider     = $Provider
        Account      = $Account
        Tenant       = $Tenant
        Subscription = $Subscription
        Region       = $Region
    }
}

New-CloudTargetRecord -Provider Azure -Tenant 'tenant-demo' -Subscription 'sub-dev'
New-CloudTargetRecord -Provider AWS -Account '111122223333' -Region 'us-east-1' 
Least privilege: do not broaden permissions just so one script can avoid handling authorization failures. A denied operation may be evidence that the identity boundary is working.

6. Cloud scale adds pagination, throttling, and retries

Cloud APIs frequently return partial pages and rate-limit high request volume. Modules may hide some mechanics, but scripts still need to understand continuation tokens/next links, retryable failures, and bounded retries. Do not assume a single command result represents an unlimited global inventory.

function Invoke-BoundedRetry {
    param(
        [Parameter(Mandatory)][scriptblock]$Operation,
        [int]$MaxAttempts = 3
    )

    for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
        try { return & $Operation }
        catch {
            if ($attempt -eq $MaxAttempts) { throw }
            Start-Sleep -Seconds ([math]::Min(2 * $attempt, 6))
        }
    }
}

# Provider-neutral pagination shape using local pages.
$pages = @{
    start = [pscustomobject]@{ Items=@('a','b'); NextToken='page2' }
    page2 = [pscustomobject]@{ Items=@('c'); NextToken=$null }
}
$token = 'start'
$allItems = [System.Collections.Generic.List[string]]::new()
while ($token) {
    $page = Invoke-BoundedRetry { $pages[$token] }
    foreach ($item in $page.Items) { $allItems.Add($item) }
    $token = $page.NextToken
}
$allItems

Retry only failures you have classified as transient. Authorization errors, invalid resource names, and schema/validation errors are usually permanent until input/configuration changes.

7. Normalize multi-cloud inventory before downstream processing

The Azure, AWS, and Graph object models differ. If the business question is simply “which compute-like resources exist, in what scope, and with what state?”, define your own inventory contract. Keep the raw provider object optional for debugging rather than forcing every consumer to know three SDKs.

function ConvertTo-CloudInventoryRecord {
    param(
        [string]$Provider,
        [string]$Scope,
        [string]$Name,
        [string]$Type,
        [string]$Location,
        [string]$State,
        [string]$Id
    )

    [pscustomobject]@{
        Provider = $Provider
        Scope    = $Scope
        Name     = $Name
        Type     = $Type
        Location = $Location
        State    = $State
        Id       = $Id
    }
}

8. Lab: normalize mocked Azure, AWS, and Graph data

No cloud account is needed. The fixtures mimic the small subset of fields the normalization layer consumes.

$azure = @(
    [pscustomobject]@{ Name='api-vm'; ResourceGroupName='rg-dev'; Location='westeurope'; PowerState='VM running'; Id='/subscriptions/demo/vm/api-vm' }
)
$aws = @(
    [pscustomobject]@{ InstanceId='i-demo123'; Name='worker'; Region='us-east-1'; State='running'; Account='111122223333' }
)
$graph = @(
    [pscustomobject]@{ DisplayName='build-runner'; Id='device-demo'; OperatingSystem='Windows'; TenantId='tenant-demo' }
)

$inventory = @(
    foreach ($x in $azure) {
        ConvertTo-CloudInventoryRecord -Provider Azure -Scope $x.ResourceGroupName -Name $x.Name -Type 'VirtualMachine' -Location $x.Location -State $x.PowerState -Id $x.Id
    }
    foreach ($x in $aws) {
        ConvertTo-CloudInventoryRecord -Provider AWS -Scope $x.Account -Name $x.Name -Type 'EC2Instance' -Location $x.Region -State $x.State -Id $x.InstanceId
    }
    foreach ($x in $graph) {
        ConvertTo-CloudInventoryRecord -Provider MicrosoftGraph -Scope $x.TenantId -Name $x.DisplayName -Type 'Device' -Location $null -State $x.OperatingSystem -Id $x.Id
    }
)

$inventory | Sort-Object Provider,Name | Format-Table
$inventory | ConvertTo-Json -Depth 5 | Set-Content ./cloud-inventory.json

Expected observations

Provider-specific source shapes become one stable object contract without any credentials or network calls.

Verification checklist

  • Each record identifies its provider and scope.
  • Provider-specific IDs are preserved without pretending they share one format.
  • Missing location for Graph is represented as null, not invented data.
  • The JSON report is provider-neutral enough for later release/reporting code.

Cleanup

Remove-Item ./cloud-inventory.json -ErrorAction SilentlyContinue

9. Cloud module versions belong in the dependency contract

Cloud modules update frequently because service APIs evolve. Pin or at least constrain versions in production automation, test upgrades, and read release notes. “Latest on every run” trades reproducibility for surprise. If you need multiple service modules, prefer the platform's recommended modular installation model where practical.

10. Common cloud automation mistakes

  • Authenticating interactively in a headless runner with no fallback design.
  • Hard-coding client secrets in source or logs.
  • Ignoring active subscription/region/tenant and acting on the wrong target.
  • Assuming one command returns every page.
  • Retrying permission or validation errors as if they were transient.
  • Forwarding raw SDK objects as a supposedly stable cross-cloud contract.

11. Knowledge check

Question 1. When might a cloud CLI be preferable to a PowerShell module?

Question 2. What is the current supported Azure PowerShell family?

Question 3. Which AWS PowerShell packaging model is currently recommended for production?

Question 4. What replaced Azure AD PowerShell/MSOnline for Graph/Entra API automation?

Question 5. Why normalize cloud responses?

12. Summary and bridge

Cloud modules remove some native-process plumbing, but they do not remove identity, context, pagination, throttling, versioning, or least-privilege concerns. Lesson 5 now combines source metadata, quality evidence, artifact checksums, environment selection, and machine-readable manifests into a provider-neutral release orchestration skeleton.

13. Authoritative references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.