Skip to content

Instantly share code, notes, and snippets.

@ToddG
Last active October 12, 2022 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ToddG/0672cacf90a98e8dd9aa8a6a12ee0e67 to your computer and use it in GitHub Desktop.
Save ToddG/0672cacf90a98e8dd9aa8a6a12ee0e67 to your computer and use it in GitHub Desktop.
pulumi __main__.py for a generic app composed of a custom vpc + fargate + custom secgroup
"""An AWS Environment for the `dashboard` application."""
import pulumi
import pulumi_awsx as awsx
import pulumi_aws as aws
from pulumi_awsx.ec2 import DefaultVpc
"""
Define the common resources for an application stack (dev/stage/prod) including:
* S3 Bucket
* Batch Compute
* DynamoDb
* VPC
* Security Group
* Cluster
The microservices will use these common services and create their own:
* Docker Images
* ECR (elastic container registry)
The microservice CI/CD pipeline will deploy into the environment using
stack references for things like:
* "clusterArn"
* "customSecurityGroupId"
* "customVpcPrivateSubnetIds"
* "albDefaultTargetGroup"
"""
APP_NAME = "dashboard"
COMPANY = "foo" # TODO
# --------------------------------------------------------------------------------------------------------------
# config
# --------------------------------------------------------------------------------------------------------------
# see https://www.pulumi.com/docs/intro/concepts/config/
config = pulumi.Config()
canary_config = config.require_object("canary")
canary_memory = canary_config.get("memory")
canary_port = canary_config.get("port")
vpc_config = config.require_object("vpc")
number_of_availability_zones = vpc_config.get("number_of_availability_zones")
# --------------------------------------------------------------------------------------------------------------
# stack
# --------------------------------------------------------------------------------------------------------------
# see https://www.pulumi.com/docs/guides/organizing-projects-stacks/
# for standard deployment, stack should be one of ['dev', 'stage', 'prod'].
stack = pulumi.get_stack()
# --------------------------------------------------------------------------------------------------------------
# resource names
# --------------------------------------------------------------------------------------------------------------
PREFIX = f"{stack}-{APP_NAME}"
BATCH_COMPUTE_RESOURCE_NAME = f"{PREFIX}-batch-compute"
BUCKET_RESOURCE_NAME = f"{PREFIX}-{COMPANY}-co-bucket"
ALB_RESOURCE_NAME = f"{PREFIX}-alb"
PREFIX_CLUSTER_RESOURCE_NAME = f"{PREFIX}-cluster"
CANARY_RESOURCE_NAME = f"{PREFIX}-canary"
CANARY_ECR_RESOURCE_NAME = f"{CANARY_RESOURCE_NAME}-ecr"
CANARY_IMAGE_CONTAINER_NAME = f"{CANARY_RESOURCE_NAME}-container"
CANARY_IMAGE_RESOURCE_NAME = f"{CANARY_RESOURCE_NAME}-image"
CANARY_FARGATE_SERVICE_RESOURCE_NAME = f"{CANARY_RESOURCE_NAME}-fargate"
DYNAMO_EXAMPLE_TABLE_RESOURCE_NAME = f"{PREFIX}-example-dynamo-table"
CUSTOM_SECURITY_GROUP_RESOURCE_NAME = f"{PREFIX}-security-group"
# --------------------------------------------------------------------------------------------------------------
# s3
# --------------------------------------------------------------------------------------------------------------
# see: https://www.pulumi.com/registry/packages/aws/api-docs/s3/
bucket = aws.s3.Bucket(BUCKET_RESOURCE_NAME)
pulumi.export('s3BucketId', bucket.id)
# --------------------------------------------------------------------------------------------------------------
# batch compute
# --------------------------------------------------------------------------------------------------------------
# see:
# see: https://www.pulumi.com/registry/packages/aws/api-docs/batch/computeenvironment/
# see: https://www.pulumi.com/registry/packages/aws-native/api-docs/batch/
# TODO: implement this
# --------------------------------------------------------------------------------------------------------------
# dynamodb
# --------------------------------------------------------------------------------------------------------------
def create_table(short_name: str, resource_name: str) -> aws.dynamodb.Table:
_example_table = aws.dynamodb.Table(
resource_name,
hash_key="id",
attributes=[aws.dynamodb.TableAttributeArgs(
name="id",
type="S"
)],
read_capacity=1,
write_capacity=1)
pulumi.export(f"{short_name}DynamoDbTableResourceName", _example_table.name)
return _example_table
example_table = create_table("example", DYNAMO_EXAMPLE_TABLE_RESOURCE_NAME)
# --------------------------------------------------------------------------------------------------------------
# vpcs
# --------------------------------------------------------------------------------------------------------------
# exports [default|custom][VpcId|VpcPublicSubnetIds|VpcPrivateSubnetIds]
# see: https://www.middlewareinventory.com/blog/pulumi-aws-example/
# see: https://www.pulumi.com/docs/guides/crosswalk/aws/vpc/#
def get_default_vpc() -> DefaultVpc:
"""See https://www.pulumi.com/registry/packages/awsx/api-docs/ec2/defaultvpc/."""
vpc = awsx.ec2.DefaultVpc("default-vpc")
_export_vpc_info("default", vpc)
return vpc
def get_custom_vpc(prefix: str, number_of_availability_zones: int) -> awsx.ec2.Vpc:
"""See https://www.pulumi.com/registry/packages/awsx/api-docs/ec2/vpc/."""
# Note: dev only needs 1 availability zone, prod will need 2 or 3.
# Specify this in the dev/stage/prod yaml config files.
vpc = awsx.ec2.Vpc(f"{prefix}-vpc", number_of_availability_zones=number_of_availability_zones)
_export_vpc_info("custom", vpc)
return vpc
def _export_vpc_info(name: str, vpc: any) -> None:
pulumi.export(f"{name}VpcId", vpc.vpc_id)
pulumi.export(f"{name}VpcPublicSubnetIds", vpc.public_subnet_ids)
pulumi.export(f"{name}VpcPrivateSubnetIds", vpc.private_subnet_ids)
default_vpc = get_default_vpc()
custom_vpc = get_custom_vpc(PREFIX, number_of_availability_zones)
# --------------------------------------------------------------------------------------------------------------
# security group
# --------------------------------------------------------------------------------------------------------------
# TODO: research the needs of our security group(s)
custom_security_group = aws.ec2.SecurityGroup(
CUSTOM_SECURITY_GROUP_RESOURCE_NAME,
description="TODO: still shaping traffic",
vpc_id=custom_vpc.vpc_id,
ingress=[
aws.ec2.SecurityGroupIngressArgs(
description="allow HTTP access from anywhere",
from_port=80,
to_port=80,
protocol="tcp",
cidr_blocks=["0.0.0.0/0"],
),
aws.ec2.SecurityGroupIngressArgs(
description="allow HTTPS access from anywhere",
from_port=443,
to_port=443,
protocol="tcp",
cidr_blocks=["0.0.0.0/0"],
),
],
egress=[
aws.ec2.SecurityGroupEgressArgs(
description="allow all protocols egress to anywhere",
from_port=0,
to_port=0,
protocol="-1",
cidr_blocks=["0.0.0.0/0"],
),
]
)
pulumi.export("customSecurityGroupId", custom_security_group.id)
# --------------------------------------------------------------------------------------------------------------
# cluster
# --------------------------------------------------------------------------------------------------------------
# see: https://www.pulumi.com/registry/packages/aws/api-docs/ecs/cluster/
cluster = aws.ecs.Cluster(PREFIX_CLUSTER_RESOURCE_NAME)
pulumi.export("clusterName", cluster.name)
pulumi.export("clusterArn", cluster.arn)
# --------------------------------------------------------------------------------------------------------------
# lb
# --------------------------------------------------------------------------------------------------------------
# see: https://www.pulumi.com/registry/packages/awsx/api-docs/lb/
# TODO: replace lb with API GATEWAY ?
# https://www.pulumi.com/docs/guides/crosswalk/aws/api-gateway/
lb = awsx.lb.ApplicationLoadBalancer(
ALB_RESOURCE_NAME,
listeners=[
awsx.lb.ListenerArgs(
port=80,
protocol='http',
)])
pulumi.export("albDnsName", lb.load_balancer.dns_name)
pulumi.export("albDefaultTargetGroupArn", lb.default_target_group.arn)
pulumi.export("albDefaultTargetGroup", lb.default_target_group)
# --------------------------------------------------------------------------------------------------------------
# image
# --------------------------------------------------------------------------------------------------------------
# see: https://www.pulumi.com/registry/packages/awsx/api-docs/ecr/image/
# TODO: replace this dummy canary app image with a real canary...
# TODO: replace hard coded directory with a repo url or use git submodules...
# TODO: can we add containers to a fargate service after the fact?
tags = {
"project": "dashboard",
"component": "canary",
"stack": stack,
"type": "microservice",
}
# each microservice will create a separate ecr, one per service.
# no need to export this ecr, as it is not used elsewhere.
canary_repo = awsx.ecr.Repository(CANARY_ECR_RESOURCE_NAME, tags=tags)
image = awsx.ecr.Image(
CANARY_IMAGE_RESOURCE_NAME,
repository_url=canary_repo.url,
path="./canary")
pulumi.export("canaryImageUri", image.image_uri)
# --------------------------------------------------------------------------------------------------------------
# fargate
# --------------------------------------------------------------------------------------------------------------
# see: https://www.pulumi.com/registry/packages/awsx/api-docs/ecs/fargateservice/
canary_service = awsx.ecs.FargateService(
CANARY_FARGATE_SERVICE_RESOURCE_NAME,
cluster=cluster.arn,
network_configuration=aws.ecs.ServiceNetworkConfigurationArgs(
subnets=custom_vpc.private_subnet_ids,
security_groups=[custom_security_group.id]
),
task_definition_args=awsx.ecs.FargateServiceTaskDefinitionArgs(
containers={
CANARY_IMAGE_CONTAINER_NAME: awsx.ecs.TaskDefinitionContainerDefinitionArgs(
image=image.image_uri,
memory=canary_memory,
port_mappings=[awsx.ecs.TaskDefinitionPortMappingArgs(
container_port=canary_port,
target_group=lb.default_target_group,
)]
)
}
))
pulumi.export("canaryFargateServiceResourceName", CANARY_FARGATE_SERVICE_RESOURCE_NAME)
pulumi.export("canaryFargateServiceUrn", canary_service.urn)
# --------------------------------------------------------------------------------------------------------------
# runbook
# --------------------------------------------------------------------------------------------------------------
# see
with open('Pulumi.README.md') as f:
pulumi.export('readme', f.read())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment