Introducing Cloud Formation Simulator Tool

AWS CloudFormation Templates (CFT) are designed to simplify deployment and management on AWS. CFTs have come a long way from being a declarative infrastructure blueprint. Power users of AWS CloudFormation now create CFTs which act like a software program that accepts parameters, processes them according to logic and generates output.

While working with CFTs, the Dome9 team faced a couple of needs that we had to address:

  • In the world of ‘infrastructure as code’ it is becoming increasingly important to perform assessments of the CFT – before they go to production. We (and our customers) wanted to run Dome9’s Clarity Visualization tool to visualize complex CFTs – ones that include parameters, conditions, etc.
  • CFT Debugging – we felt that the experience of provisioning real infrastructure to test and debug CFTs is too slow and not friendly enough for the developer. This is especially true for complex CFTs with multiple parameters / possible permutations to test.

To meet these needs, we built a tool called CFT-Simulator that allows you to run an offline (=not executing the stack in AWS environment) simulation of the CFT according to various input parameters.

With the CFT-Simulator, you can debug complex CFTs and transform a generic template into a concrete plan with all parameters and conditions being evaluated right on your laptop.

Let’s see it in action:

The tool is open sourced and hosted on github : https://github.com/Dome9/cft-simulator
It is installed according to the provided instructions.

Assuming this is our original CFT:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Mappings": {
      "RegionMap": {
        "us-east-1": {
          "AMI": "ami-use1"
        },
        "us-west-1": {
          "AMI": "ami-usw1"
        },
        "us-west-2": {
          "AMI": "ami-usw2"
        }
      }
    },
    "Parameters": {
      "EnvType": {
        "Default": "test",
        "Type": "String",
        "AllowedValues": [
          "prod",
          "dev",
          "test"
        ]
      },
      "NameIndex": {
        "Default": 0,
        "Type": "Number"
      }
    },
    "Conditions": {
      "CreateProdResources": {
        "Fn::Equals": [
          {
            "Ref": "EnvType"
          },
          "prod"
        ]
      },
      "CreateDevResources": {
        "Fn::Equals": [
          {
            "Ref": "EnvType"
          },
          "dev"
        ]
      },
      "OrTest": {
        "Fn::Or": [
          {
            "Condition": "CreateProdResources"
          },
          {
            "Condition": "CreateDevResources"
          }
        ]
      },
      "NonProd": {
        "Fn::Not": [
          {
            "Condition": "CreateProdResources"
          }
        ]
      }
    },
    "Resources": {
      "EC2Instance": {
        "Type": "AWS::EC2::Instance",
        "Properties": {
          "FindInMapTest": {
            "Fn::FindInMap": [
              "RegionMap",
              {
                "Ref": "AWS::Region"
              },
              "AMI"
            ]
          },
          "NestedIfTest": {
            "Fn::If": [
              "CreateProdResources",
              "c1.forprod",
              {
                "Fn::If": [
                  "CreateDevResources",
                  "m1.forDevstuff",
                  "m1.forOtherEnvs"
                ]
              }
            ]
          },
          "SelectTest": {
            "Fn::Select": [
              {
                "Ref": "NameIndex"
              },
              [
                "apples",
                "grapes",
                "oranges",
                "mangoes"
              ]
            ]
          },
          "JoinTest": {
            "Fn::Join": [
              ":",
              [
                "a",
                "b",
                "c"
              ]
            ]
          },
          "PseudoParam": {
            "Ref": "AWS::Region"
          }
        }
      },
      "ProdInstance": {
        "Type": "AWS::EC2::Instance",
        "Properties": {
          "Name": "ProdOnly"
        },
        "Condition": "CreateProdResources"
      },
      "NonProdInstance": {
        "Type": "AWS::EC2::Instance",
        "Properties": {
          "Name": "aNonProdInstance"
        },
        "Condition": "NonProd"
      }
    }
  }

 

We wish to simulate how it will look in ‘prod’ environment (and with parameter ‘NameIndex’=1)

So we run:

$ node cft-simulator.js -p ‘{“EnvType”:”prod”,”NameIndex”:1}’ test/test1.json

and it outputs:

{
   "AWSTemplateFormatVersion": "2010-09-09",
   "Conditions": {
     "CreateProdResources": true,
     "CreateDevResources": false,
     "OrTest": true,
     "NonProd": false
   },
   "Resources": {
     "EC2Instance": {
       "Type": "AWS::EC2::Instance",
       "Properties": {
         "FindInMapTest": "ami-use1",
         "NestedIfTest": "c1.forprod",
         "SelectTest": "grapes",
         "JoinTest": "a:b:c",
         "PseudoParam": "us-east-1"
       }
     },
     "ProdInstance": {
       "Type": "AWS::EC2::Instance",
       "Properties": {
         "Name": "ProdOnly"
       },
       "Condition": true
     }
   }
 }

 

Look ma! All of my conditions are now properly evaluated, including parameters and AWS intrinsic functions – so only ‘prod’ configurations are now in place!  I have validated that my logic works.

Now, this output is not abstract anymore, hence is more suitable to be used for static compliance tests (we’ll discuss that in a future blog) or to be uploaded into Dome9 Clarity to generate something like:

Though we created it for our own needs, we’ll be happy to know if this tool helps you too. If you wish to contribute – that’s even better!

You can check out some of our other open-source tools and software on our github site: https://github.com/Dome9.

You may also like

Comments are closed.