Cfoo (pronounced "sifu") lets you write your CloudFormation templates in YAML, and makes it easier with some helpers.
Cfoo can be installed as a Ruby Gem
$ gem install cfoo
-
Write your CloudFormation templates using Cfoo YAML
-
Turn your Cfoo templates into normal CloudFormation templates
$ cfoo web-server.template.yml database.template.yml > web-stack.template.json
- Create your stack with CloudFormation
$ cfn-create-stack --stack-name WebStack -f web-stack.template.json
Snippet from a CloudFormation template (based on this example):
"Properties": {
"ImageId" : { "Fn::FindInMap" : [ "AWSRegion2AMI", { "Ref" : "AWS::Region" }, "AMI" ] },
"InstanceType" : { "Ref" : "InstanceType" },
"SecurityGroups" : [ {"Ref" : "FrontendGroup"} ],
"KeyName" : { "Ref" : "KeyName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -v\n",
"yum update -y aws-cfn-bootstrap\n",
"function error_exit\n",
"{\n",
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "WaitHandle" }, "'\n",
" exit 1\n",
"}\n",
"/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServer ",
" --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",
"/opt/aws/bin/cfn-signal -e 0 -r \"cfn-init complete\" '", { "Ref" : "WaitHandle" }, "'\n"
]]}}
}
Equivalent Cfoo template snippet:
Properties:
ImageId : AWSRegion2AMI[$(AWS::Region)][AMI]
InstanceType: $(InstanceType)
SecurityGroups:
- $(FrontendGroup)
KeyName: $(KeyName)
UserData: !Base64 |
#!/bin/bash -v
yum update -y aws-cfn-bootstrap
function error_exit
{
/opt/aws/bin/cfn-signal -e 1 -r "\$1" '$(WaitHandle)'
exit 1
}
/opt/aws/bin/cfn-init -s $(AWS::StackId) -r WebServer --region $(AWS::Region) || error_exit 'Failed to run cfn-init'
/opt/aws/bin/cfn-signal -e 0 -r "cfn-init completed" '$(WaitHandle)'
Using Cfoo, it is possible to split your templates up into logical components that will combined to form your CloudFormation template.
First, create a directory in your project directory called modules
. For each module,
create some Cfoo templates defining the different parts of your app. Your project
structure will look like this:
my-web-app
└── modules
├── application
│ ├── app_servers.yml
│ ├── database.yml
│ └── public_load_balancer.yml
├── instances
│ └── instance_mappings.yml
└── network
├── bastion.yml
├── cfn_user.yml
├── dns.yml
├── nat.yml
├── private_subnet.yml
├── public_subnet.yml
└── vpc.yml
Use Cfoo to generate your project's CloudFormation template
$ cfoo > web-app.template.json
Cfoo allows you to simplify CloudFormation intrinsic function references using its own shorthand
CloudFormation: { "Ref" : "InstanceType" }
Cfoo Shortcut: $(InstanceType)
CloudFormation: { "FindInMap" : [ "SubnetConfig", "VPC", "CIDR" ] }
Cfoo Shortcut: $(SubnetConfig[VPC][CIDR])
CloudFormation: { "Fn::GetAtt" : [ "Ec2Instance", "PublicIp" ] }
Cfoo Shortcut: $(Ec2Instance[PublicIp])
CloudFormation: { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]}
Cfoo Shortcut: $(HostedZone).
Cfoo gives you the option of using YAML custom data-types where it helps to make your templates easier to read.
CloudFormation:
{ "Ref" : "InstanceType" }
YAML Type:
!Ref InstanceType
CloudFormation:
{ "FindInMap" : [ "SubnetConfig", "VPC", "CIDR" ] }
YAML Type:
!FindInMap [ SubnetConfig, VPC, CIDR ]
CloudFormation:
{ "Fn::GetAtt" : [ "Ec2Instance", "PublicIp" ] }
YAML Type:
!GetAtt [ Ec2Instance, PublicIp ]
CloudFormation:
{ "Fn::Base64" : "#!/bin/bash\necho 'Running script...'" }
YAML Type:
!Base64 "#!/bin/bash\necho 'running script...'"
Alternative YAML Type:
!Base64 |
#!/bin/bash
echo 'running script...'
CloudFormation:
{ "Fn::Equals": [ "sg-mysggroup", {"Ref": "ASecurityGroup"} ] },
YAML Type:
!Equals [ "sg-mysggroup", $(ASecurityGroup) ]
Cfoo aims to let developers simplify CloudFormation templates by:
- allowing them to write templates in YAML
- providing an expression language to simplify CF Ref/Attr/etc expressions
- allowing templates to be split up into logical components (to simplify and share)
Cfoo also aims (subject to Primary Goals) to:
- allow inclusion existing JSON templates (so you don't have to switch all at once)
Cfoo does not (yet) aim to:
- provide commandline utilities for interacting directly with CloudFormation (it just generates the templates for now)
- resolve/validate references (the CloudFormation API already does this)
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Make your changes (with tests please)
- Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request