Skip to content

Instantly share code, notes, and snippets.

@sunds
Created April 16, 2018 15:53
Show Gist options
  • Save sunds/4ca59203cf2c4e8e9f951053d94cc052 to your computer and use it in GitHub Desktop.
Save sunds/4ca59203cf2c4e8e9f951053d94cc052 to your computer and use it in GitHub Desktop.
AWS Fargate with System Manager Parameters for secrets. Running a Node.js container.
#
# KMS template used to create SSM keys for all environments.
# From: https://typicalrunt.me/2017/04/07/storing-secrets-with-aws-parameterstore/
#
#
#
AWSTemplateFormatVersion: "2010-09-09"
Description: "KMS key for secrets management (see Parameters for more info)"
Parameters:
ENV:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- prod
Resources:
KmsKeyAlias:
Type: "AWS::KMS::Alias"
Properties:
AliasName: !Sub "alias/${ENV}"
TargetKeyId: !Ref KmsKey
KmsKey:
Type: "AWS::KMS::Key"
Properties:
Description: !Sub "Manages secrets in the ${ENV} namespace"
Enabled: true
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Id: "KeyPolicyForKMS"
Statement:
- Sid: "Enable IAM User Permissions"
Effect: "Allow"
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: "kms:*"
Resource: "*"
Outputs:
KmsKeyId:
Description: "ID of the KMS key"
Value: !Ref KmsKey
Export:
Name: !Sub "kms:key:${ENV}:id"
KmsKeyArn:
Description: "ARN of the KMS key"
Value: !GetAtt KmsKey.Arn
Export:
Name: !Sub "kms:key:${ENV}:arn"
#
# CloudFormation template for Node.js service on AWS Fargate.
#
#
# Example:
# aws cloudformation deploy
# --stack-name dev-service
# --template-file cloudformation/service.yaml
# --parameter-overrides ENV=dev Vpc=vpc-XXX Subnets=subnet-XXX,subnet-XXX,subnet-XXX
# --capabilities CAPABILITY_IAM
#
Description: >
My Service
Parameters:
ENV:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- prod
Vpc:
Type: AWS::EC2::VPC::Id
Subnets:
Type: List<AWS::EC2::Subnet::Id>
Repository:
Type: String
Default: my-service
InstanceCount:
Type: Number
Default: 1
HealthCheckPath:
Type: String
Default: /api/v1/liveness # your service will need a healthcheck endpoint
Mappings:
CertMap:
Certificate:
"dev": arn:aws:acm:us-east-1:XXX:certificate/XXX-XXX-XXX-XXX-XXXX
"staging": arn:aws:acm:us-east-1:XXX:certificate/XXX-XXX-XXX-XXX-XXXX
"prod": arn:aws:acm:us-east-1:XXX:certificate/XXX-XXX-XXX-XXX-XXXX
Environment:
NodeEnv:
"dev": "development"
"staging": "test"
"prod": "production"
Resources:
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub ${ENV}-service
TaskIamRole:
Type: AWS::IAM::Role
Properties:
Path: !Sub /service/${ENV}/
AssumeRolePolicyDocument: |
{
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]},
"Action": [ "sts:AssumeRole" ]
}]
}
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
Policies:
- PolicyName: "secrets-management"
PolicyDocument:
Version: "2012-10-17"
Id: "AllowAccessToParameters"
Statement:
- Sid: "AllowAccessToGetParameters"
Effect: "Allow"
Action:
- "ssm:GetParameters"
- "ssm:GetParametersByPath"
Resource:
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/service/${ENV}/*"
- Sid: "AllowAccessToDecryptParameters"
Effect: "Allow"
Action: "kms:Decrypt"
Resource:
- Fn::ImportValue:
!Sub "kms:key:${ENV}:arn"
LoadBalancerSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: !Sub ${ENV}-service
SecurityGroupIngress:
- CidrIp: "0.0.0.0/0"
IpProtocol: "TCP"
FromPort: 443
ToPort: 443
VpcId: !Ref Vpc
ServiceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub ${ENV}-service
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3000 # node.js running on port 3000 in this example
ToPort: 3000
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${ENV}-service
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${ENV}-service
Subnets: !Ref Subnets
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${ENV}-service
VpcId: !Ref Vpc
TargetType: ip
Port: 3000
Protocol: HTTP
Matcher:
HttpCode: 200-299
HealthCheckIntervalSeconds: 60
HealthCheckPath: !Ref HealthCheckPath
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 30
Tags:
- Key: Name
Value: !Sub ${ENV}-service
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Certificates:
- CertificateArn: !FindInMap [CertMap, "Certificate", !Ref ENV]
Port: 443
Protocol: HTTPS
DefaultActions:
- Type: forward
TargetGroupArn:
Ref: TargetGroup
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
Conditions:
- Field: path-pattern
Values:
- "*"
ListenerArn: !Ref LoadBalancerListener
Priority: 1
Service:
Type: AWS::ECS::Service
DependsOn:
- LoadBalancerListener
Properties:
ServiceName: !Sub ${ENV}-service
Cluster: !Ref Cluster
DesiredCount: !Ref InstanceCount
LaunchType: FARGATE
TaskDefinition: !Ref TaskDefinition
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 50
LoadBalancers:
- ContainerName: !Sub ${ENV}-service
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED ## fixme Required for non-Nat'd deployments?
SecurityGroups:
- !GetAtt ServiceSecurityGroup.GroupId
Subnets: !Ref Subnets
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !GetAtt TaskIamRole.Arn
TaskRoleArn: !GetAtt TaskIamRole.Arn
ContainerDefinitions:
- Name: !Sub ${ENV}-service
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}:${ENV}-latest
Essential: true
Memory: 512
PortMappings:
- ContainerPort: 3000
Environment:
- Name: ENV
Value: !Sub ${ENV}
- Name: NODE_ENV
Value: !FindInMap [Environment, "NodeEnv", !Ref ENV]
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: us-east-1
awslogs-stream-prefix: !Sub ${ENV}-service
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${ENV}-service
RetentionInDays: 30
Outputs:
Cluster:
Value: !Ref Cluster
#!/bin/sh
#
# Get all system parameters from AWS SSM and store into the current environment
#
# Run node process
#
echo "*************************"
echo " Env: ${ENV:-dev}"
echo "*************************"
json=$(aws ssm get-parameters-by-path --path "/${SERVICE:-service}/${ENV:-dev}/" --recursive --with-decryption)
params=$(echo $json | jq -r '.Parameters[] | [.Name, .Value] | join("=")')
for param in $params
do
export ${param##/${SERVICE:-service}/${ENV:-dev}/}
done
export -p
node server.js
#
# Dockerfile
#
FROM node:9
WORKDIR /app
COPY package.json /app
RUN npm install
RUN apt-get update --yes
RUN apt-get install jq --yes --quiet
RUN apt-get install python-pip python-dev build-essential --yes --quiet
RUN pip install awscli --upgrade
COPY . /app
CMD /app/boot.sh
EXPOSE 3000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment