AWS::CloudFormation::Init
Use the AWS::CloudFormation::Init
type to include metadata on an Amazon EC2
instance for the cfn-init helper script. If your template calls the cfn-init script, the
script looks for resource metadata rooted in the AWS::CloudFormation::Init
metadata key. For more information about cfn-init, see cfn-init.
cfn-init supports all metadata types for Linux systems. It supports metadata types for Windows with conditions that are described in the sections that follow.
For an example of using AWS::CloudFormation::Init
and the cfn-init helper
script to create a Linux stack, see Deploy applications on Amazon EC2. For a Windows stack example, see Bootstrapping AWS CloudFormation Windows stacks.
Syntax
The configuration is separated into sections. The following template snippet shows how you can attach metadata for cfn-init to an EC2 instance resource within the template.
The metadata is organized into config keys, which you can group into configsets. You can
specify a configset when you call cfn-init
in your template. If you don't specify a
configset, cfn-init
looks for a single config key named config.
Note
The cfn-init
helper script processes these configuration sections in the following
order: packages, groups, users, sources, files, commands, and then services. If you
require a different order, separate your sections into different config keys, and then
use a configset that specifies the order in which the config keys should be
processed.
JSON
"Resources": { "MyInstance": { "Type": "AWS::EC2::Instance", "Metadata" : { "AWS::CloudFormation::Init" : { "config" : { "packages" : { : }, "groups" : { : }, "users" : { : }, "sources" : { : }, "files" : { : }, "commands" : { : }, "services" : { : } } } }, "Properties": { : } } }
YAML
Resources: MyInstance: Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Init: config: packages: : groups: : users: : sources: : files: : commands: : services: : Properties: :
Note
The following sections contain examples for scripts written in Unix-like shell scripting languages, such as Bash. To create scripts for PowerShell instead, make sure you're familiar with the PowerShell language. PowerShell syntax is different from Unix-like shells, so you'll need to have familiarity with the PowerShell way of doing things.
Configsets
If you want to create more than one config key and to have cfn-init
process them in a
specific order, create a configset that contains the config keys in the desired
order.
Single configset
The following template snippet creates configsets named ascending
and
descending
that each contain two config keys.
JSON
"AWS::CloudFormation::Init" : { "configSets" : { "ascending" : [ "config1" , "config2" ], "descending" : [ "config2" , "config1" ] }, "config1" : { "commands" : { "test" : { "command" : "echo \"$CFNTEST\" > test.txt", "env" : { "CFNTEST" : "I come from config1." }, "cwd" : "~" } } }, "config2" : { "commands" : { "test" : { "command" : "echo \"$CFNTEST\" > test.txt", "env" : { "CFNTEST" : "I come from config2" }, "cwd" : "~" } } } }
YAML
AWS::CloudFormation::Init: configSets: ascending: - "config1" - "config2" descending: - "config2" - "config1" config1: commands: test: command: "echo \"$CFNTEST\" > test.txt" env: CFNTEST: "I come from config1." cwd: "~" config2: commands: test: command: "echo \"$CFNTEST\" > test.txt" env: CFNTEST: "I come from config2" cwd: "~"
Related cfn-init
calls
The following example calls to cfn-init
refer to the preceding
example configsets. The example calls are abbreviated for clarity. See cfn-init for the complete syntax.
-
If a call to
cfn-init
specifies theascending
configset:cfn-init -c ascending
The script processes
config1
and then processesconfig2
and thetest.txt
file would contain the textI come from config2
. -
If a call to
cfn-init
specifies thedescending
configset:cfn-init -c descending
The script processes
config2
and then processesconfig1
and thetest.txt
file would contain the textI come from config1
.
Multiple configsets
You can create multiple configsets, and call a series of them using your cfn-init
script. Each configset can contain a list of config keys or references to other
configsets. For example, the following template snippet creates three configsets. The
first configset, test1
, contains one config key named 1
. The
second configset, test2
, contains a reference to the test1
configset and one config key named 2
. The third configset, default,
contains a reference to the configset test2
.
JSON
"AWS::CloudFormation::Init" : { "configSets" : { "test1" : [ "1" ], "test2" : [ { "ConfigSet" : "test1" }, "2" ], "default" : [ { "ConfigSet" : "test2" } ] }, "1" : { "commands" : { "test" : { "command" : "echo \"$MAGIC\" > test.txt", "env" : { "MAGIC" : "I come from the environment!" }, "cwd" : "~" } } }, "2" : { "commands" : { "test" : { "command" : "echo \"$MAGIC\" >> test.txt", "env" : { "MAGIC" : "I am test 2!" }, "cwd" : "~" } } } }
YAML
AWS::CloudFormation::Init: 1: commands: test: command: "echo \"$MAGIC\" > test.txt" env: MAGIC: "I come from the environment!" cwd: "~" 2: commands: test: command: "echo \"$MAGIC\" >> test.txt" env: MAGIC: "I am test 2!" cwd: "~" configSets: test1: - "1" test2: - ConfigSet: "test1" - "2" default: - ConfigSet: "test2"
Related cfn-init
calls
The following calls to cfn-init
refer to the configSets declared in
the preceding template snippet. The example calls are abbreviated for clarity. See
cfn-init for the complete
syntax.
-
If you specify
test1
only:cfn-init -c test1
cfn-init
processes config key1
only. -
If you specify
test2
only:cfn-init -c test2
cfn-init
processes config key1
and then processes config key2
. -
If you specify the
default
configset (or no configsets at all):cfn-init -c default
You'll get the same behavior that you would if you specify configset
test2
.
Commands
You can use the commands key to run commands on the EC2 instance. The commands are processed in alphabetical order by name.
Key | Required | Description |
---|---|---|
command |
Required |
Either an array or a string specifying the command to run. If you use an array, you don't need to escape space characters or enclose command parameters in quotes. Don't use the array to specify multiple commands. |
env |
Optional |
Sets environment variables for the command. This property overwrites, rather than appends, the existing environment. |
cwd |
Optional |
The working directory. |
test |
Optional |
A test command that determines whether For Linux, the test command must return an exit code of |
ignoreErrors |
Optional |
A Boolean value that determines whether |
waitAfterCompletion |
Optional |
For Windows systems only. Specifies how long to wait (in seconds) after a
command has finished in case the command causes a reboot. The default value
is 60 seconds and a value of "forever" directs |
Example
The following example snippet calls the echo command if the ~/test.txt
file doesn't exist.
JSON
"commands" : { "test" : { "command" : "echo \"$MAGIC\" > test.txt", "env" : { "MAGIC" : "I come from the environment!" }, "cwd" : "~", "test" : "test ! -e ~/test.txt", "ignoreErrors" : "false" }, "test2" : { "command" : "echo \"$MAGIC2\" > test2.txt", "env" : { "MAGIC2" : "I come from the environment!" }, "cwd" : "~", "test" : "test ! -e ~/test2.txt", "ignoreErrors" : "false" } }
YAML
commands: test: command: "echo \"$MAGIC\" > test.txt" env: MAGIC: "I come from the environment!" cwd: "~" test: "test ! -e ~/test.txt" ignoreErrors: "false" test2: command: "echo \"$MAGIC2\" > test2.txt" env: MAGIC2: "I come from the environment!" cwd: "~" test: "test ! -e ~/test2.txt" ignoreErrors: "false"
Files
You can use the files
key to create files on the EC2 instance. The content
can be either inline in the template or the content can be pulled from a URL. The files are
written to disk in lexicographic order. The following table lists the supported
keys.
Key | Description |
---|---|
content |
Either a string or a properly formatted JSON object. If you use a JSON
object as your content, the JSON will be written to a file on disk. Any
intrinsic functions such as NoteIf you create a symlink, the helper script modifies the permissions of the target file. Currently, you can't create a symlink without modifying the permissions of the target file. |
source |
A URL to load the file from. This option can't be specified with the content key. |
encoding |
The encoding format. Only used if the content is a string. Encoding isn't applied if you are using a source. Valid values: |
group |
The name of the owning group for this file. Not supported for Windows systems. |
owner |
The name of the owning user for this file. Not supported for Windows systems. |
mode |
A six-digit octal value representing the mode for this file. Not
supported for Windows systems. Use the first three digits for symlinks and
the last three digits for setting permissions. To create a symlink, specify
|
authentication |
The name of an authentication method to use. This overrides any default authentication. You can use this property to select an authentication method you define with the AWS::CloudFormation::Authentication resource. |
context |
Specifies a context for files that are to be processed as Mustache
templates |
Examples
The following example snippet creates a file named setup.mysql
as part of a larger
installation.
JSON
"files" : { "/tmp/setup.mysql" : { "content" : { "Fn::Join" : ["", [ "CREATE DATABASE ", { "Ref" : "DBName" }, ";\n", "CREATE USER '", { "Ref" : "DBUsername" }, "'@'localhost' IDENTIFIED BY '", { "Ref" : "DBPassword" }, "';\n", "GRANT ALL ON ", { "Ref" : "DBName" }, ".* TO '", { "Ref" : "DBUsername" }, "'@'localhost';\n", "FLUSH PRIVILEGES;\n" ]]}, "mode" : "000644", "owner" : "root", "group" : "root" } }
YAML
files: /tmp/setup.mysql: content: !Sub | CREATE DATABASE ${DBName}; CREATE USER '${DBUsername}'@'localhost' IDENTIFIED BY '${DBPassword}'; GRANT ALL ON ${DBName}.* TO '${DBUsername}'@'localhost'; FLUSH PRIVILEGES; mode: "000644" owner: "root" group: "root"
The full template is available at: https://s3.amazonaws.com/cloudformation-templates-us-east-1/Drupal_Single_Instance.template
The following example snippet creates a symlink /tmp/myfile2.txt
that
points at an existing file /tmp/myfile1.txt
. The permissions of the target
file /tmp/myfile1.txt
is defined by the mode value 644
.
JSON
"files" : { "/tmp/myfile2.txt" : { "content" : "/tmp/myfile1.txt", "mode" : "120644" } }
YAML
files: /tmp/myfile2.txt: content: "/tmp/myfile1.txt" mode: "120644"
Mustache templates are used primarily to create configuration files. For example, you
can store a configuration file in an S3 bucket and interpolate Refs and GetAtts from the
template, instead of using Fn::Join. The following example snippet
outputs Content for test9
to /tmp/test9.txt
.
JSON
"files" : { "/tmp/test9.txt" : { "content" : "Content for {{name}}", "context" : { "name" : "test9" } } }
YAML
files: /tmp/test9.txt: content: "Content for {{name}}" context: name: "test9"
When working with Mustache templates, note the following:
-
The context key must be present for the files to be processed.
-
The context key must be a key-value map, but it can be nested.
-
You can process files with inline content by using the content key and remote files by using the source key.
-
Mustache support depends on the pystache version. Version 0.5.2 supports the Mustache 1.1.2 specification
.
Groups
You can use the groups key to create Linux/UNIX groups and to assign group IDs. The groups key isn't supported for Windows systems.
To create a group, add a new key-value pair that maps a new group name to an optional group ID. The groups key can contain one or more group names. The following table lists the available keys.
Key | Description |
---|---|
|
A group ID number. If a group ID is specified, and the group already exists by name, the group creation will fail. If another group has the specified group ID, the OS may reject the group creation. Example: |
Example snippet
The following snippet specifies a group named groupOne
without assigning
a group ID and a group named groupTwo
that specified a group ID value of
45
.
JSON
"groups" : { "groupOne" : {}, "groupTwo" : { "gid" : "45" } }
YAML
groups: groupOne: {} groupTwo: gid: "45"
Packages
You can use the packages key to download and install pre-packaged applications and components. On Windows systems, the packages key supports only the MSI installer.
Supported package formats
The cfn-init
script currently supports the following package formats: apt, msi,
python, rpm, rubygems, yum, and Zypper. Packages are processed in the following order:
rpm, yum/apt/zypper, and then rubygems and python. There is no ordering between rubygems
and python, and packages within each package manager aren't guaranteed to be installed
in any order.
Specifying versions
Within each package manager, each package is specified as a package name and a list of versions. The version can be a string, a list of versions, or an empty string or list. An empty string or list indicates that you want the latest version. For rpm manager, the version is specified as a path to a file on disk or a URL.
If you specify a version of a package, cfn-init
will attempt to install that version
even if a newer version of the package is already installed on the instance. Some
package managers support multiple versions, but others may not. Verify the documentation
for your package manager for more information. If you don't specify a version and a
version of the package is already installed, the cfn-init
script won't install a new
version— it will assume that you want to keep and use the existing version.
Example snippets
RPM, yum, Rubygems, and Zypper
The following snippet specifies a version URL for rpm, requests the latest versions from yum and Zypper, and version 0.10.2 of chef from rubygems:
JSON
"rpm" : { "epel" : "http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm" }, "yum" : { "httpd" : [], "php" : [], "wordpress" : [] }, "rubygems" : { "chef" : [ "0.10.2" ] }, "zypper" : { "git" : [] }
YAML
rpm: epel: "http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm" yum: httpd: [] php: [] wordpress: [] rubygems: chef: - "0.10.2" zypper: git: []
MSI package
The following snippet specifies a URL for an MSI package:
JSON
"msi" : { "awscli" : "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi" }
YAML
msi: awscli: "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi"
Services
You can use the services key to define which services should be enabled or disabled when the instance is launched. On Linux systems, this key is supported by using sysvinit or systemd. On Windows systems, it's supported by using the Windows service manager.
The services key also allows you to specify dependencies on sources, packages, and files
so that if a restart is needed due to files being installed, cfn-init
will take care of the
service restart. For example, if you download the Apache HTTP Server package, the package
installation will automatically start the Apache HTTP Server during the stack creation
process. However, if the Apache HTTP Server configuration is updated later in the stack
creation process, the update won't take effect unless the Apache server is restarted. You
can use the services key to ensure that the Apache HTTP service is restarted.
The following table lists the supported keys.
Key | Description |
---|---|
ensureRunning |
Set to true to ensure that the service is running after Set to false to ensure that the service isn't running after Omit this key to make no changes to the service state. |
enabled |
Set to true to ensure that the service will be started automatically upon boot. Set to false to ensure that the service won't be started automatically upon boot. Omit this key to make no changes to this property. |
files |
A list of files. If |
sources |
A list of directories. If |
packages |
A map of package manager to list of package names. If |
commands |
A list of command names. If |
Examples
Linux
The following Linux snippet configures the services as follows:
-
The nginx service will be restarted if either
/etc/nginx/nginx.conf
or/var/www/html
are modified bycfn-init
. -
The php-fastcgi service will be restarted if
cfn-init
installs or updates php or spawn-fcgi using yum. -
The sendmail service will be stopped and disabled using systemd.
JSON
"services" : { "sysvinit" : { "nginx" : { "enabled" : "true", "ensureRunning" : "true", "files" : ["/etc/nginx/nginx.conf"], "sources" : ["/var/www/html"] }, "php-fastcgi" : { "enabled" : "true", "ensureRunning" : "true", "packages" : { "yum" : ["php", "spawn-fcgi"] } } }, "systemd": { "sendmail" : { "enabled" : "false", "ensureRunning" : "false" } } }
YAML
services: sysvinit: nginx: enabled: "true" ensureRunning: "true" files: - "/etc/nginx/nginx.conf" sources: - "/var/www/html" php-fastcgi: enabled: "true" ensureRunning: "true" packages: yum: - "php" - "spawn-fcgi" systemd: sendmail: enabled: "false" ensureRunning: "false"
To use systemd with a service, the service must have a systemd unit file configured. The following unit file allows systemd to start and stop the cfn-hup daemon in the multi-user service target:
[Unit] Description=cfn-hup daemon [Service] ExecStart=/usr/bin/cfn-hup -v PIDFile=/var/run/cfn-hup.pid [Install] WantedBy=multi-user.target
This configuration assumes that cfn-hup is installed under the /usr/bin directory. However, the actual location where cfn-hup is installed might vary on different platforms. You can override this configuration by creating an override file in /etc/systemd/system/cfn-hup.service.d/override.conf as follows:
# In this example, cfn-hup executable is available under /usr/local/bin [Service] ExecStart= ExecStart=/usr/local/bin/cfn-hup -v
Windows
The following Windows snippet starts the cfn-hup service, sets it to automatic, and restarts the service
if cfn-init
modifies the specified configuration files:
JSON
"services" : { "windows" : { "cfn-hup" : { "enabled" : "true", "ensureRunning" : "true", "files" : ["c:\\cfn\\cfn-hup.conf", "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf"] } } }
YAML
services: windows: cfn-hup: enabled: "true" ensureRunning: "true" files: - "c:\\cfn\\cfn-hup.conf" - "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf"
Sources
You can use the sources key to download an archive file and unpack it in a target directory on the EC2 instance. This key is fully supported for both Linux and Windows systems.
Supported formats
Supported formats are:
-
tar
-
tar+gzip
-
tar+bz2
-
zip
Examples
GitHub
If you use GitHub as a source control system, you can use cfn-init
and the sources
package mechanism to pull a specific version of your application. GitHub allows you
to create a .zip or a .tar from a specific version through a URL as follows:
https://github.com/<your directory>/(zipball|tarball)/<version>
For example, the following snippet pulls down version main as
a .tar
file.
JSON
"sources" : { "/etc/puppet" : "https://github.com/user1/cfn-demo/tarball/main" }
YAML
sources: /etc/puppet: "https://github.com/user1/cfn-demo/tarball/main"
S3 Bucket
The following example downloads a tarball from an S3 bucket and unpacks it into
/etc/myapp
:
Note
You can use authentication credentials for a source. However, you can't put an
authentication key in the sources block. Instead, include a buckets key in your
S3AccessCreds
block. For more information on Amazon S3 authentication
credentials, see AWS::CloudFormation::Authentication.
For an example, see the https://s3.amazonaws.com/cloudformation-templates-us-east-1/S3Bucket_SourceAuth.template
JSON
"sources" : { "/etc/myapp" : "https://s3.amazonaws.com/
amzn-s3-demo-bucket
/myapp.tar.gz" }
YAML
sources: /etc/myapp: "https://s3.amazonaws.com/
amzn-s3-demo-bucket
/myapp.tar.gz"
Users
You can use the users key to create Linux/UNIX users on the EC2 instance. The users key isn't supported for Windows systems.
The following table lists the supported keys.
Key | Description |
---|---|
uid |
A user ID. The creation process fails if the user name exists with a different user ID. If the user ID is already assigned to an existing user the operating system may reject the creation request. |
groups |
A list of group names. The user will be added to each group in the list. |
homeDir |
The user's home directory. |
Example
Users are created as non-interactive system users with a shell of
/sbin/nologin
. This is by design and can't be modified.
JSON
"users" : { "myUser" : { "groups" : ["groupOne", "groupTwo"], "uid" : "50", "homeDir" : "/tmp" } }
YAML
users: myUser: groups: - "groupOne" - "groupTwo" uid: "50" homeDir: "/tmp"