Skip to main content

Identity Plane — L1 Workforce Identity

Layer 1 (L1) workforce identity using AWS IAM Identity Center federated from Entra ID for centralized workforce access.

L1 Identity Architecture

Entra ID (Microsoft)
↓ (SAML 2.0 Federation)
AWS IAM Identity Center (Hub)
↓ (Permission Sets)
├─ Management Account (Organizations, Billing)
├─ B2B Workload Account (ECS, RDS, S3)
└─ Additional Accounts (Future)

Identity Sources

LayerSystemUse CaseAdmin
L1 (Workforce)IAM Identity Center + Entra IDAWS API/Console for engineers and operatorsIT/Entra admin
L2 (Customer)Keycloak (on ECS)B2B Commerce storefront loginApplication team

Non-conflation principle (ADR-020): Workforce and customer identity are completely separate. Operators cannot impersonate customers; customers cannot access AWS.

IAM Identity Center Configuration

Permission Sets

Permission sets define AWS API permissions accessible from a workload account.

# PowerUser permission set (read + write, no IAM changes)
resource "aws_ssoadmin_permission_set" "power_user" {
name = "PowerUser"
instance_arn = data.aws_ssoadmin_instances.main.arns[0]
session_duration = "PT1H" # 1-hour session
}

resource "aws_ssoadmin_managed_policy_attachment" "power_user" {
instance_arn = data.aws_ssoadmin_instances.main.arns[0]
permission_set_arn = aws_ssoadmin_permission_set.power_user.arn

managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}

# ReadOnly permission set
resource "aws_ssoadmin_permission_set" "read_only" {
name = "ReadOnly"
instance_arn = data.aws_ssoadmin_instances.main.arns[0]
session_duration = "PT4H" # 4-hour read-only session
}

Group and Account Assignment

# Engineering group in Entra ID (synced automatically)
# Assign PowerUser permission set to engineering group in B2B workload account
resource "aws_ssoadmin_account_assignment" "engineering_poweruser" {
instance_arn = data.aws_ssoadmin_instances.main.arns[0]
permission_set_arn = aws_ssoadmin_permission_set.power_user.arn

principal_id = data.aws_identitystore_group.engineering.id # From Entra sync
principal_type = "GROUP"

target_account_id = var.workload_account_id
target_type = "AWS_ACCOUNT"
}

# Operations group: ReadOnly on B2B account, PowerUser on management account (billing)
resource "aws_ssoadmin_account_assignment" "ops_readonly" {
instance_arn = data.aws_ssoadmin_instances.main.arns[0]
permission_set_arn = aws_ssoadmin_permission_set.read_only.arn

principal_id = data.aws_identitystore_group.operations.id
principal_type = "GROUP"

target_account_id = var.workload_account_id
target_type = "AWS_ACCOUNT"
}

Entra ID Federation Setup

Prerequisites

  1. Management account: Identity Center enabled (console) before Terraform apply
  2. Entra ID tenant: Admin credentials and application registration
  3. Email domain: Verify custom domain in Entra ID

SAML Trust Configuration

# Fetch Identity Center SAML metadata
data "aws_ssoadmin_instances" "main" {}

# Create Entra ID enterprise application for AWS SAML
# (Manual step: Azure portal → Enterprise Applications → New App)
# - Download metadata from Identity Center console
# - Upload to Entra ID as SAML certificate
# - Configure Name ID as "user.mail"

# Verify SAML federation working:
# 1. Identity Center console → Users → Sync status
# 2. Check groups and users appear from Entra ID
# 3. Test login: https://d-9767913734.awsapps.com/start

Operator Workflow

# Step 1: Authenticate via Entra ID
aws sso login --profile my-workload

# Step 2: Credentials cached locally (~12 hours)
# ~/.aws/sso/cache/*.json contains temporary credentials

# Step 3: Use profile in CLI commands
AWS_PROFILE=my-workload aws sts get-caller-identity
# Output: assumes identity as temporary role in workload account

# Step 4: Session expires after permission-set duration (1-4 hours)
# Automatic re-auth on next command

Security Controls

ControlImplementationVerification
MFAEntra ID Conditional Access (require MFA for AWS sign-in)Check Entra ID policy
Session DurationPermission sets max 12 hours; identity provider policy enforces lower (1-4h)aws sts get-session-token shows expiry
Audit TrailCloudTrail captures all assumed-role operations in workload accountsaws cloudtrail lookup-events --lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::IAM::Role
Credential RotationAutomatic; Identity Center issues new credentials every 1-4 hoursCheck /tmp/.aws/sso/cache/ timestamp
Cross-Account RestrictionsPermission sets scoped to specific accounts; no wildcard account accessTry assuming role in unauthorized account → denied

Compliance Implications

  • SOC 2: Centralized user provisioning/deprovisioning via Entra ID
  • APRA CPS 234: Audit trail via CloudTrail (all API calls logged with assumed identity)
  • PCI DSS: MFA enforced; session duration <4 hours
  • ISO 27001: SAML 2.0 federation with encryption in transit

Known Limitations & Workarounds

LimitationReasonWorkaround
No permanent access keys from Identity CenterBy design (session-based only)Use IAM users (non-recommended) or service roles with OIDC
Entra ID group sync delayGraph API eventual-consistency15-minute eventual sync; manual refresh available in console
No conditional access on AWS API (only console)Identity Center limitationUse VPC IP restrictions or API Gateway authentication

Source Reference

Module: terraform-aws/modules/sso/ — Permission sets, account assignments, Entra ID SAML binding

Account configuration: terraform-aws/accounts/management-account/ — Identity Center instance setup

Related documentation: For the architectural decision rationale on two-layer identity (L1 workforce vs L2 customer), see the b2b-commerce main ADRs in the root repository.