Skip to content

Instantly share code, notes, and snippets.

@bellkev
Last active December 29, 2015 09:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellkev/7653342 to your computer and use it in GitHub Desktop.
Save bellkev/7653342 to your computer and use it in GitHub Desktop.
A couple of proposed syntax options for a clojure-based way to define AWS CloudFormation templates
; Step one: Example of specifying the template map quite manually
(def parameters
{"MyParameter"
{"Type" "String"
"Description" "My string parameter"}})
(def mappings
{"MyMapping"
{"FirstLevelKeyOne"
{"SecondLevelKeyOne" "Value"}
"FirstLevelKeyTwo"
{"SecondLevelKeyTwo" "Another Value"}}})
(def conditions
{"MyCondition"
{"Fn::Not" [{"Ref" "MyParameter"}]}
"MySecondCondition"
{"Fn::Equals" [{"Fn::FindInMap" ["MyMapping" "FirstLevelKeyOne" "SecondLevelKeyOne"]}
{"Ref" "MyParameter"}]}})
(def resources
{"MyInstance"
{"Type" "AWS::EC2::Instance"
"Properties"
{"ImageId" "ami-79fd7eee"}}})
(def outputs
{"MyFirstOutput"
{"Fn::GetAtt" ["MyInstance" "InstanceType"]}
"MySecondOutput"
{"Ref" "MyParameter"}})
(def my-template {:version "some-version"
:description "Description"
:parameters parameters
:mappings mappings
:conditions conditions
:resources resources
:outputs outputs})
; Step two: Use some more record constructors and convenience functions. (Could just use
; maps instead of records, but records might be nice for more validation using something
; like prismatic/schema)
(def my-parameter (map->Parameter {:type :string
:description "My Description"}))
(def my-mapping (map->Mapping {:first-level-key-one {:second-level-key-one "Value"}
:first-level-key-two {:second-level-key-two "Another Value"}}))
(def my-condition (cfn-not (cfn-ref my-parameter)))
(def my-second-condition (cfn= (cfn-inmap my-mapping :first-level-key-one :second-level-key-two)
(cfn-ref my-parameter)))
(def my-instance (aws.ec2/map->Instance {:properties {:image-id "ami-79fd7eee"}}))
(def my-first-output (cfn-get my-instance :instance-type))
(def my-second-output (cfn-ref my-parameter))
(def my-template (map->Template {:version "some-version"
:description "Description"
:parameters (parameters my-parameter)
:mappings (mappings my-mapping)
:conditions (conditions
my-condition
my-second-condition)
:resources (resources my-instance)
:outputs (outputs
my-first-output
my-second-output)}))
; Step three: Maybe wrap everything in one wrapper macro, that delegates to the functions in (2),
; using data instead of call semantics to make implementing/understanding the macro easier
; Unfortunately, I feel like this gets hard to read/write with its abundance of keywords...
(deftemplate my-template
:version "some-version"
:description "Description"
:parameters [:my-parameter {:type :string
:description "My Description"}]
:mappings [:my-mapping {:first-level-key-one {:second-level-key-one "Value"}
:first-level-key-two {:second-level-key-two "Another Value"}}]
:conditions [:my-condition [:cfn-not [:cfn-ref :my-parameter]]
:my-second-condition [:cfn=
[:cfn-inmap :my-mapping :first-level-key-one :second-level-key-two]
[:cfn-ref :my-parameter]]]
:resources [:my-instance (aws.ec2/map->Instance {:properties {:image-id "ami-79fd7eee"}})]
:outputs [:my-first-output [:cfn-get :my-instance :instance-type]
:my-second-output [:cfn-ref :my-parameter]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment