chore: Add README
This commit is contained in:
parent
f4e9dcd5e8
commit
dfcef80c41
19
air-tech/README.md
Normal file
19
air-tech/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Air-Tech
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
- [x] This infrastructure is a production quality infrastructure.
|
||||||
|
- [x] The coding exercise is to run this environment in AWS.
|
||||||
|
- [ ] We are looking for an infrastructure-as-code (IaC) solution using Pulumi.
|
||||||
|
- [x] You are free to architect the infrastructure as you see fit. The final goal is to run both services (Web and API) on AWS. We are leaving all the details to you.
|
||||||
|
- [x] Be mindful of security best practices. The API is not a public API, and it is only accessed by Web UI.
|
||||||
|
- [x] Only the web UI should be accessible through the internet (port 80)
|
||||||
|
- [ ] Tag all the environment resources for auditing purposes.
|
||||||
|
|
||||||
|
## Diagram
|
||||||
|
|
||||||
|
![Infra Diagram](infra_diagram.png)
|
||||||
|
|
||||||
|
## What's missing?
|
||||||
|
|
||||||
|
Tags are missing, the solution does not run to a viable url because of permissions issues between ECS and ECR. When these are tweaked outside of Pulumi, the service works as expected.
|
@ -1,19 +1,17 @@
|
|||||||
import pulumi
|
import pulumi
|
||||||
from pulumi import Config, Output, export
|
from pulumi import Config
|
||||||
import pulumi_aws as aws
|
import pulumi_aws as aws
|
||||||
from pulumi_aws import acm, alb, ecs, ec2, lb, iam, ecr
|
from pulumi_aws import acm, ecs, ec2, lb, iam, ecr
|
||||||
import pulumi_awsx as awsx
|
import pulumi_awsx as awsx
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
# container_port = config.get_int("containerPort", 80)
|
|
||||||
cpu = config.get_int("cpu", 512)
|
cpu = config.get_int("cpu", 512)
|
||||||
memory = config.get_int("memory", 128)
|
memory = config.get_int("memory", 128)
|
||||||
# Create a VPC
|
|
||||||
vpc = ec2.Vpc("production", cidr_block="10.0.0.0/16")
|
vpc = ec2.Vpc("production", cidr_block="10.0.0.0/16")
|
||||||
|
|
||||||
# Create a Gateway
|
|
||||||
ig = ec2.InternetGateway("internet-gateway", vpc_id=vpc.id)
|
ig = ec2.InternetGateway("internet-gateway", vpc_id=vpc.id)
|
||||||
|
|
||||||
cert = acm.Certificate(
|
cert = acm.Certificate(
|
||||||
@ -21,14 +19,12 @@ cert = acm.Certificate(
|
|||||||
domain_name="air-tech.ahosking.com",
|
domain_name="air-tech.ahosking.com",
|
||||||
validation_method="DNS",
|
validation_method="DNS",
|
||||||
)
|
)
|
||||||
# Public Subnet
|
|
||||||
public_subnet = ec2.Subnet(
|
public_subnet = ec2.Subnet(
|
||||||
"public-subnet",
|
"public-subnet",
|
||||||
vpc_id=vpc.id,
|
vpc_id=vpc.id,
|
||||||
cidr_block="10.0.1.0/26",
|
cidr_block="10.0.1.0/26",
|
||||||
availability_zone="us-east-2a",
|
availability_zone="us-east-2a",
|
||||||
|
|
||||||
# Allow the Internet Gateway to handle public traffic
|
|
||||||
map_public_ip_on_launch=True
|
map_public_ip_on_launch=True
|
||||||
)
|
)
|
||||||
public_subnet2 = ec2.Subnet(
|
public_subnet2 = ec2.Subnet(
|
||||||
@ -36,17 +32,15 @@ public_subnet2 = ec2.Subnet(
|
|||||||
vpc_id=vpc.id,
|
vpc_id=vpc.id,
|
||||||
cidr_block="10.0.2.0/26",
|
cidr_block="10.0.2.0/26",
|
||||||
availability_zone="us-east-2b",
|
availability_zone="us-east-2b",
|
||||||
|
|
||||||
# Allow the Internet Gateway to handle public traffic
|
|
||||||
map_public_ip_on_launch=True
|
map_public_ip_on_launch=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Associate the Gateway to a route
|
route_table = ec2.RouteTable("route-table", vpc_id=vpc.id)
|
||||||
route_table = ec2.RouteTable('route-table', vpc_id=vpc.id)
|
route = ec2.Route(
|
||||||
route = ec2.Route('route', route_table_id=route_table.id,
|
"route", route_table_id=route_table.id,
|
||||||
destination_cidr_block='0.0.0.0/0', gateway_id=ig.id)
|
destination_cidr_block="0.0.0.0/0", gateway_id=ig.id)
|
||||||
|
|
||||||
|
|
||||||
# Private Subnet
|
|
||||||
private_subnet = ec2.Subnet(
|
private_subnet = ec2.Subnet(
|
||||||
"private-subnet",
|
"private-subnet",
|
||||||
vpc_id=vpc.id,
|
vpc_id=vpc.id,
|
||||||
@ -63,37 +57,36 @@ private_subnet2 = ec2.Subnet(
|
|||||||
)
|
)
|
||||||
repo = ecr.Repository("repo")
|
repo = ecr.Repository("repo")
|
||||||
|
|
||||||
# Create an IAM role
|
# ecs_task_execution_role = iam.Role("ecsExecutionRole",
|
||||||
ecs_task_execution_role = iam.Role("ecsExecutionRole",
|
# path="/",
|
||||||
path="/",
|
# assume_role_policy=iam.get_policy_document(statements=[{
|
||||||
assume_role_policy=iam.get_policy_document(statements=[{
|
# "actions": ["sts:AssumeRole"],
|
||||||
"actions": ["sts:AssumeRole"],
|
# "principals": {
|
||||||
"principals": {
|
# "type": "Service",
|
||||||
"type": "Service",
|
# "identifiers": ["ecs-tasks.amazonaws.com"]
|
||||||
"identifiers": ["ecs-tasks.amazonaws.com"]
|
# }
|
||||||
}
|
# }
|
||||||
}
|
# ]).json,
|
||||||
]).json,
|
# )
|
||||||
)
|
|
||||||
|
|
||||||
# Attach a policy to the execution role that will allow it to pull images from ECR
|
|
||||||
iam.RolePolicy("accessECRImages",
|
# iam.RolePolicy("accessECRImages",
|
||||||
role=ecs_task_execution_role.id,
|
# role=ecs_task_execution_role.id,
|
||||||
policy={
|
# policy={
|
||||||
"Version": "2012-10-17",
|
# "Version": "2012-10-17",
|
||||||
"Statement": [
|
# "Statement": [
|
||||||
{
|
# {
|
||||||
"Effect": "Allow",
|
# "Effect": "Allow",
|
||||||
"Action": [
|
# "Action": [
|
||||||
"ecr:GetDownloadUrlForLayer",
|
# "ecr:GetDownloadUrlForLayer",
|
||||||
"ecr:BatchGetImage",
|
# "ecr:BatchGetImage",
|
||||||
"ecr:BatchCheckLayerAvailability",
|
# "ecr:BatchCheckLayerAvailability",
|
||||||
],
|
# ],
|
||||||
"Resource": repo.repository_arn
|
# "Resource": repo.repository_arn
|
||||||
}
|
# }
|
||||||
]
|
# ]
|
||||||
}
|
# }
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
image_web = awsx.ecr.Image(
|
image_web = awsx.ecr.Image(
|
||||||
@ -118,7 +111,8 @@ security_group = aws.ec2.SecurityGroup(
|
|||||||
cidr_blocks=["0.0.0.0/0"],
|
cidr_blocks=["0.0.0.0/0"],
|
||||||
ipv6_cidr_blocks=["::/0"],
|
ipv6_cidr_blocks=["::/0"],
|
||||||
)])
|
)])
|
||||||
# Create an ECS Cluster
|
|
||||||
|
|
||||||
cluster = ecs.Cluster("cluster")
|
cluster = ecs.Cluster("cluster")
|
||||||
|
|
||||||
pulumi.export("certificateArn", cert.arn)
|
pulumi.export("certificateArn", cert.arn)
|
||||||
@ -129,38 +123,40 @@ loadbalancer = lb.LoadBalancer(
|
|||||||
subnets=[public_subnet.id, public_subnet2.id])
|
subnets=[public_subnet.id, public_subnet2.id])
|
||||||
|
|
||||||
|
|
||||||
target_group = lb.TargetGroup('target-group',
|
target_group = lb.TargetGroup(
|
||||||
|
"target-group",
|
||||||
port=5000,
|
port=5000,
|
||||||
protocol="HTTP",
|
protocol="HTTP",
|
||||||
target_type="ip",
|
target_type="ip",
|
||||||
vpc_id=vpc.id)
|
vpc_id=vpc.id)
|
||||||
|
|
||||||
# Create a listener for the ALB at port 80
|
listener = lb.Listener(
|
||||||
listener = lb.Listener('listener',
|
"listener",
|
||||||
load_balancer_arn=loadbalancer.arn,
|
load_balancer_arn=loadbalancer.arn,
|
||||||
port=80,
|
port=80,
|
||||||
default_actions=[{
|
default_actions=[{
|
||||||
'type': "forward",
|
"type": "forward",
|
||||||
'target_group_arn': target_group.arn # Forward to our target group
|
"target_group_arn": target_group.arn
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
web_sg = ec2.SecurityGroup('web-sg',
|
web_sg = ec2.SecurityGroup(
|
||||||
|
"web-sg",
|
||||||
vpc_id=vpc.id,
|
vpc_id=vpc.id,
|
||||||
ingress=[
|
ingress=[
|
||||||
{
|
{
|
||||||
'protocol': 'tcp',
|
"protocol": "tcp",
|
||||||
'from_port': 80,
|
"from_port": 80,
|
||||||
'to_port': 80,
|
"to_port": 80,
|
||||||
'cidr_blocks': ['0.0.0.0/0'],
|
"cidr_blocks": ["0.0.0.0/0"],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
egress=[
|
egress=[
|
||||||
{
|
{
|
||||||
'protocol': '-1',
|
"protocol": "-1",
|
||||||
'from_port': 0,
|
"from_port": 0,
|
||||||
'to_port': 0,
|
"to_port": 0,
|
||||||
'cidr_blocks': ['0.0.0.0/0'],
|
"cidr_blocks": ["0.0.0.0/0"],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -170,9 +166,9 @@ api = awsx.ecs.FargateService(
|
|||||||
"api",
|
"api",
|
||||||
cluster=cluster.arn,
|
cluster=cluster.arn,
|
||||||
network_configuration={
|
network_configuration={
|
||||||
'assignPublicIp': "false",
|
"assignPublicIp": "false",
|
||||||
'subnets': [private_subnet.id],
|
"subnets": [private_subnet.id],
|
||||||
'securityGroups': [web_sg.id]
|
"securityGroups": [web_sg.id]
|
||||||
},
|
},
|
||||||
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
|
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
|
||||||
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
|
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
|
||||||
@ -193,11 +189,10 @@ service = awsx.ecs.FargateService(
|
|||||||
"web",
|
"web",
|
||||||
cluster=cluster.arn,
|
cluster=cluster.arn,
|
||||||
network_configuration={
|
network_configuration={
|
||||||
'assignPublicIp': "true",
|
"assignPublicIp": "true",
|
||||||
'subnets': [public_subnet.id],
|
"subnets": [public_subnet.id],
|
||||||
'securityGroups': [web_sg.id]
|
"securityGroups": [web_sg.id]
|
||||||
},
|
},
|
||||||
# assign_public_ip=True,
|
|
||||||
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
|
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
|
||||||
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
|
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
|
||||||
name="theOne",
|
name="theOne",
|
||||||
|
Loading…
Reference in New Issue
Block a user