Skip to main content

03 - Azure and GCP Security Architecture & Hardening

While AWS dominates public cloud infrastructure, enterprise environments frequently operate multi-cloud architectures involving Microsoft Azure and Google Cloud Platform (GCP). Securing Azure and GCP requires understanding their unique identity models, resource hierarchies, and perimeter controls.


1. Microsoft Azure Security Mechanics

Azure infrastructure management centers around Microsoft Entra ID (formerly Azure Active Directory) and Azure Role-Based Access Control (RBAC).

Azure Resource Hierarchy & RBAC Scope

Azure RBAC permissions are assigned at four distinct scope levels: Management Group, Subscription, Resource Group, or Resource. Inherited permissions flow downward through the hierarchy.

Azure RBAC Role Definition Structure

An Azure custom role explicitly defines permitted and prohibited operations:

{
"Name": "Production Storage Operator",
"IsCustom": true,
"Description": "Allows managing blob storage items without granting subscription-level access.",
"Actions": [
"Microsoft.Storage/storageAccounts/blobServices/containers/read",
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"
],
"NotActions": [],
"DataActions": [
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"
],
"NotDataActions": [
"Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write"
],
"AssignableScopes": [
"/subscriptions/00000000-0000-0000-0000-000000000000"
]
}

[!WARNING] DataActions vs Control Plane Actions: Standard Azure RBAC Actions control infrastructure management (e.g., deleting a storage account). Accessing actual stored data requires DataActions permissions (e.g., reading blob bytes). Conflating these scopes can grant unintended control-plane privileges.


Hardening Azure Storage Accounts

Prevent data leaks in Azure Storage Accounts by disabling public access, requiring Entra ID auth, and disabling legacy Shared Key authentication.

Secure Azure Storage Account in Bicep

param location string = resourceGroup().location
param storageAccountName string = 'stappsecprod${uniqueString(resourceGroup().id)}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_GRS'
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
allowSharedKeyAccess: false // Mandatory: Disables account access keys; requires Entra ID
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
encryption: {
services: {
blob: {
enabled: true
keyType: 'Account'
}
}
keySource: 'Microsoft.Storage'
}
}
}

Disabling Shared Key Access via Azure CLI

az storage account update \
--name stappsecprod9876 \
--resource-group rg-production-sec \
--allow-blob-public-access false \
--allow-shared-key-access false

2. Google Cloud Platform (GCP) Security Architecture

Google Cloud Platform structures assets under a centralized organizational tree: Organization -> Folders -> Projects -> Resources.

GCP IAM Mechanics: Primitive vs. Predefined vs. Custom Roles

  • Primitive Roles (Owner, Editor, Viewer): Legacy roles granting sweeping access across an entire project. NEVER use in production environments.
  • Predefined Roles (roles/storage.objectViewer): Fine-grained roles managed by GCP catering to specific service actions.
  • Custom Roles: User-curated permission bundles enforcing granular least privilege.

Service Account Impersonation (Eliminating Static Keys)

Long-lived GCP Service Account JSON keys introduce severe supply-chain risks. Eliminate static JSON keys by utilizing Service Account Impersonation via short-lived OAuth 2.0 access tokens.

Multi-Language Code Snippets: GCP Service Account Impersonation

Python (google-auth)

from google.auth import impersonated_credentials
from google.auth.transport.requests import Request
from google.cloud import storage

def generate_impersonated_gcs_client(target_service_account: str):
"""Generate a GCS client using short-lived impersonated tokens instead of JSON keys."""
# Obtain base credentials (e.g., Application Default Credentials / Workload Identity)
base_credentials, _ = google.auth.default()

# Create impersonated credentials expiring in 1 hour
target_scopes = ["https://www.googleapis.com/auth/devstorage.read_only"]
impersonated_creds = impersonated_credentials.Credentials(
source_credentials=base_credentials,
target_principal=target_service_account,
target_scopes=target_scopes,
lifetime=3600
)

# Initialize GCS client using short-lived impersonation
storage_client = storage.Client(credentials=impersonated_creds)
return storage_client

Go (google.golang.org/api/impersonate)

package main

import (
"context"
"fmt"
"cloud.google.com/go/storage"
"google.golang.org/api/impersonate"
"google.golang.org/api/option"
)

func createImpersonatedClient(ctx context.Context, targetSA string) (*storage.Client, error) {
ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
TargetPrincipal: targetSA,
Scopes: []string{"https://www.googleapis.com/auth/devstorage.read_only"},
})
if err != nil {
return nil, fmt.Errorf("failed to create impersonated token source: %w", err)
}

client, err := storage.NewClient(ctx, option.WithTokenSource(ts))
if err != nil {
return nil, fmt.Errorf("failed to create GCS client: %w", err)
}
return client, nil
}

3. GCP VPC Service Controls (VPC SC)

Even if a service account key is stolen by a threat actor, VPC Service Controls prevent data exfiltration by enforcing a logical security perimeter around GCP API services (BigQuery, Cloud Storage).

Provisioning a VPC Service Perimeter in Terraform

# 1. Define Access Level (Allowed IP Range / Corporate Egress)
resource "google_access_context_manager_access_level" "corp_access" {
parent = "accessPolicies/${google_access_context_manager_access_policy.policy.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.policy.name}/accessLevels/corp_trusted_network"
title = "Corporate Trusted Network"

basic {
conditions {
ip_subnetworks = ["198.51.100.0/24"]
}
}
}

# 2. Construct VPC Service Perimeter around Production Project
resource "google_access_context_manager_service_perimeter" "prod_perimeter" {
parent = "accessPolicies/${google_access_context_manager_access_policy.policy.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.policy.name}/servicePerimeters/production_data_perimeter"
title = "Production Data Protection Perimeter"
perimeter_type = "PERIMETER_TYPE_REGULAR"

status {
restricted_services = [
"storage.googleapis.com",
"bigquery.googleapis.com"
]

resources = [
"projects/123456789012" # Production GCP Project Number
]

access_levels = [
google_access_context_manager_access_level.corp_access.name
]
}
}

4. Cross-Cloud Alignment Matrix

Architectural ControlAmazon Web Services (AWS)Microsoft AzureGoogle Cloud Platform (GCP)
Root HierarchyAWS Organization / Management AccountEntra ID Tenant & Root Management GroupGCP Organization Domain
Identity ManagementAWS IAM & AWS IAM Identity CenterMicrosoft Entra ID (Azure AD)GCP IAM & Cloud Identity
Resource GroupingAWS Accounts & OUsSubscriptions & Resource GroupsFolders & Projects
Data PerimeterS3 Bucket Policies & VPC EndpointsStorage Private Endpoints & NSGsVPC Service Controls (VPC SC)
Metadata Endpoint169.254.169.254 (IMDSv2)169.254.169.254 (Metadata: true)169.254.169.254 (Metadata-Flavor: Google)
Share this guide