Created
April 16, 2018 15:53
-
-
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.
This file contains hidden or 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
# | |
# 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