Author : HASSAN MD TAREQ | Updated : 2021/10/18

Connect to Azure

DeviceAuthentication - Connect Azure Account in CloudShell when MFA is Enabled

#
# Copy device code and click the link
#
Connect-AzAccount -UseDeviceAuthentication

#Set-AzContext -Subscription (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id

Using user defined function

function Connect-AzAccountTargetSubscription {
    param (

        [Parameter(Mandatory)]
        [string] $SubscriptionName
    )

    #
    # In Azure CloudShell, by default managed identity is used to connect to Azure Account
    # Therefore, it is not possible to get currently logged in user objectId
    # (objectId is needed to assign role to currently logged in user)
    #
    # If MFA is enabled, then following interactive login is not possible
    # $azureLoginCredential = (Get-Credential -Message "Please Enter Azure Login Credential `r`n(Azure AD Login Id & Password)")
    # Connect-AzAccount -Credential $azureLoginCredential
    # 
    # Therefore, use Device Authentication
    #

    # Check if logged in using managed identity (MSI) -> sccount will be some thing like "MSxxx"
    $loggedInAccount = (Get-AzContext).Account ?? "MS"
    if ("$loggedInAccount".Contains("MS")) {

        Write-Host "Login is required"

        Connect-AzAccount -UseDeviceAuthentication

        Set-AzContext -Subscription (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id

    }
    else {

        Write-Host "User is already logged in"
    }
}

Connect Azure Account in CloudShell when MFA is NOT Enabled

#
# Enter id and password
#
$azureLoginCredential = Get-Credential -Message "Please Enter Azure Login Credential `r`n(Azure AD Login Id & Password)"
Connect-AzAccount -Credential $azureLoginCredential

#Set-AzContext -Subscription (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id

Connect Azure Account in Local PowerShell Using Browser

#
# Connect to Azure
#
# Default browser will open, you are already logged in to Azure portal
#
Connect-AzAccount

Now, don’t forget to set AzContext to target subscription: see next section

Connect Azure Account in Local PowerShell Using Organizational Id Credentials

This scenario works only in Windows PowerShell 5.1

#
# Use Azure AD login Id and Password
#
$Credential = Get-Credential
Connect-AzAccount -Credential $Credential

Now, don’t forget to set AzContext to target subscription: see next section

Connect Azure Account in Local PowerShell from Azure VM using Managed Indentity

https://docs.microsoft.com/en-us/powershell/module/az.accounts/connect-azaccount?view=azps-6.4.0#example-6--connect-using-managed-service-identity-login-and-clientid

Set AzContext

First subscription (from the list of subscriptions belong to the tenant) will be selected. Therefore, you should set AzContext to your target subscription

#
# Set AzContext to target subscription
#
# Set-AzContext -Subscription "xxxx-xxxx-xxxx-xxxx"
#
# If target subscription belongs to different tenant:
# Get-AzSubscription -SubscriptionId "xxxx-xxxx-xxxx-xxxx" -TenantId "yyyy-yyyy-yyyy-yyyy" | Set-AzContext
# https://docs.microsoft.com/en-us/powershell/module/az.accounts/set-azcontext
#
$targetSubscriptionName = "xxx"
$targetSubscriptionId = (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id
Set-AzContext -Subscription $targetSubscriptionId

# Using name only
Set-AzContext -SubscriptionName $targetSubscriptionName

You should connect to a specific tenant and subscription (organization might have multiple tenants & each tenant might have a list of subscriptions), otherwise you might ended up performating action against wrong subscription

#
# Connect-AzAccount -Tenant 'xxxx-xxxx-xxxx-xxxx' -SubscriptionId 'yyyy-yyyy-yyyy-yyyy'
#
$tenantGuid = "xxx"
$subscriptionGuid = "xxx"

$Credential = Get-Credential
Connect-AzAccount -Credential $Credential -Tenant $tenantGuid -SubscriptionId $subscriptionGuid

Get Currently Logged In User Information

When using CloudShell, by default it used managed identity (MSI) to login. Use Device Authentication (described above) in order to connect using Azure AD user credentials.

#
# Azure AD User Principle Id (currently logged in user)
#

$currentlyLoggedInUserObjectId = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account).Id

echo (Get-AzADUser -UserPrincipalName (Get-AzContext).Account).Id

#
# AzContext Id/SignInName (currently logged in user)
#
echo ((Get-AzContext).Account).Id

Using user defined function

function Get-LoggedInUserObjectId {

    #$currentlyLoggedInUserObjectId = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account).Id
    #Write-Host "Object id of currently logged in user: $currentlyLoggedInUserObjectId"

    (Get-AzADUser -UserPrincipalName (Get-AzContext).Account).Id
}

Get Subscription Id

#
# Taget subscription
#
$targetSubscriptionName = "xxx"
$targetSubscriptionId = (Get-AzSubscription -SubscriptionName $targetSubscriptionName).Id
echo $targetSubscriptionId

#
# Current (context) subscription
#
$currentContextSubscriptionId = ((Get-AzContext).Subscription).Id
echo $currentContextSubscriptionId

# https://docs.microsoft.com/en-us/powershell/module/az.accounts/get-azsubscription

Get Tenant Id

$tenantId = (Get-AzTenant).Id
echo $tenantId

# https://docs.microsoft.com/en-us/powershell/module/az.accounts/get-aztenant

Create Storage Account Context

Using Connected Account

$saContext = (New-AzStorageContext -StorageAccountName $StorageAccountName -UseConnectedAccount)

Using ResourceGroupName and StorageAccountName

$saContext = (Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName).Context

Using Storage Account Key

$accountKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName  
$storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $accountKeys[0].value 

Using SAS Toekn

$saContext = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasToken

Using user defined function

function  New-StorageAccountKeyContext {
    param (
        [Parameter(Mandatory)]
        [string] $ResourceGroupName,
        [Parameter(Mandatory)]
        [string] $StorageAccountName
    )
    
    # Storage account context can be created in multiple ways,
    # In some case using account key might be required i.e. for creating Stored Access Policy
    #
    # $storageAccountContext = (Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName).Context
    # $saContext = (New-AzStorageContext -StorageAccountName $StorageAccountName -UseConnectedAccount) 
    #
    $saKeys = Get-AzStorageAccountKey -ResourceGroupName  $ResourceGroupName -Name $StorageAccountName
    $saKeyContext = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $saKeys[0].Value
    
    return $saKeyContext
}

Create Service Principal

Make sure you are logged in and have enough preivilege to create service principal

function New-ServicePrincipal {
    param (

        [Parameter(Mandatory)]
        [string] $ServicePrincipalDisplayName
    )

    $spCredential = @{}

    try {

        $newSp = (New-AzADServicePrincipal -DisplayName $ServicePrincipalDisplayName)
        Start-Sleep -Seconds 3 # cool down

        $spSecretAsStringObj = ConvertTo-String -SecureString $newSp.Secret

        $spCredential[$envVarKeyServicePrincipalDisplayName] = $ServicePrincipalDisplayName
        $spCredential[$envVarKeyServicePrincipalObjectId] = $newSp.Id
        $spCredential[$envVarKeyTenantId] = (Get-AzTenant).Id
        $spCredential[$envVarKeyClientId] = $newSp.ApplicationId
        $spCredential[$envVarKeyClientSecret] = $spSecretAsStringObj
    }
    catch {
        Write-Warning $Error[0]
    }

    return $spCredential
}

Save service principal credential to JsonFile: Write content to file

Role Assignment

function New-RoleAssignement {

    param (

        [Parameter(Mandatory)]
        [string] $RoleDefinitionName,

        [Parameter(Mandatory)]
        [string] $ObjectId,

        [Parameter(Mandatory)]
        [string] $Scope
    )

    #
    # Before trying to assign role,
    # Check that is given role is already assigned or not
    #
    $existingRoleAssignment = (Get-AzRoleAssignment -RoleDefinitionName $RoleDefinitionName -ObjectId $ObjectId -Scope $Scope)
        
    if ($null -eq $existingRoleAssignment) {

        Write-Host "[Role '$($RoleDefinitionName)' at Scope '$($Scope)'] does not exist, creating new role assignment"

        New-AzRoleAssignment -RoleDefinitionName $RoleDefinitionName `
            -ObjectId $ObjectId `
            -Scope $Scope `
        | Out-Null

        Write-Host "Role '$($RoleDefinitionName)' has been assigned to '$($Scope)'"
    }
    else {

        Write-Host "[Role '$($RoleDefinitionName)' at Scope '$($Scope)']  already exists"
    }
}

Create Stored Access Policy

function  New-StoredAccessPolicy {
    param (
        [Parameter(Mandatory)]
        [Microsoft.WindowsAzure.Commands.Storage.AzureStorageContext] $StorageAccountKeyContext,
        [Parameter(Mandatory)]
        [string] $ContainerName,
        [Parameter(Mandatory)]
        [string] $PolicyName,
        [Parameter(Mandatory)]
        [datetime] $ExpiryTime,
        [Parameter(Mandatory)]
        [string] $PermissionFlagString
    )

    $sap = $null
    try {
        $sap = Get-AzStorageContainerStoredAccessPolicy -Context $StorageAccountKeyContext -Container $ContainerName -Policy $PolicyName -ErrorAction Stop
    }
    catch {
        Write-Warning $Error[0]
    }

    $noPolicyWithSameName = ($null -eq $sap)

    if ($noPolicyWithSameName) {

        Write-Host "Stored Access Policy '$($PolicyName)' does not exist, creating new"

        $sapSplat = @{
            Context    = $StorageAccountKeyContext
            Container  = $ContainerName
            Policy     = $PolicyName
            ExpiryTime = $ExpiryTime
            Permission = $PermissionFlagString
        }

        $sap = New-AzStorageContainerStoredAccessPolicy @sapSplat
    
        Write-Host "Stored Access Policy '$($PolicyName)' with expiry time '$($ExpiryTime)' has been created"
    } 
    else {

        Write-Host "Stored Access Policy '$($PolicyName)' already exists"
    }

    $sap | Out-Null
}

function  New-StoredAccessPolicyAllPermissions {
    param (
        [Parameter(Mandatory)]
        [Microsoft.WindowsAzure.Commands.Storage.AzureStorageContext] $StorageAccountKeyContext,
        [Parameter(Mandatory)]
        [string] $ContainerName,
        [Parameter(Mandatory)]
        [string] $PolicyName,
        [Parameter(Mandatory)]
        [datetime] $ExpiryTime
    )

    $splat = @{
        StorageAccountKeyContext = $StorageAccountKeyContext
        ContainerName            = $ContainerName
        PolicyName               = $PolicyName
        ExpiryTime               = $ExpiryTime
        PermissionFlagString     = "racwdl"
    }

    New-StoredAccessPolicy @splat
}

Generate SAS Token

Create Stored Access policy first before generatting SAS token

function New-SasToken {
    param (
        [Parameter(Mandatory)]
        [string] $ResourceGroupName,
        [Parameter(Mandatory)]
        [string] $StorageAccountName,
        [Parameter(Mandatory)]
        [string] $ContainerName,
        [Parameter(Mandatory)]
        [string] $StoredAccessPolicyName,
        [Parameter(Mandatory)]
        [datetime] $ExpiryTime
    )
    
    # To generate SAS, followings are needed
    # 1. Storage Account Context
    # 2. Stored Access Policy
    #
    $saKeys = Get-AzStorageAccountKey -ResourceGroupName  $ResourceGroupName -Name $StorageAccountName
    $saKeyContext = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $saKeys[0].Value

    $policySplat = @{
        StorageAccountKeyContext = $saKeyContext
        ContainerName            = $ContainerName
        PolicyName               = $StoredAccessPolicyName
        ExpiryTime               = $ExpiryTime
    }

    New-StoredAccessPolicyAllPermissions @policySplat

    # Generate SAS Token
    #
    $sasSplat = @{
        Context   = $saKeyContext
        Container = $ContainerName
        Policy    = $StoredAccessPolicyName
    }
    $sasString = New-AzStorageContainerSASToken @sasSplat

    $sasToken = "$($sasString.Substring(1))" # removing '?' in the token string
    #Write-Host "Generated SAS token: $($sasToken)"

    return $sasToken # returning token to caller
}

Iterate Over all Subscriptions and Resource Groups

Get-AzSubscription | ForEach-Object {
    $subscriptionName = $_.Name
    Set-AzContext -SubscriptionId $_.SubscriptionId
    (Get-AzResourceGroup).ResourceGroupName | ForEach-Object {     
        [PSCustomObject] @{
            Subscription = $subscriptionName
            ResourceGroup = $_
        }
    }
}

Courtesy: https://stackoverflow.com/a/61680135/4802664