Last active
October 12, 2022 20:33
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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