Giter Site home page Giter Site logo

cfndsl's Introduction

cfndsl

Spec Gem Version

AWS Cloudformation templates are an incredibly powerful way to build sets of resources in Amazon's AWS environment. Unfortunately, because they are specified in JSON or YAML, they are also difficult to write and maintain:

  • JSON does not allow comments, although CloudFormation now supports YAML

  • JSON/YAML templates do not allow re-usable logic

  • It is easy for a person reading a template to miss the association and dependencies between entries

  • References and internal functions have a particularly unpleasant syntax.

The cnfdsl gem provides a DSL that allows you to write templates using friendly Ruby logic to generate and validate cloudformation templates.

Getting Started

ruby version > 2.7 is required to run cfndsl, you should look at using rbenv example for installing with rbenv

rbenv exec gem install cfndsl

Example for doing it system wide Ruby

sudo gem install cfndsl

Update the the cloudformation specification to the latest version.

cfndsl -u

or update to a specific version

cfndsl -u 7.1.0

Now write a template in the dsl

CloudFormation {
  Description "Test"

  Parameter("One") {
    String
    Default "Test"
	MaxLength 15
  }

  Output(:One,FnBase64( Ref("One")))

  EC2_Instance(:MyInstance) {
    ImageId "ami-12345678"
  }

}

Then run cfndsl on the file

chris@raspberrypi:~/git/cfndsl$ cfndsl test.rb | json_pp
{
   "Parameters" : {
      "One" : {
	 "Type" : "String",
	 "Default" : "Test",
	 "MaxLength" : 15
      }
   },
   "Resources" : {
      "MyInstance" : {
	 "Type" : "AWS::EC2::Instance",
	 "Properties" : {
	    "ImageId" : "ami-12345678"
	 }
      }
   },
   "AWSTemplateFormatVersion" : "2010-09-09",
   "Outputs" : {
      "One" : {
	 "Value" : {
	    "Fn::Base64" : {
	       "Ref" : "One"
	    }
	 }
      }
   },
   "Description" : "Test"
}

Aside: that is correct - a significant amount of the development for this gem was done on a Raspberry Pi.

Syntax

cfndsl comes with a number of helper methods defined on each resource and/or the stack as a whole.

Template Metadata

Metadata is a special template section described here. The argument supplied must be JSON-able. Some CloudFormation features reference special keys if included in the Metadata, check the AWS documentation for specifics.

CloudFormation do
  Metadata(foo: 'bar')

  EC2_Instance(:myInstance) do
    ImageId 'ami-12345678'
    InstanceType 't1.micro'
  end
end

Template Parameters

At a bare minumum, parameters need a name, and default to having Type String. Specify the parameter in the singular, not plural:

CloudFormation do
  Parameter 'foo'
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "foo": {
      "Type": "String"
    }
  }
}

However, they can accept all of the following additional keys per the documentation:

Parameter('foo') do
  Description           'This is a sample parameter definition'
  Type                  'String'
  Default               'foo'
  NoEcho                true
  AllowedValues         %w(foo bar)
  AllowedPattern        '/pattern/'
  MaxLength             5
  MinLength             3
  MaxValue              10
  MinValue              2
  ConstraintDescription 'The error message printed when a parameter outside the constraints is given'
end

Parameters can be referenced later in your template:

EC2_Instance(:myInstance) do
  InstanceType 'm3.xlarge'
  UserData Ref('foo')
end

Template Mappings

Mappings are a hash-based lookup for your template. They can be specified in the singular or plural.

CloudFormation do
  Mapping('foo', letters: { a: 'a', b: 'b' }, numbers: { 1: 1, 2: 2 })
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
    "Mappings": {
      "foo": {
	"letters": {
	  "a": "a",
	  "b": "b"
	},
	"numbers": {
	  "one": 1,
	  "two": 2
	}
      }
    }
  }
}

You can then reference them later in your template using the FnFindInMap method:

EC2_Instance(:myInstance) do
  InstanceType 'm3.xlarge'
  UserData FnFindInMap('foo', :numbers, :one)
end

Template Outputs

Outputs are declared one at a time and must be given a name and a value at a minimum, description is optional. Values are most typically obtained from other resources using Ref or FnGetAtt:

CloudFormation do
  EC2_Instance(:myInstance) do
    ImageId 'ami-12345678'
    Type 't1.micro'
  end

  Output(:myInstanceId) do
    Description 'My instance Id'
    Value Ref(:myInstance)
  end
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "myInstance": {
      "Properties": {
	"ImageId": "ami-12345678"
      },
      "Type": "AWS::EC2::Instance"
    }
  },
  "Outputs": {
    "myInstanceId": {
      "Description": "My instance Id",
      "Value": {
	"Ref": "myInstance"
      }
    }
  }
}

Template Conditions

Conditions must be created with statements in three sections: a variable entry as a Parameter, a template-level Condition that holds the logic based upon the value of that Parameter, and a resource-level Condition that references the template-level one by logical id.

CloudFormation do
  Parameter(:environment) do
    Default 'development'
    AllowedValues %w(production development)
  end

  Condition(:createResource, FnEquals(Ref(:environment), 'production'))

  EC2_Instance(:myInstance) do
    Condition :createResource
    ImageId 'ami-12345678'
    Type 't1.micro'
  end
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "environment": {
      "Type": "String",
      "Default": "development",
      "AllowedValues": [
	"production",
	"development"
      ]
    }
  },
  "Conditions": {
    "createResource": {
      "Fn::Equals": [
	{
	  "Ref": "environment"
	},
	"production"
      ]
    }
  },
  "Resources": {
    "myInstance": {
      "Condition": "createResource",
      "Properties": {
	"ImageId": "ami-12345678"
      },
      "Type": "AWS::EC2::Instance"
    }
  }
}

Template Resources

Cfndsl creates accessor methods for all of the resources listed here and here. If a resource is missing, or if you prefer to explicitly enter a resource in a template, you can do so. Keep in mind that since you are using the generic Resource class, you will also need to explicitly set the Type and that you no longer have access to the helper methods defined on that particular class, so you will have to use the Property method to set them.

CloudFormation do
  Resource(:myInstance) do
    Type 'AWS::EC2::Instance'
    Property('ImageId', 'ami-12345678')
    Property('Type', 't1.micro')
  end

  # Will generate the same json as this
  #
  # EC2_Instance(:myInstance) do
  #   ImageId 'ami-12345678'
  #   Type 't1.micro'
  # end
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "myInstance": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
	"ImageId": "ami-12345678",
	"Type": "t1.micro"
      }
    }
  }
}

Resource Types

When using the generic Resource method, rather than the dsl methods, specify the type of resource using Type and the properties using Property. See Template Resources for an example.

Resource Conditions

Resource conditions are specified singularly, referencing a template-level condition by logical id. See Template Conditions for an example.

Resource DependsOn

Resources can depend upon other resources explicitly using DependsOn. It accepts one or more logical ids.

CloudFormation do
  EC2_Instance(:database) do
    ImageId 'ami-12345678'
    Type 't1.micro'
  end

  EC2_Instance(:webserver) do
    DependsOn :database
    ImageId 'ami-12345678'
    Type 't1.micro'
  end
end
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "database": {
      "Properties": {
	"ImageId": "ami-12345678"
      },
      "Type": "AWS::EC2::Instance"
    },
    "webserver": {
      "Properties": {
	"ImageId": "ami-12345678"
      },
      "Type": "AWS::EC2::Instance",
      "DependsOn": "database"
    }
  }
}

Resource DeletionPolicy

Resources can have deletion policies associated with them. Specify them one per resource as an attribute:

CloudFormation do
  EC2_Instance(:myInstance) do
    DeletionPolicy 'Retain'
    ImageId 'ami-12345678'
    Type 't1.micro'
  end
end

Resource Metadata

You can attach arbitrary metadata as an attribute. Arguments provided must be able to be JSON-ified:

CloudFormation do
  EC2_Instance(:myInstance) do
    Metadata(foo: 'bar')
    ImageId 'ami-12345678'
    Type 't1.micro'
  end
end

Resource CreationPolicy/UpdatePolicy

These attributes are only usable on particular resources. The name of the attribute is not arbitrary, it must match the policy name you are trying to attach. Different policies have different parameters.

CloudFormation do
  EC2_Instance(:myInstance) do
    ImageId 'ami-12345678'
    Type 't1.micro'
    CreationPolicy(:ResourceSignal, { Count: 1, Timeout: 'PT1M' })
  end
end

Samples

There is a more detailed example in the samples directory. The file "autoscale.template" is one of the standard Amazon sample templates. "autoscale.rb" generates an equivalent template file.

There's also a larger set of examples available at cfndsl_examples thanks to @neillturner.

Command Line Options

The cfndsl command line program now accepts some command line options.

Usage: cfndsl [options] FILE
    -o, --output FILE                Write output to file
    -y, --yaml FILE                  Import yaml file as local variables
    -j, --json FILE                  Import json file as local variables
    -p, --pretty                     Pretty-format output JSON
    -f, --format FORMAT              Specify the output format (JSON default)
    -D, --define "VARIABLE=VALUE"    Directly set local VARIABLE as VALUE
    -v, --verbose                    Turn on verbose output
    -m, --disable-deep-merge         Disable deep merging of yaml
    -s, --specification-file FILE    Location of Cloudformation Resource Specification file
    -u [VERSION],                    Update the Resource Specification file to latest, or specific version
        --update-specification
    -g RESOURCE_TYPE,RESOURCE_LOGICAL_NAME,
        --generate                   Add resource type and logical name
    -l, --list                       List supported resources
    -h, --help                       Display this screen

By default, cfndsl will attempt to evaluate FILE as cfndsl template and print the resulting cloudformation json template to stdout. With the -o option, you can instead have it write the resulting json template to a given file. The -v option prints out additional information (to stderr) about what is happening in the model generation process.

The -y, -j, -r and -D options can be used to control some things about the environment that the template code gets evaluate in. For instance, the -D option allows you to set a variable at the command line that can then be referred to within the template itself.

This is best illustrated with a example. Consider the following cfndsl template

# cfndsl template sample/t1.rb
CloudFormation do

  description = external_parameters.fetch(:description, 'default description')
  machines = external_parameters.fetch(:machines, 1).to_i

  Description description

  (1..machines).each do |i|
    name = "machine#{i}"
    EC2_Instance(name) do
      ImageId 'ami-12345678'
      Type 't1.micro'
    end
  end

end

Note the two variables description and machines. The template sets these to some reasonable default values, and if you run cfndsl on it without changing them in any way you get the following cloudformation template:

{
  "Resources": {
    "machine1": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
	"ImageId": "ami-12345678"
      }
    }
  },
  "Description": "default description",
  "AWSTemplateFormatVersion": "2010-09-09"
}

However if you run the command

$ cfndsl sample/t1.rb -D 'description=3 machine cluster' -D 'machines=3'

you get the following generated template.

{
  "Resources": {
    "machine3": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
	"ImageId": "ami-12345678"
      }
    },
    "machine2": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
	"ImageId": "ami-12345678"
      }
    },
    "machine1": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
	"ImageId": "ami-12345678"
      }
    }
  },
  "Description": "3 machine cluster",
  "AWSTemplateFormatVersion": "2010-09-09"
}

The -y and -j options allow you to group several variable definitions into a single file (formated as either yaml or json respectively). If you had a file called 't1.yaml' that contained the following,

# sample/t1.yaml
description: 5 machine cluster
machines: 5

the command

$ cfndsl sample/t1.rb -y sample/t1.yaml

would generate a template with 5 instances declared.

Specifying multiple -y options will default deep_merge all the yaml in the order specified. You can disable this with -m.

Rake task

Simply add the following to your Rakefile:

require 'cfndsl/rake_task'

namespace(:cfndsl) do
  CfnDsl::RakeTask.new do |t|
    # Use a custom specification file
    t.specification(file: 'tmp/cloudformation_resources.json')

    desc 'Generate CloudFormation Json'
    t.json(name: :json, files: FileList.new('sample/*.rb'), pathmap: 'tmp/%f.json')
    
    # Generate yaml output, loading an extra file that matches the source file
    t.yaml(name: :yaml, files: 'sample/t1.rb', pathmap: 'tmp/%f.yaml', extras: '%X.yaml')
  end
end

And then use rake to generate the cloudformation:

$ bin/rake cfndsl:generate

Embedded Ruby

# Optionally before requiring 'cfndsl' set global options
require `cfndsl/globals`
CfnDsl.specification_file(file) # Override location of json spec file 
CfnDsl.disable_deep_merge       # Prevent monkey patching of Hash with deep merge capabilities

require `cfndsl`

# As a function that takes a block of DSL
template = CloudFormation do
  # Some CfnDsl
end
puts JSON.pretty_generate(template.validate)

# As a class that is a template
class MyTemplate < CfnDsl::CloudFormationTemplate  
  def initialize 
    super do
      # Base DSL
    end  
  end
  
  def instance(logical_id)
    this = self
    EC2_Instance(logical_id) do
      self.class.name # << 'CfnDsl::ResourceDefinition' not 'MyTemplate' !
      InstanceType this.instance_type  
    end 
  end
   
  #do not make this private! 
  def instance_type
    't3.large'      
  end
end

# As a builder class
class Builder
  include CfnDsl::CloudFormation

  def model
    this = self # the DSL blocks are executed via instance_eval
    ref_param = Ref('param')  # and other Fn::*
    template = CloudFormation do
      # Some DSL
    end
    template.validate.to_json
  end
end

Generating CloudFormation resources from cfndsl

By supplying the -g parameter you are now able to generate cloudformation resources for supported objects, for a list of supported resources run cfndsl -l

Example

cfndsl -g AWS::EC2::EIP,EIP
require 'cfndsl'
CloudFormation do
  Description 'auto generated cloudformation cfndsl template'

  EC2_EIP('EIP') do
        Domain String # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-domain
        InstanceId String # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-instanceid
  end
end

Many thanks to the base code from cfnlego to make this possible!

cfndsl's People

Contributors

aaronwalker avatar ampedandwired avatar ans0600 avatar benley avatar cmaxwellau avatar danrasband avatar elmobp avatar gergnz avatar herebebogans avatar holmesjr avatar howech avatar ianneub avatar johnf avatar josephglanville avatar josh-wrale avatar k-ong avatar ketan avatar kornypoet avatar leg100 avatar liguorien avatar louism517 avatar lwoggardner avatar mikechau avatar nickjwebb avatar orien avatar pablovarela avatar pvdvreede avatar stevenjack avatar toshke avatar webdevwilson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cfndsl's Issues

Format identifiers

Allow the format function to work with named identifiers instead of just array indexes.

Add some better support for base64 in FnFormat

I often use cloud-init config style user data, and I occasionally find that I need to send the entire contents of a file to a stack as a template parameter. In many cases it is difficult to ensure soething does not get messed up (either whitespaes in the file content, or overall yaml formatting of outer userdata) when the parameter containing the file is expanded into its value. Luckily, cfn provides a base64 function just specifically for this kind of thing.

I have found myself writing the following code a bunch of times

  Instance(:node) {
   ...
   UserData FnBase64( FnFormat( <<EOF ,
#cloud-config
...
runcmd:
  - echo %{Base64ParameterName} | base64 -d > /path/to/file
EOF
                                                       :Base64Parameter => FnBase64( Ref( :ParameterName ) ),
   ))
 }

This works reasonably well, but it would be nice if FnFormat were smart enough to realize that when I refer to a value like :Base64ParameterName, what I really mean is the base64 encoding of the value of ParameterName unless I have explicity defined the value somewhere else.

Alternatively....

It might be nice to be able to declare named values globally across the template.

  Declare( :b64ParameterName, FnBase64( Ref( :ParameterName ) )

  Instance(:node) {
   ...
   UserData FnBase64( FnFormat( <<EOF ))
#cloud-config
...
runcmd:
  - echo %{b64ParameterName} | base64 -d > /path/to/file
EOF
   ))
 }

The implication here is that anyplace that directly or implicitly evaluates Ref(:b64PaameterName) will get the value passed in as the second argument to the Declare function.

One cfndsl file => multiple Cloudformation templates

Hello again.

For large templates, the normal size limit for a single Cloudformation template is 51200 bytes.

If someone wants to create a large, layered and highly available VPC, creating a Ruby loop in cfndsl makes quick work of generating the many subnets, routes, routetables, and on. Such a deployment if attempted with a monolithic Cloudformation template quickly expands to an illegally large size.

The next issue comes when you consider the limit of 10 outputs per template. The advertised answer to having too large a template is to layer templates by using parameters and outputs. The stack I'm seeking to build has approximately ten tiers (layers of VPC subnets) striped across three availability zones. Creating such a deployment with layers quickly runs against the output limit, especially when one tries to pass the metadata for so many subnets down to nested templates.

My imagined solution is to take a very granular approach while using parametrization, in a cfndsl-generated layering structure. This method would use the same foundational Ruby loops, arrays and hashes as before, in a monolithic cfndsl file to output multiple interlocking Cloudformation templates as a single JSON stream. In this way, one cfndsl Ruby file would output one long JSON file having individual self-contained Cloudformation templates throughout. A simple post-processing function would then be implemented to split the many Cloudformation templates into discrete files.

I was able to create two Cloudformation blocks inside of my cfndsl template successfully. However, piping it to json_pp failed. I think an extra JSON wrapper level is required for this to work without error.

The other obvious option is for me to generate discrete cfndsl files to bring about the layered structure. Without some shared arrays and such, the hand-coding of each template would all but negate one of the main reasons I chose cfndsl in the first place: very powerful for each loops.

I'll stop rambling now. Please let me know if any of this makes sense.

Thanks,
Joshua

Data driven type specifier

Add a data driven mechanism that allows the language to treat some or all of the resource property complex data types as language objects, something like resources.

How can AWS CloudFormation help?

cfndsl contributors and users,

Is there anything the AWS CloudFormation service could provide that can help in using and enhancing cfndsl? (For example, any additional API, any update to the template format, etc.)

Sincerely,
The AWS CloudFormation Team

Undefined symbol: EC2MountPoint - possible issue?

The simple EC2 Example fails. I get the error Undefined symbol: EC2MountPoint for the below code.

CloudFormation {
  Description 'Example Description'

  EC2_Instance('ExampleInstance') {
    ImageId 'ami-767a391e'
    Type 't2.small'
    AvailabilityZone 'string'
    Volumes [
      EC2MountPoint {
        VolumeId 'test string'
        Device '/dev/sde'
      }
    ]
  }
}

The syntax appears to be the same for BlockDeviceMappings as seen below and I get no errors. Is there a bug for EC2MountPoint or am I doing something wrong? Thanks.

  Instance('Instance Example') {
    ImageId 'ami-767a391e'
    Type 't2.micro'
    AvailabilityZone Ref('availabilityZone')
    UserData FnBase64(FnJoin('',
                             ['mke2fs -t ext4 /dev/xvde']))

    BlockDeviceMappings [
      BlockDeviceMapping {
        DeviceName '/dev/sde'
        Ebs 'test string'
      }
    ]
  }

Publish Updated Gem?

Be great to get a gem published with some of the latest updates. Last gem was published over a year ago.

Validate against schema used by eclipse plugin

After speaking to a number of colleagues who use the AWS eclipse plugin it seemed clear that it was using some sort of schema to validate the CF in the plugin as people were able to get feedback about incorrect params straight away.

After digging around, I found the following:

TemplateSchemaRules.java

This links off to a json schema:

CloudFormationV1.schema

that has the complete list of resources and valid properties for them.

I think it would be extremely valuable to incorporate this in so we could use this to build up the resource types and validate the resulting templates against.

I need to check with the developers of the plugin first about how this schema is maintained and if it's something we can depend on.

Unintended side effectos of PR #64 - Need better error messages for ambiguous resources.

A recent change added support for OpsWorks, which defines both a Stack and and Instance resource type. This makes it so that Instance and Stack are no longer valid top level resource names. You have to use CloudFormation_Stack or EC2_Instance or the OpsWorks _ flavors explicitly.

The trouble with this is that many existing scripts out there will now fail in cryptic ways.

The solution is to ensure that ambiguous resource definitions emit explicit error messages

Add support for UpdatePolicy on AutoScalingGroups

cfndsl does not currently support the UpdatePolicy directive on AutoScalingGroups.

This is what the cfndsl code would look like:

AutoScalingGroup("MyASG") {
    UpdatePolicy("AutoScalingRollingUpdate", {
                 "MinInstancesInService" => "1",
                 "MaxBatchSize"          => "1",
                 "PauseTime"             => "PT15M"
                 })
    LaunchConfigurationName Ref("MyLC")
    MinSize "2"
    MaxSize "150"
    DesiredCapacity "3"
}

resulting in this JSON output

"MyASG": {
    "UpdatePolicy" : {
        "AutoScalingRollingUpdate" : {
            "MinInstancesInService" : "1",
            "MaxBatchSize" : "1",
            "PauseTime" : "PT15M"
        }
    },
    "Type": "AWS::AutoScaling::AutoScalingGroup",
    "Properties": {
        "DesiredCapacity": "3",
        "MaxSize": "150",
        "MinSize": "3",
        "LaunchConfigurationName": {
            "Ref": "MyLC"
        },
    }
},

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html

Better documentation

The documentation is pretty well out of date. Update it to include recent changes.

[feature] a File() helper than can interpolate references for UserData scripts

Hi,

I think it's quite common to have to write userdata scripts that contain CloudFormation references. They usually look something like this:

"UserData":{
  "Fn::Base64":{
    "Fn::Join":[
      "",
      [
        "#!/bin/bash -vx\n",
        "/opt/aws/bin/cfn-init -s ", { "Ref":"AWS::StackId" }, " -r LaunchConfig --region ", { "Ref":"AWS::Region" }, "\n",
        "sh /tmp/setup.sh\n",
        "sleep 5\n",
        "/opt/aws/bin/cfn-signal -e $? ", " --stack ", { "Ref":"AWS::StackName" }, " --resource ServiceFleet ", " --region ", { "Ref":"AWS::Region" },
      ]
    ]
  }
}

It would be great if the DSL allowed us to write userdata.sh (with bonus syntax highlighting):

#!/bin/bash -vx
/opt/aws/bin/cfn-init -s {{AWS::StackId}} -r LaunchConfig --region {{AWS::Region}}
sh /tmp/setup.sh
sleep 5
/opt/aws/bin/cfn-signal -e $? --stack {{AWS::StackName}} --resource ServiceFleet --region {{AWS::Region}}

