Skip to content

Instantly share code, notes, and snippets.

@y13i
Last active January 26, 2021 21:16
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 y13i/aa443d19d278aa00896c7d302aefd2b3 to your computer and use it in GitHub Desktop.
Save y13i/aa443d19d278aa00896c7d302aefd2b3 to your computer and use it in GitHub Desktop.
Example: AutoMuteUs on AWS

automuteus-aws

Deploys AutoMuteUs to ECS/Fargate/EFS/Route53 as cheap as possible.

Instruction

  1. Put secrets to SSM Parameter Store.
$ aws ssm put-parameter \
  --name "/automuteus/discordBotToken" \
  --type "SecureString" \
  --value "blahblah"

# same for "/automuteus/postgresPassword"
  1. Create CFn stack.
$ aws cloudformation deploy \
  --stack-name "automuteus" \
  --template-file "template.cfn.json" \
  --capabilities "CAPABILITY_IAM" \
  --role-arn "arn:aws:iam::111111111111:role/cfn" \
  --parameter-overrides DomainName=your-domain.com HostedZoneId=YOURDOMAINSHOSTEDZONEID BotPrefix=.yourprefix Enabled=true
{
"Parameters": {
"VpcCidr": {
"Type": "String",
"Description": "VPC network ranges.",
"Default": "10.0.0.0/16"
},
"BotPrefix": {
"Type": "String",
"Default": ".au"
},
"DomainName": {
"Type": "String"
},
"HostedZoneId": {
"Type": "AWS::Route53::HostedZone::Id"
},
"Enabled": {
"Type": "String",
"AllowedValues": ["true", "false"]
}
},
"Conditions": {
"EnabledCondition": {
"Fn::Equals": [{ "Ref": "Enabled" }, "true"]
}
},
"Resources": {
"Vpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"EnableDnsSupport": true,
"EnableDnsHostnames": true,
"CidrBlock": { "Ref": "VpcCidr" },
"Tags": [{ "Key": "Name", "Value": { "Ref": "AWS::StackName" } }]
}
},
"SecurityGroupIngressTCP8123": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": {
"GroupId": { "Fn::GetAtt": ["Vpc", "DefaultSecurityGroup"] },
"IpProtocol": "tcp",
"CidrIp": "0.0.0.0/0",
"FromPort": 8123,
"ToPort": 8123
}
},
"InternetGateway": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {
"Tags": [{ "Key": "Name", "Value": { "Ref": "AWS::StackName" } }]
}
},
"InternetGatewayAttachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": { "Ref": "Vpc" },
"InternetGatewayId": { "Ref": "InternetGateway" }
}
},
"PublicRouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": { "Ref": "Vpc" },
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Sub": "${AWS::StackName}-public" }
}
]
}
},
"PublicToInternetRoute": {
"Type": "AWS::EC2::Route",
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": { "Ref": "PublicRouteTable" },
"GatewayId": { "Ref": "InternetGateway" }
}
},
"PublicSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": {
"Fn::Select": [0, { "Fn::Cidr": [{ "Ref": "VpcCidr" }, 3, 8] }]
},
"MapPublicIpOnLaunch": true,
"AvailabilityZone": {
"Fn::Select": [0, { "Fn::GetAZs": { "Ref": "AWS::Region" } }]
},
"VpcId": { "Ref": "Vpc" },
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": ["-", [{ "Ref": "AWS::StackName" }, "public"]]
}
}
]
}
},
"PublicSubnetRouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": { "Ref": "PublicRouteTable" },
"SubnetId": { "Ref": "PublicSubnet" }
}
},
"Cluster": {
"Type": "AWS::ECS::Cluster",
"Properties": {
"CapacityProviders": ["FARGATE_SPOT"]
}
},
"LogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": { "RetentionInDays": 365 }
},
"FileSystem": {
"DeletionPolicy": "Retain",
"Type": "AWS::EFS::FileSystem",
"Properties": {
"PerformanceMode": "generalPurpose",
"FileSystemTags": [
{
"Key": "Name",
"Value": { "Ref": "AWS::StackName" }
}
]
}
},
"MountTarget": {
"Type": "AWS::EFS::MountTarget",
"Properties": {
"FileSystemId": { "Ref": "FileSystem" },
"SubnetId": { "Ref": "PublicSubnet" },
"SecurityGroups": [{ "Fn::GetAtt": ["Vpc", "DefaultSecurityGroup"] }]
}
},
"TaskExecutionRolePolicy": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ssm:GetParameters"],
"Resource": {
"Fn::Sub": "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*"
}
}
]
}
}
},
"TaskExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": {
"Fn::Sub": "ecs-tasks.${AWS::URLSuffix}"
}
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
{ "Ref": "TaskExecutionRolePolicy" }
]
}
},
"TaskRolePolicy": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["route53:ChangeResourceRecordSets"],
"Resource": [
{ "Fn::Sub": "arn:aws:route53:::hostedzone/${HostedZoneId}" }
]
}
]
}
}
},
"TaskRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [{ "Ref": "TaskRolePolicy" }]
}
},
"TaskDefinition": {
"Type": "AWS::ECS::TaskDefinition",
"Properties": {
"Family": { "Ref": "AWS::StackName" },
"RequiresCompatibilities": ["FARGATE"],
"NetworkMode": "awsvpc",
"Cpu": "256",
"Memory": "1024",
"ExecutionRoleArn": { "Fn::GetAtt": ["TaskExecutionRole", "Arn"] },
"TaskRoleArn": { "Fn::GetAtt": ["TaskRole", "Arn"] },
"ContainerDefinitions": [
{
"Name": "automuteus",
"DependsOn": [
{ "ContainerName": "galactus", "Condition": "START" },
{ "ContainerName": "redis", "Condition": "START" },
{ "ContainerName": "postgres", "Condition": "START" }
],
"Image": "denverquane/amongusdiscord",
"PortMappings": [
{
"ContainerPort": 5000,
"Protocol": "tcp"
}
],
"Environment": [
{
"Name": "HOST",
"Value": {
"Fn::Sub": "http://${AWS::StackName}-${AWS::Region}.${DomainName}:8123"
}
},
{
"Name": "POSTGRES_USER",
"Value": { "Ref": "AWS::StackName" }
},
{
"Name": "REDIS_ADDR",
"Value": "localhost:6379"
},
{
"Name": "GALACTUS_ADDR",
"Value": "http://localhost:5858"
},
{
"Name": "POSTGRES_ADDR",
"Value": "localhost:5432"
},
{
"Name": "AUTOMUTEUS_GLOBAL_PREFIX",
"Value": { "Ref": "BotPrefix" }
},
{
"Name": "DISABLE_LOG_FILE",
"Value": "true"
}
],
"Secrets": [
{
"Name": "DISCORD_BOT_TOKEN",
"ValueFrom": {
"Fn::Sub": "/${AWS::StackName}/discordBotToken"
}
},
{
"Name": "POSTGRES_PASS",
"ValueFrom": {
"Fn::Sub": "/${AWS::StackName}/postgresPassword"
}
}
],
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": { "Ref": "LogGroup" },
"awslogs-region": { "Ref": "AWS::Region" },
"awslogs-stream-prefix": "fargate"
}
}
},
{
"Name": "galactus",
"DependsOn": [
{ "ContainerName": "redis", "Condition": "START" },
{ "ContainerName": "upsert-rrset", "Condition": "SUCCESS" }
],
"Image": "automuteus/galactus",
"PortMappings": [
{
"ContainerPort": 8123,
"Protocol": "tcp"
}
],
"Environment": [
{
"Name": "BROKER_PORT",
"Value": "8123"
},
{
"Name": "REDIS_ADDR",
"Value": "localhost:6379"
},
{
"Name": "GALACTUS_PORT",
"Value": "5858"
}
],
"Secrets": [
{
"Name": "DISCORD_BOT_TOKEN",
"ValueFrom": {
"Fn::Sub": "/${AWS::StackName}/discordBotToken"
}
}
],
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": { "Ref": "LogGroup" },
"awslogs-region": { "Ref": "AWS::Region" },
"awslogs-stream-prefix": "fargate"
}
}
},
{
"Name": "redis",
"Image": "redis:alpine",
"PortMappings": [
{
"ContainerPort": 6379,
"Protocol": "tcp"
}
],
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": { "Ref": "LogGroup" },
"awslogs-region": { "Ref": "AWS::Region" },
"awslogs-stream-prefix": "fargate"
}
}
},
{
"Name": "postgres",
"Image": "postgres:12-alpine",
"PortMappings": [
{
"ContainerPort": 5432,
"Protocol": "tcp"
}
],
"Environment": [
{
"Name": "POSTGRES_USER",
"Value": { "Ref": "AWS::StackName" }
}
],
"Secrets": [
{
"Name": "POSTGRES_PASSWORD",
"ValueFrom": {
"Fn::Sub": "/${AWS::StackName}/postgresPassword"
}
}
],
"MountPoints": [
{
"ContainerPath": "/var/lib/postgresql/data",
"SourceVolume": "postgres-data"
}
],
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": { "Ref": "LogGroup" },
"awslogs-region": { "Ref": "AWS::Region" },
"awslogs-stream-prefix": "fargate"
}
}
},
{
"Name": "upsert-rrset",
"Image": "python",
"Essential": false,
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": { "Ref": "LogGroup" },
"awslogs-region": { "Ref": "AWS::Region" },
"awslogs-stream-prefix": "fargate"
}
},
"Command": [
"bash",
"-xec",
{
"Fn::Sub": "pip install awscli && aws --version && export PUBLIC_IP=$(curl -s checkip.amazonaws.com) && aws route53 change-resource-record-sets --hosted-zone-id ${HostedZoneId} --change-batch \"{\\\"Comment\\\": \\\"${AWS::StackName}\\\", \\\"Changes\\\": [{\\\"Action\\\": \\\"UPSERT\\\", \\\"ResourceRecordSet\\\": {\\\"Name\\\": \\\"${AWS::StackName}-${AWS::Region}.${DomainName}\\\", \\\"Type\\\": \\\"A\\\", \\\"TTL\\\": 60, \\\"ResourceRecords\\\": [{\\\"Value\\\": \\\"$PUBLIC_IP\\\"}]}}]}\""
}
]
}
],
"Volumes": [
{
"Name": "postgres-data",
"EFSVolumeConfiguration": {
"FilesystemId": { "Ref": "FileSystem" },
"TransitEncryption": "ENABLED"
}
}
]
}
},
"Service": {
"Condition": "EnabledCondition",
"Type": "AWS::ECS::Service",
"Properties": {
"Cluster": { "Ref": "Cluster" },
"DesiredCount": 1,
"TaskDefinition": { "Ref": "TaskDefinition" },
"PlatformVersion": "1.4.0",
"CapacityProviderStrategy": [
{
"CapacityProvider": "FARGATE_SPOT",
"Weight": 1
}
],
"NetworkConfiguration": {
"AwsvpcConfiguration": {
"AssignPublicIp": "ENABLED",
"SecurityGroups": [
{ "Fn::GetAtt": ["Vpc", "DefaultSecurityGroup"] }
],
"Subnets": [{ "Ref": "PublicSubnet" }]
}
},
"DeploymentConfiguration": {
"MinimumHealthyPercent": 0
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment