Identity & Access Management
1 - Multi-Cloud IAM challenges
1.1 The problem
| Challenge | Description |
|---|---|
| Multiple identities | One user = N cloud accounts |
| Different policies | Different syntax and concepts |
| Fragmented audit | Scattered logs |
| Credential rotation | Complex to automate |
2 - SSO with an Identity Provider
2.1 Centralized architecture
2.2 AWS SAML with Azure AD
# AWS - SAML Provider
resource "aws_iam_saml_provider" "azure_ad" {
name = "AzureAD"
saml_metadata_document = file("azure-ad-metadata.xml")
}
# AWS - Role assumable via SAML
resource "aws_iam_role" "admin_saml" {
name = "AzureAD-Admin"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_saml_provider.azure_ad.arn
}
Action = "sts:AssumeRoleWithSAML"
Condition = {
StringEquals = {
"SAML:aud" = "https://signin.aws.amazon.com/saml"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "admin_saml" {
role = aws_iam_role.admin_saml.name
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}
2.3 GCP Workload Identity Federation
# GCP - Workload Identity Pool
resource "google_iam_workload_identity_pool" "azure" {
workload_identity_pool_id = "azure-pool"
display_name = "Azure AD Pool"
}
# GCP - OIDC Provider
resource "google_iam_workload_identity_pool_provider" "azure_oidc" {
workload_identity_pool_id = google_iam_workload_identity_pool.azure.workload_identity_pool_id
workload_identity_pool_provider_id = "azure-oidc"
oidc {
issuer_uri = "https://login.microsoftonline.com/${var.azure_tenant_id}/v2.0"
}
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.aud" = "assertion.aud"
}
}
3 - Cross-Cloud Service Accounts
3.1 AWS to GCP
# GCP - Service Account
resource "google_service_account" "cross_cloud" {
account_id = "aws-cross-cloud"
display_name = "AWS Cross Cloud Access"
}
# GCP - IAM for Workload Identity
resource "google_service_account_iam_binding" "aws_access" {
service_account_id = google_service_account.cross_cloud.name
role = "roles/iam.workloadIdentityUser"
members = [
"principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.aws.name}/attribute.aws_role/arn:aws:iam::123456789:role/GCPAccess"
]
}
# Python - Access GCP from AWS
import google.auth
from google.auth import aws
credentials = aws.Credentials(
audience="//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/aws-pool/providers/aws-provider",
subject_token_type="urn:ietf:params:aws:token-type:aws4_request",
service_account_impersonation_url=f"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/aws-cross-cloud@{project}.iam.gserviceaccount.com:generateAccessToken"
)
# Use the credentials
from google.cloud import storage
client = storage.Client(credentials=credentials)
3.2 Azure to AWS
# AWS - Role for Azure
resource "aws_iam_role" "azure_access" {
name = "AzureServiceAccess"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/sts.windows.net/${var.azure_tenant_id}/"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"sts.windows.net/${var.azure_tenant_id}/:aud" = var.azure_app_id
}
}
}
]
})
}
4 - Unified Secrets Management
4.1 HashiCorp Vault
4.2 Multi-Cloud Vault configuration
# Vault - AWS Secrets Engine
resource "vault_aws_secret_backend" "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = "eu-west-1"
path = "aws"
}
resource "vault_aws_secret_backend_role" "deploy" {
backend = vault_aws_secret_backend.aws.path
name = "deploy"
credential_type = "assumed_role"
role_arns = ["arn:aws:iam::123456789:role/DeployRole"]
}
# Vault - Azure Secrets Engine
resource "vault_azure_secret_backend" "azure" {
subscription_id = var.azure_subscription_id
tenant_id = var.azure_tenant_id
client_id = var.azure_client_id
client_secret = var.azure_client_secret
path = "azure"
}
# Vault - GCP Secrets Engine
resource "vault_gcp_secret_backend" "gcp" {
credentials = file("gcp-sa.json")
path = "gcp"
}
4.3 Usage in Kubernetes
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-secrets
spec:
provider: vault
parameters:
vaultAddress: "https://vault.example.com"
roleName: "k8s-role"
objects: |
- objectName: "aws-creds"
secretPath: "aws/creds/deploy"
secretKey: "access_key"
- objectName: "azure-creds"
secretPath: "azure/creds/deploy"
secretKey: "client_secret"
---
apiVersion: v1
kind: Pod
spec:
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "vault-secrets"
5 - Unified RBAC
5.1 Role mapping
| Role | AWS | Azure | GCP |
|---|---|---|---|
| Admin | AdministratorAccess | Owner | Owner |
| DevOps | PowerUserAccess | Contributor | Editor |
| Dev | Developer | Developer | Developer |
| ReadOnly | ViewOnlyAccess | Reader | Viewer |
5.2 Terraform for consistent RBAC
# Common variables
variable "devops_users" {
type = list(string)
default = ["[email protected]", "[email protected]"]
}
# AWS
resource "aws_iam_group" "devops" {
name = "devops"
}
resource "aws_iam_group_policy_attachment" "devops" {
group = aws_iam_group.devops.name
policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
# Azure
resource "azurerm_role_assignment" "devops" {
for_each = toset(var.devops_users)
scope = data.azurerm_subscription.current.id
role_definition_name = "Contributor"
principal_id = data.azuread_user.users[each.key].object_id
}
# GCP
resource "google_project_iam_member" "devops" {
for_each = toset(var.devops_users)
project = var.gcp_project_id
role = "roles/editor"
member = "user:${each.key}"
}
6 - Centralized auditing
6.1 Collect the logs
# Centralization in Datadog/Splunk/ELK
sources:
aws:
- CloudTrail
- CloudWatch Logs
azure:
- Activity Log
- Azure AD Sign-in logs
gcp:
- Cloud Audit Logs
- Access Transparency logs
6.2 Unified alerts
# Common alert rules
alerts:
- name: "Privileged Access"
condition: |
(aws.eventName IN ["AssumeRole", "CreateUser", "AttachUserPolicy"])
OR (azure.operationName CONTAINS "roleAssignments/write")
OR (gcp.methodName CONTAINS "SetIamPolicy")
severity: high
- name: "Failed Authentication"
condition: |
(aws.errorCode == "AccessDenied")
OR (azure.resultType == "Failure")
OR (gcp.status.code != 0)
severity: medium
Summary
In this chapter, we learned:
- The Multi-Cloud IAM challenges
- SSO with an Identity Provider
- Cross-cloud Service Accounts
- HashiCorp Vault for secrets
- Unified RBAC
- Centralized auditing
Next step
In the next chapter, we will look at Data management.
→ Next chapter: Data management