|
| 1 | +# Account Verification for Atmos Auth Identity Selection |
| 2 | +# |
| 3 | +# Purpose: |
| 4 | +# We use Atmos auth to select an identity (AWS IAM role/user) to run Terraform. This file ensures |
| 5 | +# that the selected identity is operating in the correct AWS account that we intend to target. |
| 6 | +# |
| 7 | +# How it works: |
| 8 | +# 1. Gets the current AWS account ID from the identity selected via Atmos auth |
| 9 | +# 2. Compares it against the expected account ID derived from the context (tenant-stage) |
| 10 | +# 3. Uses the account_map variable to look up the expected account ID based on the tenant-stage |
| 11 | +# naming convention (format: "tenant-stage") |
| 12 | +# 4. If verification is enabled and the accounts don't match, Terraform will fail with a clear error |
| 13 | +# |
| 14 | +# This verification process ensures that when using Atmos auth to select an identity, we're |
| 15 | +# targeting the correct account as determined by the component's context variables. |
| 16 | + |
| 17 | +# Get current AWS account ID for verification |
| 18 | +data "aws_caller_identity" "account_verification" {} |
| 19 | + |
| 20 | +# Optional account map for account verification |
| 21 | +# If provided, will verify that the current AWS account matches the expected account |
| 22 | +# based on tenant-stage naming convention (format: "tenant-stage") |
| 23 | +# Note: If this variable is already defined in variables.tf, Terraform will use that definition |
| 24 | +variable "account_map" { |
| 25 | + type = object({ |
| 26 | + full_account_map = map(string) |
| 27 | + audit_account_account_name = optional(string, "") |
| 28 | + root_account_account_name = optional(string, "") |
| 29 | + }) |
| 30 | + description = "Map of account names (tenant-stage format) to account IDs. Used to verify we're targeting the correct AWS account. Optional attributes support component-specific functionality (e.g., audit_account_account_name for cloudtrail, root_account_account_name for aws-sso)." |
| 31 | + default = { |
| 32 | + full_account_map = {} |
| 33 | + audit_account_account_name = "" |
| 34 | + root_account_account_name = "" |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +# Compute expected account name from tenant and stage |
| 39 | +locals { |
| 40 | + # Construct expected account name in format "tenant-stage" |
| 41 | + # Only construct if both tenant and stage are non-null and non-empty |
| 42 | + expected_account_name = ( |
| 43 | + try(var.tenant, null) != null && |
| 44 | + try(var.stage, null) != null && |
| 45 | + try(var.tenant, "") != "" && |
| 46 | + try(var.stage, "") != "" |
| 47 | + ) ? "${var.tenant}-${var.stage}" : null |
| 48 | + |
| 49 | + # Get expected account ID from account_map if account_map is provided and account name can be constructed |
| 50 | + expected_account_id = try( |
| 51 | + local.expected_account_name != null ? var.account_map.full_account_map[local.expected_account_name] : null, |
| 52 | + null |
| 53 | + ) |
| 54 | + |
| 55 | + # Current account ID |
| 56 | + current_account_id = data.aws_caller_identity.account_verification.account_id |
| 57 | + |
| 58 | + # Only validate if: |
| 59 | + # 1. account_map is provided (not empty) |
| 60 | + # 2. Expected account name can be constructed from tenant-stage |
| 61 | + # 3. Expected account ID exists in the account_map |
| 62 | + should_validate = ( |
| 63 | + length(var.account_map.full_account_map) > 0 && |
| 64 | + local.expected_account_name != null && |
| 65 | + local.expected_account_id != null |
| 66 | + ) |
| 67 | + |
| 68 | + # Validation error message |
| 69 | + # Must always be a string (never null) for use in precondition error_message |
| 70 | + validation_error = local.should_validate && local.current_account_id != local.expected_account_id ? ( |
| 71 | + "Account verification failed: Expected account ID ${local.expected_account_id} for account '${local.expected_account_name}' (tenant: ${var.tenant}, stage: ${var.stage}), but current account ID is ${local.current_account_id}" |
| 72 | + ) : "Account verification check passed" |
| 73 | +} |
| 74 | + |
| 75 | +# Validate account matches expected account when account_map is provided |
| 76 | +# Using terraform_data (available in Terraform 1.4+) for validation |
| 77 | +# For Terraform < 1.4, this will still work but validation happens at plan time |
| 78 | +resource "terraform_data" "account_verification" { |
| 79 | + count = local.should_validate ? 1 : 0 |
| 80 | + |
| 81 | + lifecycle { |
| 82 | + precondition { |
| 83 | + condition = local.current_account_id == local.expected_account_id |
| 84 | + error_message = local.validation_error |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +provider "aws" { |
| 90 | + region = var.region |
| 91 | +} |
0 commit comments