Skip to content

Commit f234986

Browse files
committed
Add Terraform GitHub Actions workflow and setup
1 parent e05efcb commit f234986

File tree

17 files changed

+1269
-3
lines changed

17 files changed

+1269
-3
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# This workflow uses OpenID Connect (OIDC) to securely authenticate with AWS.
2+
# It eliminates the need to store long-lived AWS Access Keys as GitHub secrets.
3+
4+
name: 'Terraform CI/CD'
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
11+
# Grant the GitHub Actions runner permissions to get an ID token.
12+
permissions:
13+
id-token: write # This is crucial for OIDC authentication
14+
contents: read
15+
16+
jobs:
17+
terraform:
18+
name: 'Terraform Plan and Apply'
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
# Step 1: Checkout the repository code.
23+
- name: Checkout Code
24+
uses: actions/checkout@v4
25+
26+
# Step 2: Configure AWS credentials using OIDC.
27+
# This action assumes an IAM role in your AWS account using a short-lived token.
28+
- name: Configure AWS Credentials
29+
uses: aws-actions/configure-aws-credentials@v4
30+
with:
31+
# The IAM role ARN to assume.
32+
# We now use the GitHub secret for the AWS account ID.
33+
role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GitHubActionsRole'
34+
# Use the repository variable for the AWS region.
35+
aws-region: ${{ vars.AWS_REGION }}
36+
37+
# Step 3: Install the latest version of Terraform.
38+
- name: Setup Terraform
39+
uses: hashicorp/setup-terraform@v3
40+
with:
41+
terraform_version: 1.12.2
42+
43+
# Step 4: Inject sensitive variables from a GitHub Secret.
44+
- name: Create tfvars file from secret
45+
env:
46+
TF_VARS: ${{ secrets.TF_VARS_CONTENT }}
47+
run: echo "$TF_VARS" > terraform.tfvars
48+
49+
# Step 5: Initialize the Terraform project.
50+
- name: Terraform Init
51+
id: init
52+
run: terraform init
53+
54+
# Step 6: Run Checkov to scan for IaC security vulnerabilities.
55+
- name: Run Checkov
56+
id: checkov
57+
uses: bridgecrewio/checkov-action@v12
58+
with:
59+
framework: terraform
60+
directory: '.'
61+
continue-on-error: true
62+
63+
# Step 7: Validate the Terraform code.
64+
- name: Terraform Validate
65+
id: validate
66+
run: terraform validate
67+
68+
# Step 8: Create a Terraform plan.
69+
- name: Terraform Plan
70+
id: plan
71+
run: terraform plan -no-color -var-file=terraform.tfvars
72+
73+
# Step 9: Apply the Terraform plan.
74+
- name: Terraform Apply
75+
if: github.ref == 'refs/heads/main'
76+
run: terraform apply -auto-approve -var-file=terraform.tfvars

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
.terraform
33
.terraform.lock.hcl
44
*.tfstate
5-
*.tfstate.backup
5+
*.tfstate.backup
6+
*.tfvars
7+
# This is a good practice to avoid exposing your AWS credentials if you're using them.
8+
.aws/credentials
9+
# EC2 key pair file
10+
ec2-new-key.pem

app_load_balancer.tf

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# load_balancer.tf
2+
3+
# Application Load Balancer
4+
# amazonq-ignore-next-line
5+
# amazonq-ignore-next-line
6+
resource "aws_lb" "web_alb" {
7+
name = "web-alb"
8+
internal = false
9+
load_balancer_type = "application"
10+
security_groups = [aws_security_group.alb_security_group.id]
11+
subnets = [aws_subnet.public-web-subnet-1.id, aws_subnet.public-web-subnet-2.id] # Assumes public subnet variables exist
12+
13+
tags = {
14+
Name = "Web ALB"
15+
}
16+
}
17+
18+
# Target Group to route traffic to the web tier EC2 instances
19+
resource "aws_lb_target_group" "web_target_group" {
20+
name = "web-tg"
21+
port = 80
22+
protocol = "HTTP"
23+
vpc_id = aws_vpc.three_tier_app_vpc.id
24+
25+
health_check {
26+
path = "/"
27+
interval = 30
28+
timeout = 10
29+
healthy_threshold = 5
30+
unhealthy_threshold = 8
31+
}
32+
}
33+
34+
# amazonq-ignore-next-line
35+
# Listener to forward HTTP traffic from the ALB to the target group
36+
# amazonq-ignore-next-line
37+
resource "aws_lb_listener" "http" {
38+
# amazonq-ignore-next-line
39+
load_balancer_arn = aws_lb.web_alb.arn
40+
port = 80
41+
protocol = "HTTP"
42+
43+
default_action {
44+
type = "forward"
45+
target_group_arn = aws_lb_target_group.web_target_group.arn
46+
}
47+
}

