This is nearly working in all Pulumi.

Permissions are not correct in code to allow the images
to be pulled by the ECS tasks. As such, they will not start
and run without some manual intervention.

Pulumi will happily let the process sit at this stage
forever.
This commit is contained in:
Alexander Hosking 2023-09-07 00:49:03 -04:00
commit cf369c6a3a
5 changed files with 228 additions and 0 deletions

2
air-tech/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc
venv/

View File

@ -0,0 +1,2 @@
config:
aws:region: us-east-2

6
air-tech/Pulumi.yaml Normal file
View File

@ -0,0 +1,6 @@
name: air-tech
runtime:
name: python
options:
virtualenv: venv
description: A technical assessment

216
air-tech/__main__.py Normal file
View File

@ -0,0 +1,216 @@
import pulumi
from pulumi import Config, Output, export
import pulumi_aws as aws
from pulumi_aws import acm, alb, ecs, ec2, lb, iam, ecr
import pulumi_awsx as awsx
import json
config = Config()
# container_port = config.get_int("containerPort", 80)
cpu = config.get_int("cpu", 512)
memory = config.get_int("memory", 128)
# Create a VPC
vpc = ec2.Vpc("production", cidr_block="10.0.0.0/16")
# Create a Gateway
ig = ec2.InternetGateway("internet-gateway", vpc_id=vpc.id)
cert = acm.Certificate(
"air-tech",
domain_name="air-tech.ahosking.com",
validation_method="DNS",
)
# Public Subnet
public_subnet = ec2.Subnet(
"public-subnet",
vpc_id=vpc.id,
cidr_block="10.0.1.0/26",
availability_zone="us-east-2a",
# Allow the Internet Gateway to handle public traffic
map_public_ip_on_launch=True
)
public_subnet2 = ec2.Subnet(
"public-subnet2",
vpc_id=vpc.id,
cidr_block="10.0.2.0/26",
availability_zone="us-east-2b",
# Allow the Internet Gateway to handle public traffic
map_public_ip_on_launch=True
)
# Associate the Gateway to a route
route_table = ec2.RouteTable('route-table', vpc_id=vpc.id)
route = ec2.Route('route', route_table_id=route_table.id,
destination_cidr_block='0.0.0.0/0', gateway_id=ig.id)
# Private Subnet
private_subnet = ec2.Subnet(
"private-subnet",
vpc_id=vpc.id,
cidr_block="10.0.3.0/26",
availability_zone="us-east-2a",
map_public_ip_on_launch=False
)
private_subnet2 = ec2.Subnet(
"private-subnet2",
vpc_id=vpc.id,
cidr_block="10.0.4.0/26",
availability_zone="us-east-2b",
map_public_ip_on_launch=False
)
repo = ecr.Repository("repo")
# Create an IAM role
ecs_task_execution_role = iam.Role("ecsExecutionRole",
path="/",
assume_role_policy=iam.get_policy_document(statements=[{
"actions": ["sts:AssumeRole"],
"principals": {
"type": "Service",
"identifiers": ["ecs-tasks.amazonaws.com"]
}
}
]).json,
)
# Attach a policy to the execution role that will allow it to pull images from ECR
iam.RolePolicy("accessECRImages",
role=ecs_task_execution_role.id,
policy={
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
],
"Resource": repo.repository_arn
}
]
}
)
image_web = awsx.ecr.Image(
"image_web",
repository_url=repo.url,
path=".",
dockerfile=".\infra-web\Dockerfile")
image_api = awsx.ecr.Image(
"image_api",
repository_url=repo.url,
path=".",
dockerfile=".\infra-api\Dockerfile")
security_group = aws.ec2.SecurityGroup(
"securityGroup",
vpc_id=vpc.id,
egress=[aws.ec2.SecurityGroupEgressArgs(
from_port=0,
to_port=0,
protocol="-1",
cidr_blocks=["0.0.0.0/0"],
ipv6_cidr_blocks=["::/0"],
)])
# Create an ECS Cluster
cluster = ecs.Cluster("cluster")
pulumi.export("certificateArn", cert.arn)
loadbalancer = lb.LoadBalancer(
"loadbalancer",
load_balancer_type="application",
security_groups=[security_group.id],
subnets=[public_subnet.id, public_subnet2.id])
target_group = lb.TargetGroup('target-group',
port=5000,
protocol="HTTP",
target_type="ip",
vpc_id=vpc.id)
# Create a listener for the ALB at port 80
listener = lb.Listener('listener',
load_balancer_arn=loadbalancer.arn,
port=80,
default_actions=[{
'type': "forward",
'target_group_arn': target_group.arn # Forward to our target group
}])
web_sg = ec2.SecurityGroup('web-sg',
vpc_id=vpc.id,
ingress=[
{
'protocol': 'tcp',
'from_port': 80,
'to_port': 80,
'cidr_blocks': ['0.0.0.0/0'],
}
],
egress=[
{
'protocol': '-1',
'from_port': 0,
'to_port': 0,
'cidr_blocks': ['0.0.0.0/0'],
}
]
)
api = awsx.ecs.FargateService(
"api",
cluster=cluster.arn,
network_configuration={
'assignPublicIp': "false",
'subnets': [private_subnet.id],
'securityGroups': [web_sg.id]
},
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
name="theApi",
image=image_api.image_uri,
cpu=cpu,
memory=memory,
essential=True,
port_mappings=[awsx.ecs.TaskDefinitionPortMappingArgs(
container_port=5000,
host_port=5000,
)],
),
))
service = awsx.ecs.FargateService(
"web",
cluster=cluster.arn,
network_configuration={
'assignPublicIp': "true",
'subnets': [public_subnet.id],
'securityGroups': [web_sg.id]
},
# assign_public_ip=True,
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
container=awsx.ecs.TaskDefinitionContainerDefinitionArgs(
name="theOne",
image=image_web.image_uri,
cpu=cpu,
memory=memory,
essential=True,
port_mappings=[awsx.ecs.TaskDefinitionPortMappingArgs(
container_port=5000,
host_port=5000,
target_group=target_group,
)],
),
),
opts=pulumi.ResourceOptions(depends_on=[api, target_group])
)

View File

@ -0,0 +1,2 @@
pulumi>=3.0.0,<4.0.0
pulumi-aws>=6.0.2,<7.0.0