Deployment Guide

Step-by-step guide to deploy the stage environment

Prerequisites

Software on local machine

  • Terraform ≥ 1.5
  • AWS CLI v2 (with aws, aws sso login working)
  • PowerShell 5.1+ (Windows default OK)

AWS Permissions

  • Account must have permissions to create: VPC, EC2, ELB (ALB/NLB), ECR, ACM, Route53, CloudWatch, IAM, S3, CloudFront
  • IAM role MILU2-AWS-ALLOW already exists (or set existing_iam = false in shared to create)
  • Access to DNS zone *.milu.jp (for ACM certificate validation)

AWS Login

AWS Login
aws sso login                 # open browser, login once per day
aws sts get-caller-identity   # check if credentials are valid

Info

If credentials expire mid-way (1h), deploy.ps1 will automatically re-login.

Pre-deploy check (preflight)

Preflight Check
cd d:\terraform_svn\terraform-resource\terraform
.\preflight-check.ps1

Script checks 10 items: credential, S3 bucket, IAM, CloudFront, ECR, AMI, ACM, VPC peering, CIDR conflict, region. Auto-fixes existing_* flags and stage_index in terraform.tfvars if needed.

Quick Deploy (1 command)

Script runs 7 steps automatically, pausing for confirmation at key points:

Quick Deploy
cd d:\terraform_svn\terraform-resource\terraform
.\deploy.ps1
StepActionInteraction
0AWS login (opens browser if expired)Confirm on web
1Confirm aws_region, az_*, ec2_ami_id, stage_indexEnter to keep, type new value to change
2Sync shared regionAutomatic
3Preflight checkEnter to continue
4Deploy Shared (only runs if existing = false)Automatic
5Deploy Stage: auto import → terraform plan → review → Enter → terraform applyReview plan then Enter
6Verify outputsAutomatic

Manual Deploy (when you need more control)

Manual Deploy (when you need more control)
cd d:\terraform_svn\terraform-resource\terraform

# (1) Preflight
.\preflight-check.ps1

# (2) Shared — run once per account, don't repeat unless shared changes
cd shared
terraform init
terraform plan          # Expect: No changes (if existing = true)
terraform apply         # Only run if plan shows changes
cd ..

# (3) Stage
terraform init
terraform plan          # Review what will be created
terraform apply         # Type 'yes' to confirm

Target Apply (single module)

Use when you only want to apply a small change (e.g., only change EC2 user_data, not touching VPC/ALB).

Target Apply
cd d:\terraform_svn\terraform-resource\terraform

# Apply only module 17-ec2-instances
terraform plan   -target='module.ec2_instances'
terraform apply  -target='module.ec2_instances'

# Apply only module 09-autoscaling (Launch Template + ASG API)
terraform plan   -target='module.autoscaling'
terraform apply  -target='module.autoscaling'

# Apply only module 06-alb
terraform plan   -target='module.alb'
terraform apply  -target='module.alb'

# Apply multiple modules at once
terraform apply -target='module.target_groups_listeners' -target='module.alb'

Apply only 1 instance in module 17

When instance_counts.mysql = 1, the state key will be mysql-1:

Replace Instance
# Replace EC2 mysql-1 (keep other EC2s)
terraform apply -replace='module.ec2_instances.aws_instance.this["mysql-1"]'

# Replace both mysql-1 and mysql_mirror-1
terraform apply `
  -replace='module.ec2_instances.aws_instance.this["mysql-1"]' `
  -replace='module.ec2_instances.aws_instance.this["mysql_mirror-1"]'

# Force refresh ASG (force immediate re-launch)
terraform apply -replace='module.autoscaling.aws_launch_template.api'

Info

-replace forces terraform to destroy + create that specific resource, without affecting neighbors.

Module names for targeting

ModuleTarget string
01-vpcmodule.vpc
02-subnetsmodule.subnets
03-internet-gatewaymodule.internet_gateway
04-route-tables-peeringmodule.route_tables_peering
05-security-groupsmodule.security_groups
06-albmodule.alb
07-nlbmodule.nlb
08-target-groups-listenersmodule.target_groups_listeners
09-autoscalingmodule.autoscaling
11-ecrmodule.ecr
12-acmmodule.acm
13-route53module.route53
15-iammodule.iam
16-cloudwatchmodule.cloudwatch
17-ec2-instancesmodule.ec2_instances

When resources already exist on AWS (import)

deploy.ps1 auto-detects and imports 31 resource types (ECR, ALB, NLB, TG, ASG, Launch Template, CloudWatch alarms…). For manual import:

Import Resources
# ECR
terraform import 'module.ecr.aws_ecr_repository.this["milu2/milu2-stage-api"]' milu2/milu2-stage-api

# ALB internal
terraform import 'module.alb.aws_lb.internal' arn:aws:elasticloadbalancing:...

# View current state
terraform state list

Multi-region / Multi-stage

No need to copy folders. From the same terraform/ directory:

  1. Change aws_region, az_1, az_2, ec2_ami_id in terraform.tfvars
  2. Run preflight-check.ps1 → script reads Build VPC route table, suggests non-conflicting stage_index
  3. Run deploy.ps1 as usual

Tip

Each region has its own state file (terraform.tfstate in current working directory).

Destroy (tear down environment)

Danger

Warning: destroy stage will delete all VPC, EC2, ALB, NLB. Data on /home2 (mysql) will be lost. Backup mysqldump --all-databases to S3 first.
Destroy
# Destroy stage, KEEP shared
.\deploy.ps1 -DestroyOnly
# When asked 'Destroy shared resources too?' → press Enter (don't type yes)

# Destroy BOTH shared + stage
.\deploy.ps1 -DestroyOnly
# When asked → type yes + Enter

# Or manually
terraform destroy
cd shared
terraform destroy
cd ..

Important Note — read before changing EC2 user_data

Warning

Module 17 has user_data_replace_on_change = true, meaning any change in scripts/*.sh will destroy + create EC2.

Consequences:

  • EBS root lost → data on /home/ec2-user, /var/log, … is lost
  • EBS /home2 (mysql) also lost → MySQL must be re-imported from schema_only.sql + mysql_users.sql on S3
  • ASG (module 09) does rolling refresh automatically, not destroyed suddenly

Before applying user_data changes to DB:

Backup before apply
# 1. Backup MySQL
mysqldump --all-databases > schema_only.sql
aws s3 cp schema_only.sql s3://<bucket>/docker/milu2-mysql/

# 2. Apply to each instance separately to reduce risk
terraform apply -replace='module.ec2_instances.aws_instance.this["mysql-1"]'

Tip

Hot-fix without redeploy: SSH to EC2 and edit /etc/systemd/system/mount-s3.service or /home/ec2-user/milu2/<role>/docker/run.sh directly, then systemctl restart.