ec2.tf

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Description: This file defines the EC2 instances for the web
2+
# and app tiers using Auto Scaling Groups for high availability.
3+
# -------------------------------------------------------------
4+
5+
### Data source ###
6+
data "aws_ami" "amazon_linux_2" {
7+
most_recent = true
8+
owners = ["amazon"]
9+
10+
filter {
11+
name = "owner-alias"
12+
values = ["amazon"]
13+
}
14+
15+
filter {
16+
name = "name"
17+
values = ["amzn2-ami-hvm*"]
18+
}
19+
}
20+
21+
resource "aws_key_pair" "terraform_key" {
22+
key_name = "ec2-new-key"
23+
public_key = tls_private_key.rsa.public_key_openssh
24+
}
25+
26+
resource "tls_private_key" "rsa" {
27+
algorithm = "RSA"
28+
rsa_bits = 4096
29+
}
30+
31+
resource "aws_secretsmanager_secret" "ec2_private_key" {
32+
name = "ec2-private-key"
33+
description = "EC2 private key for SSH access"
34+
}
35+
36+
resource "aws_secretsmanager_secret_version" "ec2_private_key_version" {
37+
secret_id = aws_secretsmanager_secret.ec2_private_key.id
38+
secret_string = tls_private_key.rsa.private_key_pem
39+
}
40+
41+
### User Data Script for Web Server ###
42+
data "template_file" "web_user_data" {
43+
template = file("${path.module}/user_data/web_user_data.sh")
44+
}
45+
46+
### User Data Script for App Server ###
47+
data "template_file" "app_user_data" {
48+
template = file("${path.module}/user_data/app_user_data.sh")
49+
}
50+
51+
### Launch Template for Web Tier ###
52+
resource "aws_launch_template" "web_tier_template" {
53+
name_prefix = "web-tier-"
54+
image_id = data.aws_ami.amazon_linux_2.id
55+
instance_type = "t2.micro"
56+
key_name = aws_key_pair.terraform_key.key_name
57+
user_data = base64encode(data.template_file.web_user_data.rendered)
58+
59+
block_device_mappings {
60+
device_name = "/dev/xvda"
61+
ebs {
62+
volume_size = 8
63+
encrypted = true
64+
}
65+
}
66+
67+
iam_instance_profile {
68+
name = aws_iam_instance_profile.web_profile.name
69+
}
70+
71+
vpc_security_group_ids = [aws_security_group.webserver_security_group.id]
72+
73+
tag_specifications {
74+
resource_type = "instance"
75+
tags = {
76+
Name = "Web-Tier-Instance"
77+
}
78+
}
79+
}
80+
81+
### Launch Template for App Tier ###
82+
resource "aws_launch_template" "app_tier_template" {
83+
name_prefix = "app-tier-"
84+
image_id = data.aws_ami.amazon_linux_2.id
85+
instance_type = "t2.micro"
86+
key_name = aws_key_pair.terraform_key.key_name
87+
user_data = base64encode(data.template_file.app_user_data.rendered)
88+
89+
block_device_mappings {
90+
device_name = "/dev/xvda"
91+
ebs {
92+
volume_size = 8
93+
encrypted = true
94+
}
95+
}
96+
97+
iam_instance_profile {
98+
name = aws_iam_instance_profile.app_profile.name
99+
}
100+
101+
vpc_security_group_ids = [aws_security_group.appserver_security_group.id]
102+
103+
tag_specifications {
104+
resource_type = "instance"
105+
tags = {
106+
Name = "App-Tier-Instance"
107+
}
108+
}
109+
}
110+
111+
### Auto Scaling Group for Web Tier ###
112+
resource "aws_autoscaling_group" "web_asg" {
113+
name = "web-tier-asg"
114+
vpc_zone_identifier = [aws_subnet.public-web-subnet-1.id, aws_subnet.public-web-subnet-2.id]
115+
desired_capacity = 2
116+
max_size = 2
117+
min_size = 2
118+
health_check_type = "ELB"
119+
health_check_grace_period = 300
120+
target_group_arns = [aws_lb_target_group.web_target_group.arn]
121+
122+
launch_template {
123+
id = aws_launch_template.web_tier_template.id
124+
version = "$Latest"
125+
}
126+
127+
tag {
128+
key = "Name"
129+
value = "Web-Tier-ASG-Instance"
130+
propagate_at_launch = true
131+
}
132+
}
133+
134+
### Auto Scaling Group for App Tier ###
135+
resource "aws_autoscaling_group" "app_asg" {
136+
name = "app-tier-asg"
137+
vpc_zone_identifier = [aws_subnet.private-app-subnet-1.id, aws_subnet.private-app-subnet-2.id]
138+
desired_capacity = 2
139+
max_size = 2
140+
min_size = 2
141+
health_check_type = "EC2"
142+
health_check_grace_period = 300
143+
144+
launch_template {
145+
id = aws_launch_template.app_tier_template.id
146+
version = "$Latest"
147+
}
148+
149+
tag {
150+
key = "Name"
151+
value = "App-Tier-ASG-Instance"
152+
propagate_at_launch = true
153+
}
154+
}
155+
156+
# Instance profiles to attach the IAM roles to the EC2 instances
157+
resource "aws_iam_instance_profile" "web_profile" {
158+
name = "web-tier-profile"
159+
role = aws_iam_role.web_tier_role.name
160+
}
161+
162+
resource "aws_iam_instance_profile" "app_profile" {
163+
name = "app-tier-profile"
164+
role = aws_iam_role.app_tier_role.name
165+
}

0 commit comments

Comments
 (0)