mirror of
https://github.com/ghndrx/terraform-foundation.git
synced 2026-02-10 06:45:06 +00:00
feat: Terraform Foundation - AWS Landing Zone
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
This commit is contained in:
432
terraform/03-platform/main.tf
Normal file
432
terraform/03-platform/main.tf
Normal file
@@ -0,0 +1,432 @@
|
||||
################################################################################
|
||||
# Layer 03: Platform
|
||||
#
|
||||
# Shared platform services for all tenants:
|
||||
# - ECR repositories for container images
|
||||
# - CodePipeline/CodeBuild for CI/CD
|
||||
# - Secrets Manager baseline
|
||||
# - SSM Parameter Store hierarchy
|
||||
#
|
||||
# Depends on: 00-bootstrap, 02-network
|
||||
################################################################################
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = ">= 5.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {
|
||||
key = "03-platform/terraform.tfstate"
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Variables
|
||||
################################################################################
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = "us-east-1"
|
||||
}
|
||||
|
||||
variable "state_bucket" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "project_name" {
|
||||
type = string
|
||||
description = "Project name for resource naming"
|
||||
}
|
||||
|
||||
variable "enable_cicd" {
|
||||
type = bool
|
||||
default = true
|
||||
description = "Enable CI/CD resources (CodeBuild, S3 artifacts)"
|
||||
}
|
||||
|
||||
variable "ecr_repos" {
|
||||
type = list(string)
|
||||
default = ["base", "app"]
|
||||
description = "List of shared ECR repositories to create"
|
||||
}
|
||||
|
||||
variable "ecr_image_retention_count" {
|
||||
type = number
|
||||
default = 30
|
||||
description = "Number of images to retain per repository"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Provider
|
||||
################################################################################
|
||||
|
||||
provider "aws" {
|
||||
region = var.region
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
Layer = "03-platform"
|
||||
ManagedBy = "terraform"
|
||||
Project = var.project_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Data Sources
|
||||
################################################################################
|
||||
|
||||
data "terraform_remote_state" "network" {
|
||||
backend = "s3"
|
||||
config = {
|
||||
bucket = var.state_bucket
|
||||
key = "02-network/terraform.tfstate"
|
||||
region = var.region
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_caller_identity" "current" {}
|
||||
data "aws_region" "current" {}
|
||||
|
||||
################################################################################
|
||||
# ECR Repositories
|
||||
################################################################################
|
||||
|
||||
resource "aws_ecr_repository" "shared" {
|
||||
for_each = toset(var.ecr_repos)
|
||||
|
||||
name = "${var.project_name}/${each.key}"
|
||||
image_tag_mutability = "MUTABLE"
|
||||
|
||||
image_scanning_configuration {
|
||||
scan_on_push = true
|
||||
}
|
||||
|
||||
encryption_configuration {
|
||||
encryption_type = "AES256"
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "${var.project_name}-${each.key}"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ecr_lifecycle_policy" "shared" {
|
||||
for_each = aws_ecr_repository.shared
|
||||
repository = each.value.name
|
||||
|
||||
policy = jsonencode({
|
||||
rules = [
|
||||
{
|
||||
rulePriority = 1
|
||||
description = "Keep last ${var.ecr_image_retention_count} images"
|
||||
selection = {
|
||||
tagStatus = "any"
|
||||
countType = "imageCountMoreThan"
|
||||
countNumber = var.ecr_image_retention_count
|
||||
}
|
||||
action = { type = "expire" }
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# CI/CD - Artifact Bucket
|
||||
################################################################################
|
||||
|
||||
resource "aws_s3_bucket" "artifacts" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
bucket = "${var.project_name}-cicd-artifacts-${data.aws_caller_identity.current.account_id}"
|
||||
|
||||
tags = { Name = "CI/CD Artifacts" }
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_versioning" "artifacts" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
bucket = aws_s3_bucket.artifacts[0].id
|
||||
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_lifecycle_configuration" "artifacts" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
bucket = aws_s3_bucket.artifacts[0].id
|
||||
|
||||
rule {
|
||||
id = "cleanup-old-artifacts"
|
||||
status = "Enabled"
|
||||
|
||||
expiration {
|
||||
days = 90
|
||||
}
|
||||
|
||||
noncurrent_version_expiration {
|
||||
noncurrent_days = 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
bucket = aws_s3_bucket.artifacts[0].id
|
||||
|
||||
block_public_acls = true
|
||||
block_public_policy = true
|
||||
ignore_public_acls = true
|
||||
restrict_public_buckets = true
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# CI/CD - CodeBuild Role
|
||||
################################################################################
|
||||
|
||||
resource "aws_iam_role" "codebuild" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
name = "${var.project_name}-codebuild"
|
||||
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Action = "sts:AssumeRole"
|
||||
Principal = { Service = "codebuild.amazonaws.com" }
|
||||
}]
|
||||
})
|
||||
|
||||
tags = { Name = "${var.project_name}-codebuild" }
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "codebuild" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
name = "codebuild-policy"
|
||||
role = aws_iam_role.codebuild[0].id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Sid = "CloudWatchLogs"
|
||||
Effect = "Allow"
|
||||
Action = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
|
||||
Resource = [
|
||||
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/codebuild/${var.project_name}-*",
|
||||
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/codebuild/${var.project_name}-*:*"
|
||||
]
|
||||
},
|
||||
{
|
||||
Sid = "S3Artifacts"
|
||||
Effect = "Allow"
|
||||
Action = ["s3:GetObject", "s3:PutObject", "s3:GetObjectVersion"]
|
||||
Resource = "${aws_s3_bucket.artifacts[0].arn}/*"
|
||||
},
|
||||
{
|
||||
Sid = "ECRAuth"
|
||||
Effect = "Allow"
|
||||
Action = ["ecr:GetAuthorizationToken"]
|
||||
Resource = "*"
|
||||
},
|
||||
{
|
||||
Sid = "ECRPush"
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"ecr:BatchCheckLayerAvailability",
|
||||
"ecr:GetDownloadUrlForLayer",
|
||||
"ecr:BatchGetImage",
|
||||
"ecr:PutImage",
|
||||
"ecr:InitiateLayerUpload",
|
||||
"ecr:UploadLayerPart",
|
||||
"ecr:CompleteLayerUpload"
|
||||
]
|
||||
Resource = "arn:aws:ecr:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:repository/${var.project_name}/*"
|
||||
},
|
||||
{
|
||||
Sid = "SSMParams"
|
||||
Effect = "Allow"
|
||||
Action = ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"]
|
||||
Resource = "arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/*"
|
||||
},
|
||||
{
|
||||
Sid = "SecretsManager"
|
||||
Effect = "Allow"
|
||||
Action = ["secretsmanager:GetSecretValue"]
|
||||
Resource = "arn:aws:secretsmanager:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:secret:${var.project_name}/*"
|
||||
},
|
||||
{
|
||||
Sid = "VPC"
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"ec2:CreateNetworkInterface",
|
||||
"ec2:DescribeNetworkInterfaces",
|
||||
"ec2:DeleteNetworkInterface",
|
||||
"ec2:DescribeSubnets",
|
||||
"ec2:DescribeSecurityGroups",
|
||||
"ec2:DescribeVpcs"
|
||||
]
|
||||
Resource = "*"
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# CodeBuild - Shared Build Project
|
||||
################################################################################
|
||||
|
||||
resource "aws_codebuild_project" "build" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
name = "${var.project_name}-build"
|
||||
description = "Shared build project for ${var.project_name}"
|
||||
build_timeout = 30
|
||||
service_role = aws_iam_role.codebuild[0].arn
|
||||
|
||||
artifacts {
|
||||
type = "S3"
|
||||
location = aws_s3_bucket.artifacts[0].bucket
|
||||
packaging = "ZIP"
|
||||
}
|
||||
|
||||
environment {
|
||||
compute_type = "BUILD_GENERAL1_SMALL"
|
||||
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
|
||||
type = "LINUX_CONTAINER"
|
||||
image_pull_credentials_type = "CODEBUILD"
|
||||
privileged_mode = true # Required for Docker
|
||||
|
||||
environment_variable {
|
||||
name = "AWS_DEFAULT_REGION"
|
||||
value = data.aws_region.current.name
|
||||
}
|
||||
|
||||
environment_variable {
|
||||
name = "AWS_ACCOUNT_ID"
|
||||
value = data.aws_caller_identity.current.account_id
|
||||
}
|
||||
|
||||
environment_variable {
|
||||
name = "ECR_REGISTRY"
|
||||
value = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com"
|
||||
}
|
||||
}
|
||||
|
||||
source {
|
||||
type = "NO_SOURCE"
|
||||
buildspec = <<-YAML
|
||||
version: 0.2
|
||||
phases:
|
||||
pre_build:
|
||||
commands:
|
||||
- echo "Override this buildspec in your project"
|
||||
build:
|
||||
commands:
|
||||
- echo "Build phase"
|
||||
post_build:
|
||||
commands:
|
||||
- echo "Post-build phase"
|
||||
YAML
|
||||
}
|
||||
|
||||
vpc_config {
|
||||
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
|
||||
subnets = data.terraform_remote_state.network.outputs.private_subnet_ids
|
||||
security_group_ids = [aws_security_group.codebuild[0].id]
|
||||
}
|
||||
|
||||
logs_config {
|
||||
cloudwatch_logs {
|
||||
group_name = "/aws/codebuild/${var.project_name}"
|
||||
stream_name = "build"
|
||||
}
|
||||
}
|
||||
|
||||
tags = { Name = "${var.project_name}-build" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# CodeBuild Security Group
|
||||
################################################################################
|
||||
|
||||
resource "aws_security_group" "codebuild" {
|
||||
count = var.enable_cicd ? 1 : 0
|
||||
name = "${var.project_name}-codebuild"
|
||||
description = "Security group for CodeBuild"
|
||||
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
description = "Allow all outbound"
|
||||
}
|
||||
|
||||
tags = { Name = "${var.project_name}-codebuild" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# SSM Parameter Store - Hierarchy Base
|
||||
################################################################################
|
||||
|
||||
resource "aws_ssm_parameter" "platform_config" {
|
||||
name = "/${var.project_name}/platform/region"
|
||||
type = "String"
|
||||
value = data.aws_region.current.name
|
||||
|
||||
tags = { Name = "Platform Region" }
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "vpc_id" {
|
||||
name = "/${var.project_name}/platform/vpc_id"
|
||||
type = "String"
|
||||
value = data.terraform_remote_state.network.outputs.vpc_id
|
||||
|
||||
tags = { Name = "VPC ID" }
|
||||
}
|
||||
|
||||
resource "aws_ssm_parameter" "private_subnets" {
|
||||
name = "/${var.project_name}/platform/private_subnet_ids"
|
||||
type = "StringList"
|
||||
value = join(",", data.terraform_remote_state.network.outputs.private_subnet_ids)
|
||||
|
||||
tags = { Name = "Private Subnet IDs" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Outputs
|
||||
################################################################################
|
||||
|
||||
output "ecr_repositories" {
|
||||
value = {
|
||||
for k, v in aws_ecr_repository.shared : k => {
|
||||
arn = v.arn
|
||||
url = v.repository_url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "artifacts_bucket" {
|
||||
value = var.enable_cicd ? aws_s3_bucket.artifacts[0].id : null
|
||||
}
|
||||
|
||||
output "codebuild_project" {
|
||||
value = var.enable_cicd ? aws_codebuild_project.build[0].name : null
|
||||
}
|
||||
|
||||
output "codebuild_role_arn" {
|
||||
value = var.enable_cicd ? aws_iam_role.codebuild[0].arn : null
|
||||
}
|
||||
|
||||
output "codebuild_security_group" {
|
||||
value = var.enable_cicd ? aws_security_group.codebuild[0].id : null
|
||||
}
|
||||
|
||||
output "ssm_prefix" {
|
||||
value = "/${var.project_name}"
|
||||
}
|
||||
Reference in New Issue
Block a user