Skip to content

Commit 40cd9d0

Browse files
committed
dev corrected
1 parent c7dda29 commit 40cd9d0

16 files changed

Lines changed: 474 additions & 314 deletions

Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ TFVARS_AWS_REGION := $(shell awk -F'=' '/^aws_region[[:space:]]*=/{gsub(/["[:spa
5050

5151
PROJECT_NAME := $(or $(TFVARS_PROJECT_NAME),$(PROJECT_NAME),mypythonproject1)
5252
AWS_REGION := $(or $(TFVARS_AWS_REGION),$(AWS_REGION),us-east-1)
53+
DOCKER_PLATFORM ?= linux/amd64
5354

5455
ECS_CLUSTER ?= $(PROJECT_NAME)-cluster-$(ENV)
5556
ECS_SERVICE_BACKEND ?= backend-service-$(ENV)
@@ -98,7 +99,7 @@ help:
9899
@echo " $(YELLOW)make lint$(NC) - Ruff lint+format-check (backend) + ESLint + type-check (frontend)"
99100
@echo ""
100101
@echo "$(GREEN)⚙️ SETUP$(NC)"
101-
@echo " $(YELLOW)make bootstrap$(NC) - One-time AWS infra bootstrap (ECR, IAM, S3, DynamoDB)"
102+
@echo " $(YELLOW)make bootstrap$(NC) - Terraform bootstrap (ECR, IAM, S3, optional DynamoDB lock table)"
102103
@echo " $(YELLOW)make setup-env$(NC) - Export env vars for Terraform/deploy (ENV=staging|prod|dev)"
103104
@echo ""
104105
@echo "$(GREEN)🏗️ TERRAFORM$(NC)"
@@ -305,9 +306,9 @@ tf-destroy:
305306

306307
docker-build:
307308
@if [ -z "$(ENV)" ]; then echo "$(RED)❌ ENV not set. Usage: make docker-build ENV=staging$(NC)"; exit 1; fi
308-
@echo "$(GREEN)🔨 Building Docker images for ENV=$(ENV)...$(NC)"
309-
docker build --build-arg BUILD_ENV=$(ENV) -t mypythonproject1/backend:$(ENV) ./backend
310-
docker build --build-arg BUILD_ENV=$(ENV) -t mypythonproject1/frontend:$(ENV) ./frontend
309+
@echo "$(GREEN)🔨 Building Docker images for ENV=$(ENV) (platform=$(DOCKER_PLATFORM))...$(NC)"
310+
docker buildx build --platform $(DOCKER_PLATFORM) --provenance=false --sbom=false --build-arg BUILD_ENV=$(ENV) -t mypythonproject1/backend:$(ENV) --load ./backend
311+
docker buildx build --platform $(DOCKER_PLATFORM) --provenance=false --sbom=false --build-arg BUILD_ENV=$(ENV) -t mypythonproject1/frontend:$(ENV) --load ./frontend
311312
@echo "$(GREEN)✅ Docker images built$(NC)"
312313

313314
docker-push:

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ At minimum, create:
122122
3. IAM roles assumed by GitHub Actions environments (`staging`, `production`)
123123
4. ECR repositories for backend/frontend images
124124

125+
Run bootstrap:
126+
127+
```bash
128+
make bootstrap
129+
```
130+
131+
Optional inputs:
132+
133+
```bash
134+
GITHUB_ORG=<org> GITHUB_REPO=<repo> AWS_REGION=us-east-1 make bootstrap
135+
```
136+
125137
See `infra/README.md` for full bootstrap and IAM guidance.
126138

127139
## Versioning

infra/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ Notes:
6868
3. Create IAM roles trusted for GitHub Environments (`staging`, `production`)
6969
4. Create ECR repositories for backend/frontend
7070

71+
Recommended bootstrap path in this repo:
72+
73+
```bash
74+
make bootstrap
75+
```
76+
77+
This runs the dedicated stack in `infra/bootstrap`.
78+
7179
## Local usage
7280

7381
```bash
@@ -104,4 +112,6 @@ terraform plan -var-file="envs/staging.tfvars"
104112
- `envs/prod.tfvars`: production sizing/capacity
105113
- `envs/dev.tfvars`: developer/shared lower-cost setup
106114

115+
Note for `dev`: ECS desired counts are intentionally set to `0` so first-time `terraform apply` succeeds even before ECR images are pushed. After pushing images, scale up by setting `desired_count` / `frontend_desired_count` (and `min_capacity`) above `0`.
116+
107117
Keep shared structure in modules and only vary environment inputs in tfvars.

infra/backend-config.hcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
bucket = "terraform-state-040769423303"
1+
bucket = "terraform-state-767397670484"
22
key = "terraform/dev/terraform.tfstate"
33
region = "us-east-1"
44
use_lockfile = true

infra/bootstrap/main.tf

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
data "aws_caller_identity" "current" {}
2+
3+
data "aws_partition" "current" {}
4+
5+
locals {
6+
account_id = data.aws_caller_identity.current.account_id
7+
effective_bucket = var.state_bucket_name != "" ? var.state_bucket_name : "terraform-state-${local.account_id}"
8+
backend_repo_name = "${var.project_name}/backend"
9+
frontend_repo_name = "${var.project_name}/frontend"
10+
}
11+
12+
resource "aws_s3_bucket" "terraform_state" {
13+
bucket = local.effective_bucket
14+
15+
tags = {
16+
Name = local.effective_bucket
17+
Purpose = "terraform-state"
18+
}
19+
}
20+
21+
resource "aws_s3_bucket_versioning" "terraform_state" {
22+
bucket = aws_s3_bucket.terraform_state.id
23+
24+
versioning_configuration {
25+
status = "Enabled"
26+
}
27+
}
28+
29+
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
30+
bucket = aws_s3_bucket.terraform_state.id
31+
32+
rule {
33+
apply_server_side_encryption_by_default {
34+
sse_algorithm = "AES256"
35+
}
36+
}
37+
}
38+
39+
resource "aws_s3_bucket_public_access_block" "terraform_state" {
40+
bucket = aws_s3_bucket.terraform_state.id
41+
42+
block_public_acls = true
43+
block_public_policy = true
44+
ignore_public_acls = true
45+
restrict_public_buckets = true
46+
}
47+
48+
resource "aws_dynamodb_table" "terraform_lock" {
49+
count = var.create_lock_table ? 1 : 0
50+
name = var.lock_table_name
51+
billing_mode = "PAY_PER_REQUEST"
52+
hash_key = "LockID"
53+
54+
attribute {
55+
name = "LockID"
56+
type = "S"
57+
}
58+
59+
tags = {
60+
Name = var.lock_table_name
61+
Purpose = "terraform-lock-legacy"
62+
}
63+
}
64+
65+
resource "aws_ecr_repository" "backend" {
66+
name = local.backend_repo_name
67+
image_tag_mutability = "MUTABLE"
68+
69+
image_scanning_configuration {
70+
scan_on_push = true
71+
}
72+
73+
encryption_configuration {
74+
encryption_type = "AES256"
75+
}
76+
}
77+
78+
resource "aws_ecr_repository" "frontend" {
79+
name = local.frontend_repo_name
80+
image_tag_mutability = "MUTABLE"
81+
82+
image_scanning_configuration {
83+
scan_on_push = true
84+
}
85+
86+
encryption_configuration {
87+
encryption_type = "AES256"
88+
}
89+
}
90+
91+
resource "aws_iam_openid_connect_provider" "github" {
92+
url = "https://token.actions.githubusercontent.com"
93+
client_id_list = ["sts.amazonaws.com"]
94+
thumbprint_list = var.oidc_thumbprints
95+
}
96+
97+
data "aws_iam_policy_document" "github_actions_trust" {
98+
statement {
99+
effect = "Allow"
100+
101+
actions = ["sts:AssumeRoleWithWebIdentity"]
102+
103+
principals {
104+
type = "Federated"
105+
identifiers = [aws_iam_openid_connect_provider.github.arn]
106+
}
107+
108+
condition {
109+
test = "StringEquals"
110+
variable = "token.actions.githubusercontent.com:aud"
111+
values = ["sts.amazonaws.com"]
112+
}
113+
114+
condition {
115+
test = "StringLike"
116+
variable = "token.actions.githubusercontent.com:sub"
117+
values = [
118+
for env in var.github_environments :
119+
"repo:${var.github_org}/${var.github_repo}:environment:${env}"
120+
]
121+
}
122+
}
123+
}
124+
125+
resource "aws_iam_role" "github_actions" {
126+
name = var.github_actions_role_name
127+
assume_role_policy = data.aws_iam_policy_document.github_actions_trust.json
128+
}
129+
130+
data "aws_iam_policy_document" "github_actions_permissions" {
131+
statement {
132+
sid = "ECR"
133+
effect = "Allow"
134+
actions = ["ecr:*"]
135+
resources = [
136+
aws_ecr_repository.backend.arn,
137+
aws_ecr_repository.frontend.arn,
138+
"arn:${data.aws_partition.current.partition}:ecr:${var.aws_region}:${local.account_id}:repository/${local.backend_repo_name}",
139+
"arn:${data.aws_partition.current.partition}:ecr:${var.aws_region}:${local.account_id}:repository/${local.frontend_repo_name}"
140+
]
141+
}
142+
143+
statement {
144+
sid = "ECRAuth"
145+
effect = "Allow"
146+
actions = ["ecr:GetAuthorizationToken"]
147+
resources = ["*"]
148+
}
149+
150+
statement {
151+
sid = "ECSAndInfraDeploy"
152+
effect = "Allow"
153+
actions = [
154+
"ecs:*",
155+
"ec2:*",
156+
"elasticloadbalancing:*",
157+
"logs:*",
158+
"cloudwatch:*",
159+
"secretsmanager:*",
160+
"kms:*",
161+
"rds:*"
162+
]
163+
resources = ["*"]
164+
}
165+
166+
statement {
167+
sid = "StateBucket"
168+
effect = "Allow"
169+
actions = ["s3:*"]
170+
resources = [
171+
aws_s3_bucket.terraform_state.arn,
172+
"${aws_s3_bucket.terraform_state.arn}/*"
173+
]
174+
}
175+
176+
statement {
177+
sid = "LegacyLockTable"
178+
effect = "Allow"
179+
actions = ["dynamodb:*"]
180+
resources = var.create_lock_table ? [aws_dynamodb_table.terraform_lock[0].arn] : ["*"]
181+
}
182+
183+
statement {
184+
sid = "PassRole"
185+
effect = "Allow"
186+
actions = ["iam:PassRole", "iam:GetRole", "iam:CreateServiceLinkedRole"]
187+
resources = ["*"]
188+
}
189+
}
190+
191+
resource "aws_iam_role_policy" "github_actions" {
192+
name = "GitHubActionsPolicy"
193+
role = aws_iam_role.github_actions.id
194+
policy = data.aws_iam_policy_document.github_actions_permissions.json
195+
}

infra/bootstrap/outputs.tf

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
output "aws_account_id" {
2+
description = "AWS account id"
3+
value = data.aws_caller_identity.current.account_id
4+
}
5+
6+
output "aws_region" {
7+
description = "AWS region used by bootstrap"
8+
value = var.aws_region
9+
}
10+
11+
output "terraform_state_bucket" {
12+
description = "Terraform remote state bucket"
13+
value = aws_s3_bucket.terraform_state.bucket
14+
}
15+
16+
output "terraform_lock_table" {
17+
description = "Legacy lock table name (empty when disabled)"
18+
value = var.create_lock_table ? aws_dynamodb_table.terraform_lock[0].name : ""
19+
}
20+
21+
output "github_actions_role_arn" {
22+
description = "Role ARN for GitHub Actions OIDC"
23+
value = aws_iam_role.github_actions.arn
24+
}
25+
26+
output "backend_ecr_repository_url" {
27+
description = "Backend ECR repository URL"
28+
value = aws_ecr_repository.backend.repository_url
29+
}
30+
31+
output "frontend_ecr_repository_url" {
32+
description = "Frontend ECR repository URL"
33+
value = aws_ecr_repository.frontend.repository_url
34+
}

infra/bootstrap/providers.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
terraform {
2+
required_version = ">= 1.5"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = "~> 5.0"
8+
}
9+
}
10+
}
11+
12+
provider "aws" {
13+
region = var.aws_region
14+
}

infra/bootstrap/variables.tf

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
variable "aws_region" {
2+
description = "AWS region for bootstrap resources"
3+
type = string
4+
default = "us-east-1"
5+
}
6+
7+
variable "project_name" {
8+
description = "Project name prefix"
9+
type = string
10+
default = "mypythonproject1"
11+
}
12+
13+
variable "github_org" {
14+
description = "GitHub organization/user name"
15+
type = string
16+
}
17+
18+
variable "github_repo" {
19+
description = "GitHub repository name"
20+
type = string
21+
}
22+
23+
variable "github_actions_role_name" {
24+
description = "IAM role name assumed by GitHub Actions via OIDC"
25+
type = string
26+
default = "GitHubActionsRole"
27+
}
28+
29+
variable "github_environments" {
30+
description = "Allowed GitHub Environments that can assume this role"
31+
type = list(string)
32+
default = ["staging", "production"]
33+
}
34+
35+
variable "state_bucket_name" {
36+
description = "Override for Terraform state bucket name; empty uses terraform-state-<account-id>"
37+
type = string
38+
default = ""
39+
}
40+
41+
variable "lock_table_name" {
42+
description = "DynamoDB lock table name (legacy compatibility)"
43+
type = string
44+
default = "terraform-locks"
45+
}
46+
47+
variable "create_lock_table" {
48+
description = "Create DynamoDB lock table for backward compatibility"
49+
type = bool
50+
default = true
51+
}
52+
53+
variable "oidc_thumbprints" {
54+
description = "Thumbprints for GitHub OIDC provider"
55+
type = list(string)
56+
default = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
57+
}

infra/envs/dev.tfvars

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ log_retention_days = 3
2323

2424
# ECS
2525
image_tag = "dev"
26+
frontend_image_tag = "dev"
2627
task_cpu = "256"
2728
task_memory = "512"
2829
desired_count = 1
30+
frontend_desired_count = 1
2931
min_capacity = 1
3032
max_capacity = 3
3133
target_cpu_utilization = 70

0 commit comments

Comments
 (0)