{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "This is a sample CloudFormation template for deploying Dynamic DynamoDB version 2.x. This package will be installed from the Python Package Index repository. For more information, see http://dynamic-dynamodb.readthedocs.org.", "Parameters": { "S3Bucket": { "Type": "String", "Default": "s3://bucket-name/folder-path-if-applicable/", "Description": "Remove this default value and enter your pre-created S3 Bucket url upto folder name. (Please include the trailing /. Example: https://s3.amazonaws.com/my-bucket/ should be input as s3://my-bucket/). It will be used for storing a copy of Dynamic DynamoDB configuration file." }, "S3BucketRegion": { "Type": "String", "Default": "us-east-1", "Description": "Region the S3 bucket is located in. This is needed due to a limitation in the AWS CLI (https://github.com/aws/aws-cli/issues/564)" }, "KeyPair": { "Default": "key-pair-name", "Description": "Remove this default value and enter the name of your EC2 keypair to use for SSH access", "Type": "String", "MinLength": "1", "MaxLength": "64", "AllowedPattern": "[-_ a-zA-Z0-9\\.]*", "ConstraintDescription": "can contain only alphanumeric characters, spaces, dashes, underscores and dots." }, "InstanceType" : { "Default" : "t2.micro", "Description" : "AWS EC2 instance type", "Type" : "String" } }, "Mappings": { "RegionMap": { "us-east-1": { "AMI": "ami-fb8e9292", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "us-west-1": { "AMI": "ami-7aba833f", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "us-west-2": { "AMI": "ami-043a5034", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "eu-west-1": { "AMI": "ami-2918e35e", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "sa-east-1": { "AMI": "ami-215dff3c", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "ap-southeast-1": { "AMI": "ami-b40d5ee6", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "ap-southeast-2": { "AMI": "ami-3b4bd301", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "ap-northeast-1": { "AMI": "ami-c9562fc8", "IamRoleServiceHost": "ec2.amazonaws.com", "IamRolePolicySnsResource": "arn:aws:sns:*::dynamic-dynamodb" }, "cn-north-1": { "AMI": "ami-0637ff6b", "IamRoleServiceHost": "ec2.amazonaws.com.cn", "IamRolePolicySnsResource": "arn:aws-cn:sns:*::dynamic-dynamodb" } } }, "Resources": { "LaunchConfig": { "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { "Fn::FindInMap": [ "RegionMap", { "Ref": "AWS::Region" }, "AMI" ] }, "InstanceType": { "Ref" : "InstanceType" }, "KeyName": { "Ref": "KeyPair" }, "SecurityGroups": [ { "Ref": "SecurityGroup" } ], "IamInstanceProfile" : { "Ref" : "IamInstanceProfile" }, "UserData": { "Fn::Base64": { "Fn::Join": [ "\n", [ "#!/usr/bin/env bash", "# Helper function", "log(){", " timenow=`date +%Y-%m-%dT%H:%M:%S.%N`", " echo \"$timenow: $1\" >> $INSTALLER_LOG_FILE_LOCATION", "}", "", "backup_file_locally(){", " FILE=$1", " BACKUP_FILE_NAME=$FILE.`date +\"%Y.%m.%d.%H.%M.%S.%N\".backup`", " mv $FILE \"$FILE.`date +\"%Y.%m.%d.%H.%M.%S.%N\".backup`\"", " log \"Backed up $FILE to $BACKUP_FILE_NAME\"", "}", "error_exit(){", " log \"$1\"", { "Fn::Join": [ "", [ "/opt/aws/bin/cfn-signal ", " -e 1 ", " -r \"$1\"", " '", { "Ref" : "WaitHandle" }, "'\n" ] ] }, " exit 1", "}", "INSTALLER_LOG_FILE_LOCATION=/etc/dynamic-dynamodb/logs/installer.log", "mkdir -p /etc/dynamic-dynamodb/{scripts,logs} || error_exit 'Failed to create /etc/dynamic-dynamodb'", { "Fn::Join": [ " ", [ "/opt/aws/bin/cfn-init ", " --stack ", { "Ref": "AWS::StackId" }, " --resource LaunchConfig ", " --region ", { "Ref": "AWS::Region" }, " || error_exit 'Failed in AWS::CloudFormation::Init. Check logs at /var/log/cfn-init.log'" ] ] }, "", "if [[ -f $INSTALLER_LOG_FILE_LOCATION ]]; then", " backup_file_locally $INSTALLER_LOG_FILE_LOCATION", "fi", "easy_install pip || error_exit 'Failed to install pip'", "log \"Installed pip\"", "echo \"dynamic-dynamodb>=2.0.0,<3.0.0\">/etc/dynamic-dynamodb/requirements.txt", "/usr/local/bin/pip install -U -r /etc/dynamic-dynamodb/requirements.txt || error_exit 'Failed to install dynamic-dynamodb package from pip repository'", "log \"Installed dynamic-dynamodb\"", "", "", "mkdir -p ~/.aws || error_exit 'Failed to create /home/root/.aws'", "cp /home/ec2-user/.aws/config ~/.aws/config", { "Fn::Join": [ "", [ "echo \"aws s3 cp /etc/dynamic-dynamodb/dynamic-dynamodb.conf ", { "Ref": "S3Bucket" }, "dynamic-dynamodb.conf --region ", { "Ref" : "S3BucketRegion" }, " || (echo 'Failed to upload /etc/dynamic-dynamodb/dynamic-dynamodb.conf to ", { "Ref": "S3Bucket" }, "' ; exit 1)\">/etc/dynamic-dynamodb/scripts/upload-config-to-s3.sh", " || error_exit 'Failed to create /etc/dynamic-dynamodb/scripts/upload-config-to-s3.sh'" ]] }, { "Fn::Join": [ " ", [ "sh /etc/dynamic-dynamodb/scripts/init_config_file.sh /etc/dynamic-dynamodb/dynamic-dynamodb.conf", { "Ref": "S3Bucket" }, " $INSTALLER_LOG_FILE_LOCATION", " || error_exit 'Failed to initialize config file'" ]] }, "service dynamic-dynamodb start || error_exit 'Failed to start dynamic-dynamodb service. Check /etc/dynamic-dynamodb/logs/service.log'", "log \"Dynamic dynamodb service started\"", { "Fn::Join": [ "", [ "# All is well so signal success\n", "/opt/aws/bin/cfn-signal -e 0 -r \"Dynamic DynamoDB instance setup complete\" '", { "Ref": "WaitHandle" }, "'\n" ]] } ]] } } }, "Metadata": { "AWS::CloudFormation::Init": { "config": { "files": { "/home/ec2-user/.aws/config": { "content" : { "Fn::Join" : ["", [ "[default]\n", "region=", { "Ref": "AWS::Region" }, "\n" ]]}, "mode": "000755", "owner": "root", "group": "root" }, "/etc/init.d/dynamic-dynamodb": { "content": { "Fn::Join": [ "\n", [ "#!/usr/bin/env bash", "### BEGIN INIT INFO", "# Provides: dynamic-dynamodb", "# Required-Start: $remote_fs $syslog", "# Required-Stop: $remote_fs $syslog", "# Default-Start: 2 3 4 5", "# Default-Stop: 0 1 6", "# Short-Description: Automatic scaling for AWS DynamoDB", "# Description: Dynamic DynamoDB provides automatic scaling for AWS DynamoDB", "### END INIT INFO", "", "NAME=dynamicdynamodb", "DAEMON=/usr/local/bin/dynamic-dynamodb", "DRY_RUN=$2", "DAEMON_START_ARGS=\"--config /etc/dynamic-dynamodb/dynamic-dynamodb.conf --daemon start\"", "DAEMON_STOP_ARGS=\"--config /etc/dynamic-dynamodb/dynamic-dynamodb.conf --daemon stop\"", "SCRIPTNAME=/etc/init.d/$NAME", "SERVICE_LOG_FILE=/etc/dynamic-dynamodb/logs/service.log", "", "if [ \"$DRY_RUN\" == \"--dry-run\" ]; then", " DAEMON_START_ARGS=\"--config /etc/dynamic-dynamodb/dynamic-dynamodb.conf --dry-run --daemon start\"", " DAEMON_STOP_ARGS=\"--config /etc/dynamic-dynamodb/dynamic-dynamodb.conf --dry-run --daemon stop\"", "elif [ \"$2\" != \"\" ]; then", " echo \"$2\"", " echo \"Second parameter has to be --dry-run and is used only when running start/restart/force-reload commands\"", " exit 1", "fi", "", "# Exit if the package is not installed", "[ -x \"$DAEMON\" ] || exit 1", "", ". /etc/rc.d/init.d/functions", "", "log(){", " timenow=`date +%Y-%m-%dT%H:%M:%S.%N`", " echo \"$timenow: $1\"", " echo \"$timenow: $1\" >> $SERVICE_LOG_FILE", "}", "", "error_exit(){", " log \"$1\"", " exit 1", "}", "", "", "do_start()", "{", " if [ \"$DRY_RUN\" == \"--dry-run\" ]; then", " log \"do_start:Dry run mode, not uploading latest config file to S3\"", " else", " log \"do_start:Firstly, uploading latest config file to S3\"", " sh /etc/dynamic-dynamodb/scripts/upload-config-to-s3.sh || error_exit \"Failed in uploading config file to s3 bucket\"", " fi", " log \"do_start:Starting $NAME\"", " daemon $DAEMON $DAEMON_START_ARGS || error_exit \"Failed in starting $NAME service\"", "}", "", "do_stop()", "{", " log \"do_stop:Stopping $NAME\"", " daemon $DAEMON $DAEMON_STOP_ARGS || error_exit \"Failed in stopping $NAME service\"", "}", "", "", "case \"$1\" in", " start)", " do_start", " ;;", " stop)", " do_stop", " ;;", " status)", " status \"$DAEMON\" \"$NAME\" && exit 0 || exit $?", " ;;", " restart|force-reload)", " do_stop", " do_start", " #*)", " ;;", " *)", " echo \"Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}\" >&2", " exit 3", " ;;", "esac", "", ":" ] ] }, "mode": "000755", "owner": "root", "group": "root" }, "/etc/dynamic-dynamodb/scripts/init_config_file.sh": { "content": { "Fn::Join": [ "", [ "#!/usr/bin/env bash\n", "CONFIG_FILE_LOCAL_LOCATION=$1\n", "S3_BUCKET=$2\n", "INSTALLER_LOG_FILE_LOCATION=$3\n", "CONFIG_FILE_S3_LOCATION=\"$S3_BUCKET\"dynamic-dynamodb.conf\n", "DOES_CONFIG_FILE_EXIST_ON_S3=0\n", "DOES_CONFIG_FILE_EXIST_ON_LOCALLY=0\n\n", "log(){\n", " timenow=`date +%Y-%m-%dT%H:%M:%S.%N`\n", " echo \"$timenow: $1\" >> $INSTALLER_LOG_FILE_LOCATION\n", "}\n\n", "error_exit(){\n", " log \"$1\"\n", " exit 1\n", "}\n\n", "backup_file_locally(){\n", " FILE=$1\n", " BACKUP_FILE_NAME=$FILE.`date +\\\"%Y.%m.%d.%H.%M.%S.%N\\\".backup`\n", " mv $FILE \"$FILE.`date +\\\"%Y.%m.%d.%H.%M.%S.%N\\\".backup`\" || error_exit \"Failed in creating backup file $BACKUP_FILE_NAME\"\n", " log \"Backed up $FILE to $BACKUP_FILE_NAME\"\n", "}\n\n", "generate_new_config_file(){\n", " log \"Loading an example config file to $CONFIG_FILE_LOCAL_LOCATION\"\n", " cp /etc/dynamic-dynamodb/example-dynamic-dynamodb.conf $CONFIG_FILE_LOCAL_LOCATION\n", " sed -i 's/region:\\ us-east-1/region:\\ ", { "Ref" : "AWS::Region" },"/g' /etc/dynamic-dynamodb/example-dynamic-dynamodb.conf\n", " log \"Loaded example configuration to $CONFIG_FILE_LOCAL_LOCATION\"\n", "}\n\n", "download_from_s3(){\n", " aws s3 cp $CONFIG_FILE_S3_LOCATION $CONFIG_FILE_LOCAL_LOCATION --region ", { "Ref" : "S3BucketRegion" } ," || error_exit \"Failed to download config file from $CONFIG_FILE_S3_LOCATION to $CONFIG_FILE_S3_LOCATION even though it existed on S3\"\n", " log \"Downloaded config file from $CONFIG_FILE_S3_LOCATION to $CONFIG_FILE_LOCAL_LOCATION\"\n", "}\n\n", "aws s3 cp $CONFIG_FILE_S3_LOCATION . --region ", { "Ref" : "S3BucketRegion" } ," >/dev/null 2>&1\n", "if [[ \"$?\" -eq 0 ]]; then\n", " DOES_CONFIG_FILE_EXIST_ON_S3=1\n", "fi\n\n", "if [[ -f $CONFIG_FILE_LOCAL_LOCATION ]]; then\n", " DOES_CONFIG_FILE_EXIST_ON_LOCALLY=1\n", "fi\n\n", "if [ $DOES_CONFIG_FILE_EXIST_ON_S3 -eq 0 ] && [ $DOES_CONFIG_FILE_EXIST_ON_LOCALLY -eq 0 ]; then\n", " generate_new_config_file\n", "elif [ $DOES_CONFIG_FILE_EXIST_ON_S3 -eq 0 ] && [ $DOES_CONFIG_FILE_EXIST_ON_LOCALLY -eq 1 ]; then\n", " log \"Config file exists locally and not on $CONFIG_FILE_S3_LOCATION, continuing to use the file\";\n", "elif [ $DOES_CONFIG_FILE_EXIST_ON_S3 -eq 1 ] && [ $DOES_CONFIG_FILE_EXIST_ON_LOCALLY -eq 0 ]; then\n", " download_from_s3\n", "elif [ $DOES_CONFIG_FILE_EXIST_ON_S3 -eq 1 ] && [ $DOES_CONFIG_FILE_EXIST_ON_LOCALLY -eq 1 ]; then\n", " backup_file_locally $CONFIG_FILE_LOCAL_LOCATION\n", " download_from_s3\n", "fi\n" ] ] }, "mode": "000755", "owner": "root", "group": "root" }, "/etc/dynamic-dynamodb/example-dynamic-dynamodb.conf": { "source": "https://raw.github.com/sebdah/dynamic-dynamodb/master/example-dynamic-dynamodb.conf", "mode": "000644", "owner": "root", "group": "root" } } } } } }, "AutoScalingGroup": { "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "AvailabilityZones": { "Fn::GetAZs": "" }, "Cooldown": 300, "LaunchConfigurationName": { "Ref": "LaunchConfig" }, "MaxSize": 1, "MinSize": 1, "Tags" : [ { "Key" : "Name", "Value" : "dynamic-dynamodb", "PropagateAtLaunch" : "true" } ] } }, "IamRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version" : "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ { "Fn::FindInMap": [ "RegionMap", { "Ref": "AWS::Region" }, "IamRoleServiceHost" ] } ] }, "Action": [ "sts:AssumeRole" ] }] }, "Path": "/", "Policies": [ { "PolicyName": "root", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:DescribeTable", "dynamodb:ListTables", "dynamodb:UpdateTable", "cloudwatch:GetMetricStatistics", "s3:PutObject", "s3:GetObject" ], "Resource": ["*"] }, { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": [ { "Fn::FindInMap": [ "RegionMap", { "Ref": "AWS::Region" }, "IamRolePolicySnsResource" ] } ] } ] } }] } }, "IamInstanceProfile": { "Type": "AWS::IAM::InstanceProfile", "Properties": { "Path": "/", "Roles": [ { "Ref": "IamRole" } ] } }, "SecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Allow access to MyInstance", "SecurityGroupIngress": [ { "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "CidrIp": "0.0.0.0/0" } ] } }, "WaitHandle" : { "Type": "AWS::CloudFormation::WaitConditionHandle" }, "WaitCondition" : { "Type": "AWS::CloudFormation::WaitCondition", "DependsOn": "AutoScalingGroup", "Properties": { "Handle": { "Ref" : "WaitHandle" }, "Timeout": "600" } } }, "Outputs": { "URL": { "Description": "EC2 instance", "Value": "You can find your EC2 instance at https://console.aws.amazon.com/ec2/v2/home?#Instances:search=dynamic-dynamodb" } } }