首页 > 解决方案 > 您可以通过 PowerShell 使用虚拟机身份访问令牌下载 Azure Blob,而无需使用访问密钥

问题描述

我正在设置一个 VM 以在创建时进行一些引导。其中一部分是将 blob 从 azure 存储帐户下载到 VM。这些都在同一个订阅、资源组等中。

我可以这样做:

function Get-BlobUsingVMIdentity 
{
    param(
        [Parameter(Mandatory = $true)] $containerName,
        [Parameter(Mandatory = $true)] $blobName,
        [Parameter(Mandatory = $true)] $outputFolder
    )

    write-host "Defining package information"
        mkdir $outputFolder -force

    write-host "Getting Instance meta data"
        $instanceInfo = Invoke-WebRequest -UseBasicParsing -Uri 'http://169.254.169.254/metadata/instance/?api-version=2018-02-01' `
            -Headers @{Metadata="true"} `
            | select -expand content `
            | convertfrom-json `
            | select -expand compute

        $storageAccountName = "$($instanceInfo.resourceGroupName.replace('-rg',''))sa" # This is custom since we know our naming schema
        $resourceGroupName = $($instanceInfo.resourceGroupName)
        $subscriptionId = $($instanceInfo.subscriptionId)

    write-host "Got storageAccountName [$storageAccountName], resourceGroupName [$resourceGroupName], subscriptionId [$subscriptionId]"

    write-host "Getting VM Instance Access Token"
        $response = Invoke-WebRequest  -UseBasicParsing -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' `
            -Headers @{Metadata = "true" }

        $content = $response.Content | ConvertFrom-Json
        $access_token = $content.access_token

    write-host "Getting SAS Token From Storage Account"
        $params = @{canonicalizedResource = "/blob/$($storageAccountName)/$($containerName)"; signedResource = "c"; signedPermission = "rcw"; signedProtocol = "https"; signedExpiry = "2031-09-23T00:00:00Z" }
        $jsonParams = $params | ConvertTo-Json

        $sasResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($resourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($storageAccountName)/listServiceSas/?api-version=2017-06-01" `
            -Method POST `
            -Body $jsonParams `
            -Headers @{Authorization="Bearer $access_token"}

        $sasContent = $sasResponse.Content | ConvertFrom-Json
        $sasCred = $sasContent.serviceSasToken

    write-host "Manually download blob"
        $params = @{signedResource = "c"; signedPermission = "rcw"; signedProtocol = "https"; signedExpiry = "2031-09-23T00:00:00Z" }
        $jsonParams = $params | ConvertTo-Json

        $sasResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://$($storageAccountName).blob.core.windows.net/$($containerName)/$($blobName)?api-version=2017-06-01" `
            -Method POST `
            -Body $jsonParams `
            -Headers @{Authorization="Bearer $access_token"}

        $sasContent = $sasResponse.Content | ConvertFrom-Json
        $sasCred = $sasContent.serviceSasToken

    write-host "Setting up storage context"
        $ctx = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasCred

    write-host "Downloading package"
        Get-AzStorageBlobContent `
            -Blob $blobName `
            -Container $containerName `
            -Destination $outputFolder `
            -Context $ctx `
            -Force
}

这工作正常,除了我必须授予对身份的完全/写访问权限才能使用访问密钥。

是否有类似的方法允许对 blob 进行只读访问?我的目标是: 1. 任何地方都没有存储凭据 2. 从 azure 存储将 blob 下载到 VM 3. 没有静态定义的变量(例如:subscriptionid) 4. 对 blob/存储帐户的只读访问。

感谢任何帮助!

标签: azurepowershellapiblobamazon-iam

解决方案


据我了解,您想使用 Azure VM MSI 访问 Azure 存储。如果有,请参考以下步骤:

  1. 在 VM 上启用系统分配的托管标识
Connect-AzAccount
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM
Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
  1. 授予 VM 访问 Azure 存储容器的权限
Connect-AzAccount
$spID = (Get-AzVM -ResourceGroupName myRG -Name myVM).identity.principalid
New-AzRoleAssignment -ObjectId $spID -RoleDefinitionName "Storage Blob Data Reader" -Scope "/subscriptions/<mySubscriptionID>/resourceGroups/<myResourceGroup>/providers/Microsoft.Storage/storageAccounts/<myStorageAcct>/blobServices/default/containers/<container-name>"
  1. 访问 blob
# get AD access token
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' `
                              -Headers @{Metadata="true"}
$content =$response.Content | ConvertFrom-Json
$access_token = $content.access_token

# call Azure blob rest api
$url="https://<myaccount>.blob.core.windows.net/<mycontainer>/<myblob>"
$RequestHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$RequestHeader.Add("Authorization", "Bearer $access_token")
$RequestHeader.Add("x-ms-version", "2019-02-02")

$result = Invoke-WebRequest -Uri $url -Headers $RequestHeader
$result.content 

更新

根据我的测试,当我们获得访问 Azure blob 的令牌时,我们需要将资源更改为https://storage.azure.com/

# get AD access token
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://storage.azure.com/' `
                              -Headers @{Metadata="true"}
$content =$response.Content | ConvertFrom-Json
$access_token = $content.access_token

# call Azure blob rest api
$url="https://<myaccount>.blob.core.windows.net/<mycontainer>/<myblob>"
$RequestHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$RequestHeader.Add("Authorization", "Bearer $access_token")
$RequestHeader.Add("x-ms-version", "2019-02-02")

$result = Invoke-WebRequest -Uri $url -Headers $RequestHeader
$result.content 

在此处输入图像描述


推荐阅读