Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
286 lines (266 sloc) 9.98 KB
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Docker'
Parameters:
ParentVPCStack:
Description: 'Stack name of parent VPC stack based on vpc/vpc-*azs.yaml template.'
Type: String
ParentSSHBastionStack:
Description: 'Optional Stack name of parent SSH bastion host/instance stack based on vpc/vpc-ssh-bastion.yaml template.'
Type: String
Default: ''
KeyName:
Description: 'Optional key pair of the ec2-user to establish a SSH connection to the EC2 instance.'
Type: String
Default: ''
IAMUserSSHAccess:
Description: 'Synchronize public keys of IAM users to enable personalized SSH access (Doc: https://cloudonaut.io/manage-aws-ec2-ssh-access-with-iam/).'
Type: String
Default: false
AllowedValues:
- true
- false
InstanceType:
Description: 'The instance type for the EC2 instance.'
Type: String
Default: 't2.micro'
DesiredInstances:
Description: 'The number of EC2 instances'
Type: Number
Default: 1
SubnetsReach:
Description: 'Should the instances have direct access to the Internet or do you prefer private subnets with NAT?'
Type: String
Default: Public
AllowedValues:
- Public
- Private
DockerVersion:
Description: 'Specifies the version of the Docker engine'
Type: String
Default: "1.13.0"
DockerPreRelease:
Description: 'Specifies if an experimental version of Docker Engine should be used'
Type: String
Default: false
AllowedValues:
- true
- false
Conditions:
HasKeyName: !Not [!Equals [!Ref KeyName, '']]
HasIAMUserSSHAccess: !Equals [!Ref IAMUserSSHAccess, 'true']
HasSSHBastionSecurityGroup: !Not [!Equals [!Ref ParentSSHBastionStack, '']]
UsePreRelease: !Equals [!Ref DockerPreRelease, 'true']
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub ${AWS::StackName}
VpcId:
'Fn::ImportValue': !Sub '${ParentVPCStack}-VPC'
SecurityGroupInSSHBastion:
Type: 'AWS::EC2::SecurityGroupIngress'
Condition: HasSSHBastionSecurityGroup
Properties:
GroupId: !Ref SecurityGroup
IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId:
'Fn::ImportValue': !Sub '${ParentSSHBastionStack}-SecurityGroup'
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: '/'
Roles:
- !Ref IAMRole
IAMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- 'ec2.amazonaws.com'
Action:
- 'sts:AssumeRole'
Path: '/'
Policies:
- PolicyName: asg
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'autoscaling:DescribeAutoScalingGroups'
- 'autoscaling:DescribeAutoScalingInstances'
- 'ec2:DescribeInstances'
Resource:
- '*'
IAMPolicySSHAccess:
Type: 'AWS::IAM::Policy'
Condition: HasIAMUserSSHAccess
Properties:
Roles:
- !Ref IAMRole
PolicyName: iam
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'iam:ListUsers'
Resource:
- '*'
- Effect: Allow
Action:
- 'iam:ListSSHPublicKeys'
- 'iam:GetSSHPublicKey'
Resource:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/*'
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
# AvailabilityZones: !Ref AvailabilityZones
VPCZoneIdentifier:
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetA${SubnetsReach}'
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetB${SubnetsReach}'
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetC${SubnetsReach}'
LaunchConfigurationName: !Ref LaunchConfiguration
MinSize: !Ref DesiredInstances
MaxSize: 100
DesiredCapacity: !Ref DesiredInstances
MetricsCollection:
- Granularity: 1Minute
Metrics:
- GroupInServiceInstances
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}
PropagateAtLaunch: 'true'
CreationPolicy:
ResourceSignal:
Timeout: PT05M
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: !Ref DesiredInstances
MaxBatchSize: '1'
PauseTime: PT10M
SuspendProcesses:
- AlarmNotification
WaitOnResourceSignals: 'true'
LaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Metadata:
AWS::CloudFormation::Init:
configSets:
default:
!If [HasIAMUserSSHAccess, [ssh-access, docker], [docker]]
ssh-access:
files:
'/opt/authorized_keys_command.sh':
content: |
#!/bin/bash -e
if [ -z "$1" ]; then
exit 1
fi
SaveUserName="$1"
SaveUserName=${SaveUserName//"+"/".plus."}
SaveUserName=${SaveUserName//"="/".equal."}
SaveUserName=${SaveUserName//","/".comma."}
SaveUserName=${SaveUserName//"@"/".at."}
aws iam list-ssh-public-keys --user-name "$SaveUserName" --query "SSHPublicKeys[?Status == 'Active'].[SSHPublicKeyId]" --output text | while read KeyId; do
aws iam get-ssh-public-key --user-name "$SaveUserName" --ssh-public-key-id "$KeyId" --encoding SSH --query "SSHPublicKey.SSHPublicKeyBody" --output text
done
mode: '000755'
owner: root
group: root
'/opt/import_users.sh':
content: |
#!/bin/bash
aws iam list-users --query "Users[].[UserName]" --output text | while read User; do
SaveUserName="$User"
SaveUserName=${SaveUserName//"+"/".plus."}
SaveUserName=${SaveUserName//"="/".equal."}
SaveUserName=${SaveUserName//","/".comma."}
SaveUserName=${SaveUserName//"@"/".at."}
if id -u "$SaveUserName" >/dev/null 2>&1; then
echo "$SaveUserName exists"
else
#sudo will read each file in /etc/sudoers.d, skipping file names that end in ‘~’ or contain a ‘.’ character to avoid causing problems with package manager or editor temporary/backup files.
SaveUserFileName=$(echo "$SaveUserName" | tr "." " ")
/usr/sbin/adduser "$SaveUserName"
echo "$SaveUserName ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$SaveUserFileName"
fi
done
mode: '000755'
owner: root
group: root
'/etc/cron.d/import_users':
content: |
*/10 * * * * root /opt/import_users.sh
mode: '000644'
owner: root
group: root
commands:
'a_configure_sshd_command':
command: 'sed -i "s:#AuthorizedKeysCommand none:AuthorizedKeysCommand /opt/authorized_keys_command.sh:g" /etc/ssh/sshd_config'
'b_configure_sshd_commanduser':
command: 'sed -i "s:#AuthorizedKeysCommandUser nobody:AuthorizedKeysCommandUser nobody:g" /etc/ssh/sshd_config'
'c_import_users':
command: './import_users.sh'
cwd: '/opt'
services:
sysvinit:
sshd:
enabled: true
ensureRunning: true
commands:
- 'a_configure_sshd_command'
- 'b_configure_sshd_commanduser'
docker:
commands:
'a_get_certificates':
command: 'sudo apt-get install -y apt-transport-https ca-certificates'
'b_set_gpg_key':
command: 'sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D'
'c_add_docker_repo':
command: !If [UsePreRelease, 'echo "deb https://apt.dockerproject.org/repo ubuntu-xenial testing" | sudo tee /etc/apt/sources.list.d/docker.list', 'echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | sudo tee /etc/apt/sources.list.d/docker.list']
'd_update_aptget':
command: 'sudo apt-get update'
'e_install_docker':
command: !Sub 'sudo apt-get install -y docker-engine=${DockerVersion}-0~ubuntu-xenial'
'f_create_service':
command: 'sudo service docker start'
'g_add_ubuntu_user_to_docker_group':
command: 'sudo usermod -aG docker ubuntu'
'h_verify_installation':
command: 'docker run hello-world'
# Optional
# 'i_run_your_container':
# command: 'docker run -d -p 80:80 --name nginx nginx'
Properties:
ImageId: ami-6f587e1c # Ubuntu 16.04
InstanceType: !Ref InstanceType
SecurityGroups:
- !Ref SecurityGroup
IamInstanceProfile: !Ref InstanceProfile
KeyName: !If [HasKeyName, !Ref KeyName, !Ref 'AWS::NoValue']
BlockDeviceMappings:
- DeviceName: "/dev/xvdcz"
Ebs:
VolumeSize: '22'
UserData:
"Fn::Base64": !Sub |
#!/bin/bash -xe
sudo apt-get update
sudo apt-get -y upgrade
# Install AWSCli
sudo apt install -y awscli
# Install cfn-init for Ubuntu
apt-get -y install python-setuptools
easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
ln -s /root/aws-cfn-bootstrap-latest/init/ubuntu/cfn-hup /etc/init.d/cfn-hup
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource LaunchConfiguration
cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup