Blog Post

Azure Storage Blog
4 MIN READ

Enable Secure access to Azure Storage Account across multiple subscriptions

paggarwal's avatar
paggarwal
Icon for Microsoft rankMicrosoft
Apr 19, 2021

Public read access to Azure containers and blob storage is an easy and convenient way to share data, however it also poses a security risk. For better and enhanced security, public access to the entire storage account can be disallowed regardless of the public access setting for an individual container present within the storage container. Disallowing public access to storage prevents a user from enabling public access for a container in the respective storage account.

Ensuring secure access to storage account(s) across subscriptions and storage accounts can be tedious as we grow. Here is a solution that can help you to disallow public access to storage account(s) at scale. You can extract the list of all storage accounts from the Azure subscription(s) and use the same .csv file as an input in the solution below to disallow access to storage account containers at scale across all your subscriptions.

 

Pre-Requisite:
 - Az Modules must be installed
 - Service Principal created as part of Step 1, must be having contributor level access to subscriptions
 
Steps to follow:
Step 1: Create a service principal
Post creation of service principal, please retrieve below values.
  1. Tenant Id
  2. Client Secret
  3. Client Id
 
Step 2: Create a PowerShell function which will be used in generating authorization token
function Get-apiHeader{
[CmdletBinding()]
Param
(
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $TENANTID,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $ClientId,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $PasswordClient,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $resource
)
$tokenresult=Invoke-RestMethod -Uri https://login.microsoftonline.com/$TENANTID/oauth2/token?api-version=1.0 -Method Post -Body @{"grant_type" = "client_credentials"; "resource" = "https://$resource/"; "client_id" = "$ClientId"; "client_secret" = "$PasswordClient" }
$token=$tokenresult.access_token
$Header=@{
  'Authorization'="Bearer $token"
  'Host'="$resource"
  'Content-Type'='application/json'
  }
return $Header
}
 
Step 3: Invoke API to retrieve authorization token using function created in above step
Note: Replace $TenantId, $ClientId and $ClientSecret with value captured in step 1
$AzureApiheaders = Get-apiHeader -TENANTID $TenantId -ClientId $ClientId -PasswordClient $ClientSecret -resource "management.azure.com"
 
Step 4: Extracting list of storage accounts across accessible subscriptions
$subscriptionList =  Get-AzSubscription
$subscriptionIdList = $subscriptionList.Id
foreach($subscriptionId in $subscriptionIdList)
{
$resourceURL = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.Storage/storageAccounts?api-version=2021-01-01"
$resourcedetails=(Invoke-RestMethod  -Uri $resourceURL -Headers $AzureApiheaders -Method GET)
$TableData = $resourcedetails.value.ID
}

 

Step 5: Enable secure access to storage account
foreach($Data in $TableData)
{
  #Select Current Subscription and get All Storage Accounts
  $resourceid=$Data
  $resourceURL="https://management.azure.com$($resourceid)?api-version=2021-02-01"
  $resourcedetails=(Invoke-RestMethod  -Uri $resourceURL -Headers $AzureApiheaders -Method GET)
  $resourcelocation=$resourcedetails.location
  $permissions=$resourcedetails.properties.allowBlobPublicAccess
  if($permissions -eq $false)
  {
   Write-Output "Public access to Storage Account: $($resourcedetails.name) is already disabled"
  }
  Else 
  {
   Write-Output "Changing ACL for Storage Account: $($resourcedetails.name)" 
   $body = @"
   {
    "location":"$($resourcelocation)",
    "properties": {
         "allowBlobPublicAccess":  "false"
                  }
   }"@
   Invoke-RestMethod -Uri $resourceURL -Method Put -Headers $AzureApiheaders -Body $body 
  }
}

 

Overall Script:

function Get-apiHeader{
[CmdletBinding()]
Param
(
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $TENANTID,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $ClientId,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $PasswordClient,
 [Parameter(Mandatory=$true)]
 [System.String]
 [ValidateNotNullOrEmpty()]
 $resource
)
$tokenresult=Invoke-RestMethod -Uri https://login.microsoftonline.com/$TENANTID/oauth2/token?api-version=1.0 -Method Post -Body @{"grant_type" = "client_credentials"; "resource" = "https://$resource/"; "client_id" = "$ClientId"; "client_secret" = "$PasswordClient" }
$token=$tokenresult.access_token
$Header=@{
  'Authorization'="Bearer $token"
  'Host'="$resource"
  'Content-Type'='application/json'
  }
return $Header
}

$AzureApiheaders = Get-apiHeader -TENANTID $TenantId -ClientId $ClientId -PasswordClient $ClientSecret -resource "management.azure.com"

$subscriptionList =  Get-AzSubscription
$subscriptionIdList = $subscriptionList.Id

foreach($subscriptionId in $subscriptionIdList)
{
$resourceURL = "https://management.azure.com/subscriptions/$($subscriptionId)/providers/Microsoft.Storage/storageAccounts?api-version=2021-01-01"
$resourcedetails=(Invoke-RestMethod  -Uri $resourceURL -Headers $AzureApiheaders -Method GET)
$TableData = $resourcedetails.value.ID
foreach($Data in $TableData)
{
  #Select Current Subscription and get All Storage Accounts
  $resourceid=$Data
  $resourceURL="https://management.azure.com$($resourceid)?api-version=2021-02-01"
  $resourcedetails=(Invoke-RestMethod  -Uri $resourceURL -Headers $AzureApiheaders -Method GET)
  $resourcelocation=$resourcedetails.location
  $permissions=$resourcedetails.properties.allowBlobPublicAccess
  if($permissions -eq $false)
  {
   Write-Output "Public access to Storage Account: $($resourcedetails.name) is already disabled"
  }
  Else 
  {
   Write-Output "Changing ACL for Storage Account: $($resourcedetails.name)" 
   $body = @"
   {
    "location":"$($resourcelocation)",
    "properties": {
         "allowBlobPublicAccess":  "false"
                  }
   }"@
   Invoke-RestMethod -Uri $resourceURL -Method Put -Headers $AzureApiheaders -Body $body 
  }
}
}

 

References:

https://docs.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-configure?tabs=powershell

Updated Apr 23, 2021
Version 2.0

2 Comments

  • Hi sebastianheil, yes you are correct, however following cannot be scaled. Solution specified above can perform the remediation across multiple subscriptions within minutes.

  • sebastianheil's avatar
    sebastianheil
    Brass Contributor

    There is a security center recommendation called "Storage account public access should be disallowed". Isn't this recommendation covering this issue? This recommendation also has a "quick fix" button, that to my knowledge might be doing the same as what you described over here. Or am I missing something?