Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ infrastructure/
container-foundation.yaml
container-application.yaml
ec2-platform.yaml
native-cicd.yaml # planned
github-oidc.yaml
policies/
security.guard
```
Expand All @@ -46,6 +46,8 @@ CloudFormation root stack.
6. `ec2-platform.yaml` creates the non-containerized ALB, private frontend and
backend Auto Scaling groups, CodeDeploy resources, Systems Manager access,
CloudWatch logs, scaling policies, and basic alarms.
7. `github-oidc.yaml` creates the credential-free GitHub Actions trust and
application deployment role for ECS and EC2 releases.

The container platform is split into foundation and application stacks because
the ECR repositories must exist before the first immutable images can be
Expand Down Expand Up @@ -79,6 +81,13 @@ Application Load Balancer. Backend database credentials are not placed in
CloudFormation user data or CodeDeploy bundles. The backend reads the
RDS-managed secret at startup using its EC2 instance role.

The EC2 Auto Scaling groups use EC2 instance health rather than ELB health.
CodeDeploy intentionally removes instances from target groups during in-place
deployments; using ELB health at the Auto Scaling layer would replace healthy
instances while they are draining. Application health remains enforced by
CodeDeploy validation hooks, ALB target checks, CloudWatch alarms, and
automatic rollback.

## Basic Monitoring

Monitoring is embedded in the workload templates instead of being maintained
Expand Down Expand Up @@ -139,11 +148,80 @@ application deployment workflow:
- ECS cluster name
- Frontend and backend ECS service names
- Frontend and backend ECR repository URIs
- EC2 CodeDeploy artifact bucket, applications, and deployment groups

The application repository builds and publishes immutable images. It does not
run CloudFormation. This repository provisions and changes the platform. It
does not build application source.

## GitHub OIDC Deployment Role

Deploy `github-oidc.yaml` once per environment and configure its
`GitHubDeploymentRoleArn` output as the `AWS_ROLE_ARN` variable in the matching
GitHub Environment. Also configure `AWS_REGION`.

```bash
aws cloudformation deploy \
--stack-name coditude-dev-github-oidc \
--template-file infrastructure/nested/github-oidc.yaml \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
ProjectName=coditude \
Environment=dev \
--profile coditude-dev \
--region ap-south-1
```

The role can publish ECR images, update existing ECS services, upload immutable
CodeDeploy artifacts under the EC2 platform bucket, and create deployments for
the environment's frontend and backend deployment groups. The application
workflows do not create EC2, networking, database, or load-balancer resources.

## EC2 Deployment And Testing

Create the EC2 platform through a reviewed CloudFormation change set. The
template creates one private frontend and backend Auto Scaling group,
CodeDeploy applications and deployment groups, an ALB, CloudWatch logs,
scaling, and alarms.

After the stack completes, run **Deploy EC2** from the application repository.
Select `bootstrap=true` for the first release only. Later releases use
`bootstrap=false`.

Inspect the platform:

```bash
aws cloudformation describe-stacks \
--stack-name coditude-dev-ec2-platform \
--query 'Stacks[0].{Status:StackStatus,Outputs:Outputs}' \
--output json \
--profile coditude-dev \
--region ap-south-1

aws autoscaling describe-auto-scaling-groups \
--query 'AutoScalingGroups[?starts_with(AutoScalingGroupName, `coditude-dev-ec2-platform`)].{Name:AutoScalingGroupName,Desired:DesiredCapacity,Instances:Instances[].InstanceId}' \
--output table \
--profile coditude-dev \
--region ap-south-1
```

Verify the deployed application with the `ApplicationUrl` stack output:

```bash
curl http://EC2_ALB_DNS/
curl http://EC2_ALB_DNS/api/v1/message
```

The API response should contain:

```json
{
"message": "Backend is running successfully",
"environment": "dev",
"source": "postgresql"
}
```

## Multi-Environment Strategy

One reusable template set supports `dev`, `staging`, and `prod`. The
Expand Down
88 changes: 61 additions & 27 deletions infrastructure/nested/ec2-platform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Resources:
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckPath: /api/health
HealthCheckPort: traffic-port
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
Expand All @@ -308,6 +308,9 @@ Resources:
Port: 3000
Protocol: HTTP
TargetType: instance
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: "30"
UnhealthyThresholdCount: 3
VpcId: !Ref VpcId
Tags:
Expand All @@ -331,6 +334,9 @@ Resources:
Port: 8000
Protocol: HTTP
TargetType: instance
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: "30"
UnhealthyThresholdCount: 3
VpcId: !Ref VpcId
Tags:
Expand Down Expand Up @@ -400,6 +406,36 @@ Resources:
ListenerArn: !Ref HttpsListener
Priority: 10

PublicLoadBalancerToBackendEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
Description: Forward API requests to the EC2 backend
DestinationSecurityGroupId: !Ref BackendSecurityGroupId
FromPort: 8000
GroupId: !Ref PublicLoadBalancerSecurityGroupId
IpProtocol: tcp
ToPort: 8000

BackendFromPublicLoadBalancerIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Receive EC2 API requests from the public load balancer
FromPort: 8000
GroupId: !Ref BackendSecurityGroupId
IpProtocol: tcp
SourceSecurityGroupId: !Ref PublicLoadBalancerSecurityGroupId
ToPort: 8000

FrontendInternetHttpEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
CidrIp: 0.0.0.0/0
Description: Call the public EC2 application endpoint through NAT
FromPort: 80
GroupId: !Ref FrontendSecurityGroupId
IpProtocol: tcp
ToPort: 80

FrontendLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
Expand All @@ -421,6 +457,17 @@ Resources:
HttpTokens: required
SecurityGroupIds:
- !Ref FrontendSecurityGroupId
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: !Sub "${ProjectName}-${Environment}-frontend"
- Key: Project
Value: coditude
- Key: Environment
Value: !Ref Environment
- Key: ManagedBy
Value: cloudformation
UserData:
Fn::Base64: !Sub |
#!/bin/bash
Expand Down Expand Up @@ -468,18 +515,6 @@ Resources:
-a fetch-config -m ec2 \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json \
-s
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: !Sub "${ProjectName}-${Environment}-frontend"
- Key: Project
Value: coditude
- Key: Environment
Value: !Ref Environment
- Key: ManagedBy
Value: cloudformation

BackendLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
Expand All @@ -501,6 +536,17 @@ Resources:
HttpTokens: required
SecurityGroupIds:
- !Ref BackendSecurityGroupId
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: !Sub "${ProjectName}-${Environment}-backend"
- Key: Project
Value: coditude
- Key: Environment
Value: !Ref Environment
- Key: ManagedBy
Value: cloudformation
UserData:
Fn::Base64: !Sub |
#!/bin/bash
Expand Down Expand Up @@ -547,24 +593,12 @@ Resources:
-a fetch-config -m ec2 \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json \
-s
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: !Sub "${ProjectName}-${Environment}-backend"
- Key: Project
Value: coditude
- Key: Environment
Value: !Ref Environment
- Key: ManagedBy
Value: cloudformation

FrontendAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
DesiredCapacity: !Ref FrontendDesiredCapacity
HealthCheckGracePeriod: 300
HealthCheckType: ELB
HealthCheckType: EC2
LaunchTemplate:
LaunchTemplateId: !Ref FrontendLaunchTemplate
Version: !GetAtt FrontendLaunchTemplate.LatestVersionNumber
Expand All @@ -589,7 +623,7 @@ Resources:
Properties:
DesiredCapacity: !Ref BackendDesiredCapacity
HealthCheckGracePeriod: 300
HealthCheckType: ELB
HealthCheckType: EC2
LaunchTemplate:
LaunchTemplateId: !Ref BackendLaunchTemplate
Version: !GetAtt BackendLaunchTemplate.LatestVersionNumber
Expand Down
24 changes: 23 additions & 1 deletion infrastructure/nested/github-oidc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,28 @@ Resources:
- servicediscovery:TagResource
- servicediscovery:UntagResource
Resource: "*"
- PolicyName: DeployEc2Application
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: !Sub
- "arn:${AWS::Partition}:s3:::${Project}-${Stage}-ec2-platform-deploymentartifactbucket-*/releases/*"
- Project: !Ref ProjectName
Stage: !Ref Environment
- Effect: Allow
Action:
- codedeploy:CreateDeployment
- codedeploy:GetDeployment
- codedeploy:GetDeploymentGroup
- codedeploy:UpdateDeploymentGroup
Resource: !Sub
- "arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${Project}-${Stage}-*/${Project}-${Stage}-*"
- Project: !Ref ProjectName
Stage: !Ref Environment
Tags:
- Key: Project
Value: coditude
Expand All @@ -182,7 +204,7 @@ Resources:

Outputs:
GitHubDeploymentRoleArn:
Description: Role ARN configured in the GitHub dev environment.
Description: Application deployment role configured in the GitHub environment.
Value: !GetAtt GitHubDeploymentRole.Arn

GitHubOidcProviderArn:
Expand Down