mirror of
https://github.com/ghndrx/terraform-foundation.git
synced 2026-02-10 06:45:06 +00:00
Enterprise-grade multi-tenant AWS cloud foundation. Modules: - GitHub OIDC for keyless CI/CD authentication - IAM account settings and security baseline - AWS Config Rules for compliance - ABAC (Attribute-Based Access Control) - SCPs (Service Control Policies) Features: - Multi-account architecture - Cost optimization patterns - Security best practices - Comprehensive documentation Tech: Terraform, AWS Organizations, IAM Identity Center
223 lines
6.1 KiB
HCL
223 lines
6.1 KiB
HCL
################################################################################
|
|
# App Account Module
|
|
#
|
|
# Account vending machine for provisioning new workload accounts:
|
|
# - Creates AWS account via Organizations
|
|
# - Applies account baseline
|
|
# - Sets up cross-account IAM roles
|
|
# - Configures budget alerts
|
|
################################################################################
|
|
|
|
terraform {
|
|
required_version = ">= 1.5.0"
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = ">= 5.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
data "aws_organizations_organization" "this" {}
|
|
|
|
locals {
|
|
# Generate account email if not provided
|
|
account_email = var.account_email != "" ? var.account_email : "${var.email_prefix}+${var.account_name}@${var.email_domain}"
|
|
|
|
# Standard account tags
|
|
account_tags = {
|
|
AccountName = var.account_name
|
|
Environment = var.environment
|
|
Owner = var.owner
|
|
CostCenter = var.cost_center
|
|
OrganizationUnit = var.organizational_unit
|
|
ManagedBy = "terraform"
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# AWS Account
|
|
################################################################################
|
|
|
|
resource "aws_organizations_account" "this" {
|
|
name = var.account_name
|
|
email = local.account_email
|
|
parent_id = var.organizational_unit_id
|
|
|
|
# IAM user access to billing (usually disabled)
|
|
iam_user_access_to_billing = var.iam_user_access_to_billing ? "ALLOW" : "DENY"
|
|
|
|
# Role name for cross-account access from management account
|
|
role_name = var.admin_role_name
|
|
|
|
# Don't close account on destroy (safety)
|
|
close_on_deletion = var.close_on_deletion
|
|
|
|
tags = merge(var.tags, local.account_tags)
|
|
|
|
lifecycle {
|
|
# Prevent accidental deletion
|
|
prevent_destroy = false # Set to true in production
|
|
|
|
# Email cannot be changed
|
|
ignore_changes = [email, role_name]
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Cross-Account IAM Role (in new account)
|
|
# Note: This creates a role that can be assumed from the management account
|
|
################################################################################
|
|
|
|
# Provider for the new account (assumes role created during account creation)
|
|
provider "aws" {
|
|
alias = "new_account"
|
|
region = var.region
|
|
|
|
assume_role {
|
|
role_arn = "arn:aws:iam::${aws_organizations_account.this.id}:role/${var.admin_role_name}"
|
|
session_name = "terraform-account-setup"
|
|
}
|
|
}
|
|
|
|
# Readonly role for cross-account access
|
|
resource "aws_iam_role" "cross_account_readonly" {
|
|
provider = aws.new_account
|
|
count = var.create_cross_account_roles ? 1 : 0
|
|
|
|
name = "cross-account-readonly"
|
|
path = "/cross-account/"
|
|
|
|
assume_role_policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Action = "sts:AssumeRole"
|
|
Effect = "Allow"
|
|
Principal = {
|
|
AWS = var.readonly_trusted_principals
|
|
}
|
|
}]
|
|
})
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "cross-account-readonly"
|
|
})
|
|
|
|
depends_on = [aws_organizations_account.this]
|
|
}
|
|
|
|
resource "aws_iam_role_policy_attachment" "cross_account_readonly" {
|
|
provider = aws.new_account
|
|
count = var.create_cross_account_roles ? 1 : 0
|
|
|
|
role = aws_iam_role.cross_account_readonly[0].name
|
|
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
|
|
}
|
|
|
|
# Admin role for cross-account access (requires MFA)
|
|
resource "aws_iam_role" "cross_account_admin" {
|
|
provider = aws.new_account
|
|
count = var.create_cross_account_roles ? 1 : 0
|
|
|
|
name = "cross-account-admin"
|
|
path = "/cross-account/"
|
|
|
|
assume_role_policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Action = "sts:AssumeRole"
|
|
Effect = "Allow"
|
|
Principal = {
|
|
AWS = var.admin_trusted_principals
|
|
}
|
|
Condition = {
|
|
Bool = {
|
|
"aws:MultiFactorAuthPresent" = "true"
|
|
}
|
|
}
|
|
}]
|
|
})
|
|
|
|
max_session_duration = 3600
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "cross-account-admin"
|
|
})
|
|
|
|
depends_on = [aws_organizations_account.this]
|
|
}
|
|
|
|
resource "aws_iam_role_policy_attachment" "cross_account_admin" {
|
|
provider = aws.new_account
|
|
count = var.create_cross_account_roles ? 1 : 0
|
|
|
|
role = aws_iam_role.cross_account_admin[0].name
|
|
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
|
|
}
|
|
|
|
################################################################################
|
|
# Account Baseline (in new account)
|
|
################################################################################
|
|
|
|
module "account_baseline" {
|
|
source = "../account-baseline"
|
|
count = var.apply_baseline ? 1 : 0
|
|
|
|
providers = {
|
|
aws = aws.new_account
|
|
}
|
|
|
|
name = var.account_name
|
|
|
|
enable_ebs_encryption = true
|
|
enable_s3_block_public = true
|
|
enable_password_policy = true
|
|
enable_access_analyzer = true
|
|
|
|
# Security services typically managed by delegated admin
|
|
enable_securityhub = false
|
|
enable_guardduty = false
|
|
enable_config = false
|
|
|
|
tags = merge(var.tags, local.account_tags)
|
|
|
|
depends_on = [aws_organizations_account.this]
|
|
}
|
|
|
|
################################################################################
|
|
# Budget (in new account)
|
|
################################################################################
|
|
|
|
resource "aws_budgets_budget" "this" {
|
|
provider = aws.new_account
|
|
count = var.budget_limit > 0 ? 1 : 0
|
|
|
|
name = "${var.account_name}-monthly-budget"
|
|
budget_type = "COST"
|
|
limit_amount = tostring(var.budget_limit)
|
|
limit_unit = "USD"
|
|
time_unit = "MONTHLY"
|
|
|
|
notification {
|
|
comparison_operator = "GREATER_THAN"
|
|
threshold = 80
|
|
threshold_type = "PERCENTAGE"
|
|
notification_type = "ACTUAL"
|
|
subscriber_email_addresses = [var.owner_email != "" ? var.owner_email : local.account_email]
|
|
}
|
|
|
|
notification {
|
|
comparison_operator = "GREATER_THAN"
|
|
threshold = 100
|
|
threshold_type = "PERCENTAGE"
|
|
notification_type = "ACTUAL"
|
|
subscriber_email_addresses = [var.owner_email != "" ? var.owner_email : local.account_email]
|
|
}
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.account_name}-monthly-budget"
|
|
})
|
|
|
|
depends_on = [aws_organizations_account.this]
|
|
}
|