and then:

UserData FnBase64(File("userdata.sh"))

Is that something you cfnsdl already supports that I missed, or a feature you think could be useful? Note the example above would only handle simple Refs, which would already be useful. I can't think of a nice syntax to support the full gamut of operations like FindInMap, etc...

Thanks

Oddity where do/end block breaks expectations, but {} works fine?

Hi @howech @stevenjack

The following code will happily generate correct CloudFormation...

alarms = []

alarms.push Resource("CPUAlarmHigh") {
  Type "AWS::CloudWatch::Alarm"
  Property "AlarmDescription", "Scale-up if CPU > 80% for 1 minutes"
  ...other properties...
}

alarms.each do |alarm|
  alarm.declare {
    Property "MetricName", "CPUUtilization"
    ...other properties...
  }
end

But if you change {} on alarms.push Resource("CPUAlarmHigh") {} to be do/end like so:

alarms.push Resource("CPUAlarmHigh") do
  ...code...
end

Then I've found that the Type "AWS::CloudWatch::Alarm" doesn't get applied to the generated CloudFormation.

Any ideas why this is?

Help with conditions

Hello-

I'm trying to setup a condition in my cfndsl but keep getting the following error (I suspect something is wrong with my syntax).

Undefined symbol: fnEquals(#<CfnDsl::RefDefinition:0x007f90799f4d98 @Ref="Environment">, "infrastructure01")

Here is my code:

#my parameter I'm basing my condition off of
  Parameter("Environment") {
    String
    Description "The environment/vpc name"
    AllowedValues [
      "infrastructure01",
      "lab01"
    ]
  }

#
# a bunch of different resources here like Roles, Policies, Instances, Security Groups, etc, etc
#

#my condition referencing the parameter stated above
Condition("PrivateIPCondition", fnEquals(Ref("Environment"),"infrastructure01"))

If I comment out the Condition, my template renders perfectly. Can't seem to get the condition piece working right. What am I doing wrong here?

Thanks for the great tool and the help!

Unify implementations of singular/plural methods for array properties

Basically, there should be no difference between the plural and the singular methods. If a type has an array property named "Things", the type should have methods called "Thing", "Things", "thing" and "things" that all work like the following

#append t to the types list of Things
Thing t

# Append things t1, t2, t3, t4 to the list of things
Thing [t1, t2, t3, t4]

# Create a new thing, evaluate the block in its context 
# and append it to the list of things.
Thing {
  #thing block
}

# Create a new thing, evaluate the block in its context
# with the given parameter and append it to the list of
# things
Thing x { |y|
  #parameterized thing block
}

# Create a new thing for each element in the array, evaluate
# the block in context with the array item value as a parameter
# and append all of the results to the list of things.
Thing [x1, x2, x3, x4] { |y|
  #parameterized thing block
}

# Do nothing, but return the list of things
Thing()

# Actually, all of these should return the value of the types array variable

Self-referencing SecurityGroups

I'm trying to allow instances in a VPC-contained security group to talk to one another by self-referencing the SourceSecurityGroupID.

Here is an example of what I'm trying to do:

https://forums.aws.amazon.com/message.jspa?messageID=230251

I get various errors, depending on how I arrange things.

Here's an example of the output I'm seeing:

Invalid Reference: Resource SecGrpForPublicNAT refers to SecGrpForPublicNAT
Invalid Reference: Resource SecGrpForPrivateWeb refers to SecGrpForPrivateWeb

I tried breaking out into a SecurityGroupIngress resource item, which references the main SecurityGroup resource as a Ref: on properties: GroupId and SourceSecurityGroupId.

That exercise produced:

vagrant@wralebox:~/ezdi-devops/assets/cfndsl$ cfndsl ServiceVpc.rb
ServiceVpc.rb:398: Undefined symbol: SourceSecurityGroupId(#<CfnDsl::RefDefinition:0x00000000f7bac0 @ref="SecGrpForPublicNAT">)
ServiceVpc.rb:506: Undefined symbol: SourceSecurityGroupId(#<CfnDsl::RefDefinition:0x00000000f669b8 @ref="SecGrpForPrivateWeb">)
ServiceVpc.rb:570: Undefined symbol: SourceSecurityGroupId(#
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x000000013a3a58 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x00000001390ed0 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x000000013916a0 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x0000000138bc00 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x0000000138b4a8 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x0000000138ad00 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x0000000138a5d0 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x00000001389e28 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x00000001389680 @ref="Vpc">)
ServiceVpc.rb:1626: Undefined symbol: VpcId(#<CfnDsl::RefDefinition:0x00000001388eb0 @ref="Vpc">)

FnNot() feels awkward

I find myself writing things like FnNot(FnEquals(…)), which is wrong, since this compiles to { "Fn::Not": { "Fn::Equals": … } }. Fn::Not takes a single element array, so the correct formulation is FnNot([FnEquals…]) instead.

I don't believe it's ever correct to call FnNot() with any parameter other than a single-element array, so… can we make the natural syntax valid? That is, if FnNot() gets something that's not an array, can we automatically wrap it in one?

Better test cases

The core test cases handle internal functions and reference checking.

Need some tests that look explicitly into some json generation, and testing the edge cases on the generated property methods.

DependsOn should be constructive

currently, the resource method "DependsOn" replaces the resources dependency list with its arguments. It would be more useful if DependsOn would append its arguments to the list of dependencies, so you could use it to accumulate dependencies.

Support for rendering nested stacks

nested stacks for example

master.rb

CloudFormation {
....
  Resource("VPCStack") {
    Type 'AWS::CloudFormation::Stack'
    Property('TemplateURL', "https://s3-ap-southeast-2.amazonaws.com/#{sourceBucket}/cloudformation/#{cf_version}/vpc.json" )
    Property('TimeoutInMinutes', 5)
    Property('Parameters',{
      EnvironmentType: Ref('EnvironmentType'),
      EnvironmentName: Ref('EnvironmentName')
    })

  Resource("DatabaseStack") {
    Type 'AWS::CloudFormation::Stack'
    DependsOn('VPCStack')
    Property('TemplateURL', "https://s3-ap-southeast-2.amazonaws.com/#{sourceBucket}/cloudformation/#{cf_version}/database.json" )
    Property('TimeoutInMinutes', 5)
    Property('Parameters',{
      EnvironmentType: Ref('EnvironmentType'),
      EnvironmentName: Ref('EnvironmentName'),
      RDSSnapshotID: Ref('RDSSnapshotID'),
      VPC: FnGetAtt('VPCStack', 'Outputs.VPCId'),
      RouteTablePrivateA: FnGetAtt('VPCStack', 'Outputs.RouteTablePrivateA'),
      RouteTablePrivateB: FnGetAtt('VPCStack', 'Outputs.RouteTablePrivateB')
    })
  }
.....
}

And we would have a corresponding cfndsl template per nested and more often that not called the sample name as the filename is the TemplateURL property. In the example above we would have a vpc.rb and database.rb

Right now we need to call cfndsl for each template not a huge deal but if we could combine this into a single cfndsl execution it would be possible to do output parameter validation. Also by combining them into a single execution we could do some calculation in one nested stack that could be used in another.

For example we calculate subnet offsets in the vpc template and then use those calculated values in the database template with out having to use cloudformation output parameters to pass essentially something that is static.

ideally I'd just like to call cfndl passing my master template and have it render all of the nested stacks and validate all of the input/output parameters from each parent and child

@stevenjack Thoughts????

SecurityGroupIngress/Egress formatting with additional []

I have defined a SecurityGroup with an 'embedded' Ingress rule

EC2_SecurityGroup(:mySG) {
  VpcId Ref(:VPC)
  GroupDescription "my SG"
  SecurityGroupIngress [
    {
      "CidrIp"     => "10.0.0.0/8",
      "IpProtocol" => "tcp",
      "FromPort"   => "22",
      "ToPort"     => "22"
    }
  ]
}  

The generated JSON for the Ingress contains an additional [ .... ] which is rejected by CloudFormation. This also happens for Egress as well. Note that the similar format NetworkInterfaces (in EC2_Instance) seems to work OK.

Generated:

            "SecurityGroupIngress" : [
               [
                  {
                     "FromPort" : "22",
                     "IpProtocol" : "tcp",
                     "CidrIp" : "10.0.0.0/8",
                     "ToPort" : "22"
                  }
               ]
            ]

should be:

            "SecurityGroupIngress" : [
                  {
                     "FromPort" : "22",
                     "IpProtocol" : "tcp",
                     "CidrIp" : "10.0.0.0/8",
                     "ToPort" : "22"
                  }
               ]

Removing the [ ...] from around

     SecurityGroupIngress: [ EC2SecurityGroupRule ]
     SecurityGroupEgress: [ EC2SecurityGroupRule ]

in aws_types.yaml seems to fix the problem for me but I don't think that's the correct approach

Is this a good way to extend cfndsl for project specific requirements?

I'm not a Ruby developer so I'm not sure what's a good way to extend cfndsl to support project specific requirements. For example I'd like to define a single method that can define an egress->ingress rule pair for a security. eg Web Security Group => DB Security Group on tcp port 1443.

I've got this to work but wonder if there is a better or more elegant way.

CloudFormation do
  Description("SecurityGroups")
  AWSTemplateFormatVersion("2010-09-09")

  VPC = "myVPC"
  SG_OWNER_ID = "1234567890"  

  def EC2_SecurityGroupPermission(name, source, destination, protocol, port)

    EC2_SecurityGroupEgress("#{name}Egress") {
      GroupId source
      IpProtocol protocol
      FromPort port
      ToPort port
      DestinationSecurityGroupId destination
    } 

    EC2_SecurityGroupIngress("#{name}Ingress") {
      GroupId destination
      IpProtocol protocol
      FromPort port
      ToPort port
      SourceSecurityGroupId source
      SourceSecurityGroupOwnerId SG_OWNER_ID
    }  

  end

  EC2_SecurityGroup(:sgWeb) {
    VpcId VPC
    GroupDescription "SG-Web"
    SecurityGroupIngress []
    SecurityGroupEgress []
  } 

  EC2_SecurityGroup(:sgDB) {
    VpcId VPC
    GroupDescription "SG-DB"
    SecurityGroupIngress []
    SecurityGroupEgress []
  }   

  EC2_SecurityGroupPermission("WebToDB1443", Ref(:sgWeb), Ref(:sgDB), "tcp", "1443")

end

This generates:

{
   "Description" : "DEVTEST-DSL: NAT, Bastion, Nginx, Rabbit, Redis",
   "AWSTemplateFormatVersion" : "2010-09-09",
   "Resources" : {
      "sgWeb" : {
         "Type" : "AWS::EC2::SecurityGroup",
         "Properties" : {
            "GroupDescription" : "SG-Web",
            "SecurityGroupIngress" : [],
            "SecurityGroupEgress" : [],
            "VpcId" : "myVPC"
         }
      },
     "sgDB" : {
       "Type" : "AWS::EC2::SecurityGroup",
       "Properties" : {
         "SecurityGroupEgress" : [],
         "SecurityGroupIngress" : [],
         "GroupDescription" : "SG-DB",
         "VpcId" : "myVPC"
       }
     },
     "WebToDB1443Ingress" : {
         "Type" : "AWS::EC2::SecurityGroupIngress",
         "Properties" : {
            "SourceSecurityGroupId" : {
               "Ref" : "sgWeb"
            },
            "FromPort" : "1443",
            "SourceSecurityGroupOwnerId" : "1234567890",
            "ToPort" : "1443",
            "IpProtocol" : "tcp",
            "GroupId" : {
               "Ref" : "sgDB"
            }
         }
      },
      "WebToDB1443Egress" : {
         "Properties" : {
            "IpProtocol" : "tcp",
            "DestinationSecurityGroupId" : {
               "Ref" : "sgDB"
            },
            "GroupId" : {
               "Ref" : "sgWeb"
            },
            "ToPort" : "1443",
            "FromPort" : "1443"
         },
         "Type" : "AWS::EC2::SecurityGroupEgress"
      }
   }
}

Requesting cfndsl example based on this AWS example template

Hi. I'm eager to use cfndsl for a three-AZ eight-tier H/A VPC, similar to the following AWS article. Each of the eight tiers would get a dedicated subnet, per AZ. I'm worried about expressing this complex VPC architecture in CloudFormation, which is why I'm looking at cfndsl.

https://aws.amazon.com/articles/6079781443936876

I'm newish to Ruby, but I'm trying to port their example to cfndsl and eventually extend it for the third AZ.

Please consider rendering an example of a similar configuration using cfndsl.

Thanks,
Joshua

Support for userData Script

Hi there, I haven't found support for having a script in UserData. I think it'd be useful to have for instance:

in userdata.sh

    #!/bin/bash
    echo "hello world"

in Ruby:

    Resource(…) {
            Property("UserData", FnBase64(IO.read(‘userdata.sh’)))
    }

of course the problem arises when instead of a simple script like the one above, you start inserting things like Ref("AWS::StackId”), and alike…

any ideas?

cheers

Fede

Output to a string instead of STDOUT?

Thus far I've used cfndsl to output the CF JSON via the command line, and it's been awesome.

I'd like to step it up though and integrate it into a Rails app, that stores the cfndsl code in a database and then generate the output JSON from that and then (with aws-sdk) actually launch the CF template.

I'm not quite sure how to do that. So far the only thing that I've gotten to work is to open a shell and run the file from there. There's got to be a better way.

Anyone have any suggestions?

Nested arrays in SecurityGroupIngress property of EC2_SecurityGroup

When trying to add ingress rules directly as a property on a SecurityGroup the array gets nested with an outer array.

EC2_SecurityGroup("SampleGroup"){
    VpcId Ref("VpcId")
    GroupDescription "Sample group"
    SecurityGroupIngress [{:IpProtocol=>"tcp"}]
  }

Will render the following.

"SecurityGroupIngress": [
          [
            {
              "IpProtocol": "tcp"
            }
          ]
        ]

I can see where it is all happening...well I think so. https://github.com/stevenjack/cfndsl/blob/master/lib/cfndsl/Plurals.rb#L7 I have not figured out how to fix it, if someone would like to give me some pointers I am happy to have a crack.

Better way to tag Instances

Come up with a shorter way to tag instances.

Currently you have to:

Instance("MyInstance") {
  Tag {
    Key "foo"
    Value "bar"
  }
}

But it would be nice if you could

Instance("MyInstance") {
  Tag("foo","bar")
}

"AWS::SNS::Topic" does not appear to be fully defined

Hi there,

Thanks for all your great work on this gem. I have been using it very heavily.

It looks like AWS::SNS::Topic is not fully defined in the YAML file. I am happy to create a PR that fixes this one up.

If you could provide some guidance on your preferred path to contribution that would also be really helpful. Would you prefer I create issue and put then create the PR off the back of that for changes such as this? Or is a simple PR on its own sufficient?

Fraser

CloudFormation output ordering doesn't match order of cfndsl template

Minor hang I suppose, but I think it is more efficient if the CloudFormation template renders in the order of the source cfndsl template. I suppose making this happen is probably pretty hard, given the many abstractions cfndsl introduces.

Having parameters like "AvailabilityZone{1,2,3}" show up at random in the list makes debugging and output template sharing a little more difficult, I think.

Thanks!

Joshua

Config driven template generation

@aaronwalker This is great, been thinking (Not for this PR) but it could be better if you were able to define a .cfndsl.yml config in your project directory which could look like:

extras: "default_extras.yml"
templates:
  component_one:
    filename: "component_one/template.json"
    extras: "component_one/extras.yml"
    output: "stacks/component_one.json"

You then just run cfndsl and it'll generate the templates based on this configuration (Bonus of having default extras at the top then each template can override and have it's own extras).

With the identifier as the key for the config, you could also just generate one component by doing something like:

cfndsl -c component_one

(Would have to look at the best way of handling this to keep backwards compatibility)

Errors on Mac 10.8.4, using version installed with 'gem install cfndsl'

Am I doing something obviously wrong?

laptop:cfndsl-templates josh$ cfndsl
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Errors.rb:7: warning: don't put space before argument parentheses
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Errors.rb:15: warning: don't put space before argument parentheses
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: syntax error, unexpected '=', expecting '|' (SyntaxError)
...ethod(method_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: Can't assign to nil
...d(method_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: syntax error, unexpected ',', expecting '='
...d_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:139: syntax error, unexpected kELSE, expecting kEND
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: syntax error, unexpected '=', expecting '|'
                define_method(method) do | value=nil, *rest, &block |
                                                 ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: Can't assign to nil
                define_method(method) do | value=nil, *rest, &block |
                                                     ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: syntax error, unexpected ',', expecting '='
                define_method(method) do | value=nil, *rest, &block |
                                                            ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:157: syntax error, unexpected kEND, expecting $end
  end  
     ^
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
    from /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl.rb:7
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
    from /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/bin/cfndsl:2
    from /usr/bin/cfndsl:19:in `load'
    from /usr/bin/cfndsl:19
lap-28-174:cfndsl-templates josh$ cfndsl autoscale.rb 
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Errors.rb:7: warning: don't put space before argument parentheses
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Errors.rb:15: warning: don't put space before argument parentheses
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: syntax error, unexpected '=', expecting '|' (SyntaxError)
...ethod(method_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: Can't assign to nil
...d(method_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:74: syntax error, unexpected ',', expecting '='
...d_name) do | value=nil, *rest, &block|
                              ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:139: syntax error, unexpected kELSE, expecting kEND
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: syntax error, unexpected '=', expecting '|'
                define_method(method) do | value=nil, *rest, &block |
                                                 ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: Can't assign to nil
                define_method(method) do | value=nil, *rest, &block |
                                                     ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:145: syntax error, unexpected ',', expecting '='
                define_method(method) do | value=nil, *rest, &block |
                                                            ^
/Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl/Types.rb:157: syntax error, unexpected kEND, expecting $end
  end  
     ^
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
    from /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/lib/cfndsl.rb:7
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
    from /Library/Ruby/Gems/1.8/gems/cfndsl-0.0.16/bin/cfndsl:2
    from /usr/bin/cfndsl:19:in `load'
    from /usr/bin/cfndsl:19
laptop:cfndsl-templates josh$ 

Add Properties syntax for Resources

I'm currently writing a templates for some new infrastructure and I'm finding I need to repeat the Property declaration more than I'd like.
It'd be great if you could bundle together the Property declarations for a resource.

Currently I've got the following code

  Resource("MySQLDatabase") {
    Type "AWS::RDS::DBInstance"
    Property("Engine", "MySQL")
    Property("DBName", "Production")
    Property("MultiAZ", Ref("MultiAZDatabase"))
    Property("MasterUsername", Ref("DBUsername"))
    Property("MasterUserPassword", Ref("DBPassword"))
    Property("DBInstanceClass", Ref("DBInstanceClass"))
    Property("")
  }

It'd be nice if you could pass in a hash instead. eg

  Resource("MySQLDatabase") {
    Type "AWS::RDS::DBInstance"
    Properties(["Engine" => "MySQL",
                         "DBName" => "Production")
                         "MultiAZ" => Ref("MultiAZDatabase"))
                         "MasterUsername" => Ref("DBUsername"))
                         "MasterUserPassword" => Ref("DBPassword"))
                         "DBInstanceClass" => Ref("DBInstanceClass")]
  }

Does this sound like something that would be useful?

How do I format a JSON Abstracted Resource Object Property?

I have a QueuePolicy resource which works well with cfndsl

  Resource("sqspolicyEvents") do
    Type("AWS::SQS::QueuePolicy")
    Property("Queues", [
      Ref("queueEvents")
    ])
    Property("PolicyDocument", {
      "Statement" => [
        {
          "Action"    => ["SQS:SendMessage"],
          "Effect"    => "Allow",
          "Principal" => {"AWS" => [ "*"] },
          "Resource"  => [FnGetAtt("queueEvents", "Arn")],
          "Sid"       => "Sid1430973749198"
        }
      ]
    })
  end

I'm in the process of updating my 'code' to use the abstracted objects; eg:

  QueuePolicy(:sqspolicyEvents) {
    Queues [ Ref("queueEvents") ]
    PolicyDocument {
      "Statement" => [
        {
          "Action"    => ["SQS:SendMessage"],
          "Effect"    => "Allow",
          "Principal" => {"AWS" => [ "*"] },
          "Resource"  => [FnGetAtt("queueEvents", "Arn")],
          "Sid"       => "Sid1430973749198"
        }
      ]
    }
  }

However I'm getting a syntax error: syntax error, unexpected =>, expecting '}'

What's the correct way to format a PolicyDocument? The definitions in aws_types.yaml indicates a Type of JSON but I've not been able to work out how to do format this.

  "AWS::IAM::Policy" :
   Properties:
    PolicyName: String
    PolicyDocument: JSON
    Groups: [ String ]
    Users: [ String ]
    Roles: [ String ]

License missing from gemspec

Some companies will only use gems with a certain license.
The canonical and easy way to check is via the gemspec,

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Even for projects that already specify a license, including a license in your gemspec is a good practice, since it is easily
discoverable there without having to check the readme or for a license file. For example, it is the field that rubygems.org uses to display a gem's license.

For example, there is a License Finder gem to help companies ensure all gems they use
meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough
issue that even Bundler now generates gems with a default 'MIT' license.

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), github has created a license picker tool.

In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :).

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue and let me know. In either case, I'll follow up. Thanks!

p.s. I've written a blog post about this project

aws_types.aws AWS::SQS::Queue missing property QueueName: String

aws_types.aws is missing QueueName: String from Properties.

it should be able to support

  Queue(:queueEvents) {
    QueueName QUEUE_EVENTS_NAME
    VisibilityTimeout "30"
  }

suggested fix:

  "AWS::SQS::Queue" :
    Properties:
     VisibilityTimeout: String
     QueueName: String
    Attributes:
     Arn: String
     QueueName: String

Add support for Openstack Heat

Openstack's Orchestration package, Heat, seems to be almost blow for blow similar to AWS Cloudformation (YAML instead of JSON, and some other minor diffs...) It should not be too hard to adapt this project to also be able to drive openstack configurations.

Limited Validatation of Resource Property Data Types

For properties of simple types, ensure that the supplied value matches up with the type. For properties of complex type, make sure that the value supplied is not incompatible (eg for a parameter that wants an array of structures, complain if you give it a string.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.