jagregory / cfval Goto Github PK
View Code? Open in Web Editor NEWThe CloudFormation template validator
License: MIT License
The CloudFormation template validator
License: MIT License
The Ref docs and the DBSubnetGroup docs both have no mention of a Ref value for a DBSubnetGroup (although the Ref docs do list DBSecurityGroup twice which could be an oversight).
However, there is a sample template VPC_WordPress_Single_Instance_With_RDS.template
which does use a Ref of an DBSubnetGroup and it appears to be the Name.
The CLI tool itself is very bare bones. It literally does nothing apart from receive JSON from STDIN.
The minimum the CLI should do is:
template:
{
"Parameters": {
"subnetIds": {
"Type": "List<AWS::EC2::Subnet::Id>"
}
},
"Resources": {
"ElasticacheSubnetGroup": {
"Type": "AWS::ElastiCache::SubnetGroup",
"Properties": {
"Description": ".",
"SubnetIds": [
{ "Fn::Select": [ "0", { "Ref": "subnetIds" } ] }
]
}
}
}
}
yields error:
ERROR Resources.ElasticacheSubnetGroup.SubnetIds.0.Fn::Select.Index Wrong type for index string
using cfval
version e7af583dbdc0f7641678070a176c82a7a9ffe996
.
Dangerous coersions seem to usually be warnings, so the only thing that seems out of place to me here is that this is reported as an error.
You know what an IPv4 IP address looks like.
A valid ContainerDefinition results in an error:
Resources.taskDefinition.ContainerDefinitions.0.Memory ........ float64 used in List<String> property
The docs say it should be an Integer.
I believe the issue lies in container_definition.go#L43
The sample templates such as worker-role.template
use it, and presumably they outrank the Fn::Base64 docs.
The Cloudformation Init services UserGuide documents both ensureRunning
and enabled
as boolean.
The template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"instance": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"foo": {
"services": {
"sysvinit": {
"bar": {
"ensureRunning": true,
"enabled": true
}
}
}
}
}
}
}
}
}
Yields these undesired errors:
Resources.instance.Metadata.AWS::CloudFormation::Init.foo.services.sysvinit.bar.enabled ......... Value of Bool used in Number property
Resources.instance.Metadata.AWS::CloudFormation::Init.foo.services.sysvinit.bar.ensureRunning ... Value of Bool used in Number property
version: 87b495da21cbd389ad5532a2824919a5b674361c
Step 1. Validate that uses of Ref
and GetAtt
will return a value of the correct type
"KeyName": { "Ref": "MaxInstanceCount" } // should fail due to MaxInstanceCount being an int
Step 2. Introduce richer types to further help with validations
"SecurityGroupId": { "Ref": "SecurityGroup" } // should fail because Ref on a SecurityGroup returns the NAME not the ID
"SecurityGroupName": { "Fn::GetAtt": ["SecurityGroup", "GroupId"] } // ditto
It's possible to use AWS::NoValue
with conditions to "unset" a property, allowing you to have otherwise conflicting properties together.
e.g.
"VPCSecurityGroups": { "Fn::If" : [ "Is-EC2-VPC", [ { "Fn::GetAtt": [ "DBEC2SecurityGroup", "GroupId" ] } ], { "Ref" : "AWS::NoValue"}]},
"DBSecurityGroups": { "Fn::If" : [ "Is-EC2-Classic", [ { "Ref": "DBSecurityGroup" } ], { "Ref" : "AWS::NoValue"}]}
Currently we fail on this, because you've specified both properties where you're only allowed to specify one.
The simple implementation of this would be to downgrade a conflict to a warning if it's body is a condition containing AWS::NoValue
. The warning would be more that there could be something wrong, but we can't reliably tell.
A more advanced implementation would be to evaluate the conditionals and see if they ever would result in the two properties being present at once. This will be pretty complicated with nested conditions.
Should validate DependsOn
for resources in the same way we validate Refs.
I am able to cause a panic when validating this template:
{
"Resources": {
"x": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"VPCZoneIdentifier": {
"Fn::FindInMap": ["mapName", { "Ref": "something" }, "secondKey"]
}
}
}
}
}
Panic:
panic: interface conversion: interface is map[string]interface {}, not []interface {}
goroutine 1 [running]:
github.com/jagregory/cfval/schema.Schema.Validate(0x1, 0x0, 0x0, 0x0, 0x0, 0x7fe5af1d0378, 0xc8200a6780, 0x7fe5af1d02f8, 0xc82000d4a0, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/schema.go:63 +0x143
github.com/jagregory/cfval/schema.Properties.Validate(0xc82000d3e0, 0x7fe5af1d0a98, 0xc820086bd0, 0x0, 0x0, 0x0, 0x90)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/properties.go:41 +0xa95
github.com/jagregory/cfval/schema.Resource.Validate(0x6b2320, 0x22, 0x0, 0xc82000d3e0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/resource.go:18 +0xfa
github.com/jagregory/cfval/schema.resourceValidate(0xc820175b00, 0x22, 0xc820175b30, 0x0, 0x7fe5af1d09f8, 0xc820086b40, 0xc820086b40, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:49 +0x284
github.com/jagregory/cfval/schema.TemplateValidate(0xc82018f5c0, 0x7fe5af1d09c8, 0xc820175740, 0x7fe5af1d09c8, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:15 +0x3c0
main.ValidateCommand.Run(0xc82000a070, 0x0, 0x0, 0xc820173d40)
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:174 +0x788
main.(*ValidateCommand).Run(0x7d3c58, 0xc82000a070, 0x0, 0x0, 0x6e2ff0)
<autogenerated>:6 +0xab
github.com/mitchellh/cli.(*CLI).Run(0xc8200b83c0, 0xc820175830, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/mitchellh/cli/cli.go:153 +0x538
main.main()
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:201 +0x181
goroutine 5 [runnable]:
os/signal.loop()
/usr/lib/go/src/os/signal/signal_unix.go:20
created by os/signal.init.1
/usr/lib/go/src/os/signal/signal_unix.go:28 +0x37
Where possible, we should validate that the map names/keys are valid in a Fn::FindInMap
call. This will only work up to the point of where we encounter a non-string value.
{ "Fn::FindInMap": ["mapName", "topKey", "secondKey"] } // should be validatable
{ "Fn::FindInMap": ["mapName", { "Ref": "something" }, "secondKey"] } // won't be
The docs say it is, but the sample templates don't specify it so perhaps it isn't; either that, or it is mandatory but didn't used to be. Maybe creating a test stack would flush this one out?
Probably a precursor to #14
Certain properties, such as AWS::ElasticBeanstalk::Application
ApplicationVersions
are deprecated. Old templates still include them and they still work, but we should warn or error on their use (and not just error with "unexpected property").
Constraints: Up to 255 ASCII characters
LBCookieStickinessPolicy
on AWS::ELB::LoadBalancer
should be a list, currently only allows a single item.
When I validate this template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"sg": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
}
},
"elb": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"SecurityGroups": [ { "Fn::GetAtt": [ "sg", "GroupId" ] } ]
}
}
}
}
I get this error:
panic: interface conversion: interface is parse.IntrinsicFunction, not string
goroutine 1 [running]:
github.com/jagregory/cfval/schema.resourceID.Validate(0x696a30, 0xf, 0xc82003e580, 0x39, 0xc8200683c0, 0x64dc40, 0xc82018f8c0, 0x7ff5981caa68, 0xc820089320, 0x0, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/value_id.go:41 +0x19a
github.com/jagregory/cfval/schema.(*resourceID).Validate(0xc8200db200, 0x64dc40, 0xc82018f8c0, 0x7ff5981caa68, 0xc820089320, 0x0, 0x0, 0x0, 0x0, 0x0)
<autogenerated>:90 +0xfa
github.com/jagregory/cfval/schema.validateValue(0x64dc40, 0xc82018f8c0, 0x7ff5981caa68, 0xc820089320, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/schema.go:116 +0x11e
github.com/jagregory/cfval/schema.Schema.Validate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7ff5981c6350, 0xc8200db200, 0x0, 0x64dc40, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/schema.go:54 +0x197
github.com/jagregory/cfval/schema.validateArray(0x7ff5981c6350, 0xc8200db200, 0x5e6ce0, 0xc82018f8e0, 0x7ff5981caa68, 0xc820089200, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/schema.go:67 +0x45d
github.com/jagregory/cfval/schema.Schema.Validate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7ff5981c6288, 0xc8200cb770, 0x0, 0x5e6ce0, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/schema.go:52 +0x136
github.com/jagregory/cfval/schema.Properties.Validate(0xc8200db260, 0x7ff5981ca9f8, 0xc820089050, 0x0, 0x0, 0x0, 0x90)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/properties.go:41 +0xa95
github.com/jagregory/cfval/schema.Resource.Validate(0x6cc180, 0x27, 0xc8200db170, 0xc8200db260, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/resource.go:18 +0x119
github.com/jagregory/cfval/schema.resourceValidate(0xc8201e80f0, 0x27, 0xc8201e83f0, 0xc8201e9170, 0x7ff5981ca950, 0xc820088fc0, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/template.go:49 +0x293
github.com/jagregory/cfval/schema.TemplateValidate(0xc82018f5a0, 0x7ff5981ca920, 0xc820175770, 0xc8201e91a0, 0xc8201e91a0, 0x0, 0x0, 0x0)
/home/vagrant/golang/src/github.com/jagregory/cfval/schema/template.go:15 +0x3db
main.ValidateCommand.Run(0xc82000a070, 0x0, 0x0, 0xc820173d40)
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:180 +0x81b
main.(*ValidateCommand).Run(0x7f5d60, 0xc82000a070, 0x0, 0x0, 0x6ff3f8)
<autogenerated>:6 +0xab
github.com/mitchellh/cli.(*CLI).Run(0xc8200bc3c0, 0xc820175860, 0x0, 0x0)
/home/vagrant/golang/src/github.com/mitchellh/cli/cli.go:153 +0x538
main.main()
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:207 +0x181
goroutine 5 [runnable]:
os/signal.loop()
/usr/lib/go/src/os/signal/signal_unix.go:20
created by os/signal.init.1
/usr/lib/go/src/os/signal/signal_unix.go:28 +0x37
Using this version a1b2fef9596d0677dff72479f5ce6de9047ee5ad
The docs indicate that MySQL
is the correct value for Engine
. Of course, mysql
works too. Who knows if it's just case-insensitive, or if this is just a special case. I guess since it works, it would be nice if it didn't error.
template:
{
"Resources": {
"MasterDb": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"Engine": "mysql",
"AllocatedStorage": "10",
"DBInstanceClass": "db.m1.medium",
"DBSubnetGroupName": "foo"
}
}
}
}
error:
ERROR Resources.MasterDb.Engine Invalid enum option mysql, expected one of [MySQL, mariadb, oracle-se1, oracle-se, oracle-ee, sqlserver-ee, sqlserver-se, sqlserver-ex, sqlserver-web, postgres, aurora]
version: e7af583dbdc0f7641678070a176c82a7a9ffe996
The docs say MountPoints is not mandatory, but when I leave it out, I get this:
Resources.taskDefinition.ContainerDefinitions.0.MountPoints ... MountPoints is required because it's mandatory
Looks like the issue is in container_definition.go#L49
Currently is expecting an actual Policy object.
There's a couple of places where you can stipulate an AWS account something belongs to. We treat these as ValueString
right now; a more restrictive AwsAccountID
type might be useful.
There's a bunch of places where we're using ValueString
to represent an ARN
.
Tip: grep for "ARN" and "Name" there's a heap of comments and TODOs. Mostly it's in the ReturnValue
of resources marked as // Name
.
Currently Intrinsic Functions can be substituted in place of a value, but this is not strictly always correct. Certain functions are only valid in particular places, especially when nesting them. e.g. You can't use an Fn::GetAtt
within an Fn::FindInMap
.
Function support/restrictions are currently implemented in:
Whilst the documentation[1] suggests an array is required in DBSecurityGroupIngress
, it also supports a map. A template such as this will create successfully in cloudformation:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "test",
"Resources": {
"dbsg": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"DBSecurityGroupIngress": {
"EC2SecurityGroupId": {
"Ref": "sg"
}
},
"EC2VpcId": "vpc-xxxxxx",
"GroupDescription": "xxx"
}
},
"sg": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": "vpc-xxxxxx",
"GroupDescription": "xxx"
}
}
}
}
And it will result in a panic:
panic: interface conversion: interface is map[string]interface {}, not []interface {}
goroutine 1 [running]:
github.com/jagregory/cfval/schema.Schema.Validate(0x1, 0x0, 0x0, 0x0, 0x0, 0x7f75a3707210, 0xc820010310, 0x7f75a37073a8, 0xc82018e940, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/schema.go:63 +0x143
github.com/jagregory/cfval/schema.Properties.Validate(0xc8201748d0, 0x7f75a3707a20, 0xc8200870e0, 0x0, 0x0, 0x0, 0x90)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/properties.go:41 +0xa95
github.com/jagregory/cfval/schema.Resource.Validate(0x6a9f20, 0x19, 0x0, 0xc8201748d0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/resource.go:18 +0xfa
github.com/jagregory/cfval/schema.resourceValidate(0xc82018f720, 0x19, 0xc820175b30, 0x0, 0x7f75a3707980, 0xc820087050, 0xc820087050, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:49 +0x284
github.com/jagregory/cfval/schema.TemplateValidate(0xc82018f5c0, 0x7f75a3707950, 0xc820175740, 0x7f75a3707950, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:15 +0x3c0
main.ValidateCommand.Run(0xc82000a070, 0x0, 0x0, 0xc820173d40)
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:174 +0x788
main.(*ValidateCommand).Run(0x7d3c58, 0xc82000a070, 0x0, 0x0, 0x6e2ff0)
<autogenerated>:6 +0xab
github.com/mitchellh/cli.(*CLI).Run(0xc8200b83c0, 0xc820175830, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/mitchellh/cli/cli.go:153 +0x538
main.main()
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:201 +0x181
goroutine 5 [runnable]:
os/signal.loop()
/usr/lib/go/src/os/signal/signal_unix.go:20
created by os/signal.init.1
/usr/lib/go/src/os/signal/signal_unix.go:28 +0x37
Using cfval
built from dba9c5fc82c1dafbe858ad427872e113be8d013e
Should be able to regex validate igw-15a4417c
.
There are a bunch of non-enforceable "required if being used in a VPC" properties. We can sometimes tell if a resource is in a VPC based on other properties, and sometimes by finding owning resources (e.g. an EC2 Instance in a Subnet which is in a VPC); however, quite often these resources won't be available to us to interrogate because they may be defined elsewhere outside of the template. For this we either need active interrogation (e.g. query AWS) or we can flick a flag to let the user say "yes all these resources are going to be in a VPC, trust me".
For now, the latter -vpc
flag is probably a sensible idea.
The docs for RecordSet don't mention a Comment
property but the samples include one. The RecordSet
properties are shared with the nested-resource RecordSetGroup
whose property docs do include a Comment
property. Mystery!
Check that the attribute in a Fn::GetAtt
call is actually a valid attribute for the resource specified (if we can drill in).
{ "Fn::GetAtt": ["MyEc2Instance", "PublicIp"] } // valid "PublicIp" is an exposed attribute of an AWS::EC2::Instance
{ "Fn::GetAtt": ["MyEc2Instance", "InstanceId"] } // fail "InstanceId" is not exposed
This will require tracking the exposed attributes of each resource, see: Return Values.
Certain functions can return arrays and therefore be used in place of an entire array sub-resource.
"AvailabilityZones": ["ap-southeast-2a","ap-southeast-2b","ap-southeast-2c"]
Can be replaced with:
"AvailabilityZones": { "Ref": "myAvailabilityZones" }
Fn::FindInMap
, Fn::GetAZs
, Fn::GetAtt
, and Ref
(if the attribute/parameter/pseudo-parameter is an array) all can potentially return arrays.
Should be able to regex validate subnet-9d4a7b6c
.
Nat Gateways were recently added to CloudFormation.
AWS::CloudFormation::CustomResource introduces some concepts we don't support right now.
Type
of a resource can be arbitrary (e.g. Custom::*
)Properties
of a resource are a combination of known properties and arbitrary names from the provider of the custom resourceAttributes
are also not known, and from the providerPossible solutions:
AllowUnexpectedProperties
flag on the Resource
, to indicate alternate behaviour when a property isn't found*
property, which behaves the same as above; this might be harder due to the Schema
having an explicit type."ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" }, { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] }
In this case, the Ref is being passed a PropertyContext(ImageId)
and creating a Warning based on a AWS::Region
not being assignable to an InstanceId
, when in actual fact the Ref is just being used as a String in a FindInMap.
This can probably be solved by creating a pseudo-property context when a Intrinsic Function is nested.
The Ref/ReturnValue of a EC2::SecurityGroup is the ID if the SecurityGroup is in a VPC, or the Name if it isn't. This is annoying, because you always get a coercion warning.
Resources.ELB.SecurityGroups.0.Ref ...... Ref value of 'ElbSecurityGroup' is String but is being dangerously coerced to a SecurityGroupID property
Not sure when this happened, but you can no longer pipe JSON from STDIN to cfval
.
This is pretty low priority, as so far there's only one property I've seen documented as replaced
"This property replaces the MinAdjustmentStep property".
MinAdjustmentStep
isn't a known property and will report it as unexpected; however, if we really wanted we could be a bit more sophisticated and say this property is no longer supported and has been superseded by MinAdjustmentMagnitude
.
Below is the status of all the current (as of Feb 2016) AWS resources.
Validations don't run on coerced values - deliberately. An integer validation won't run on a string coerced into an integer property (so a IntegerRange(0, 5)
won't be applied to the value "10"
and thus will pass).
We could or should try converting the value, rather than just warning about a coercion.
Fn::And
- Boolean
Fn::Base64
- String
Fn::Equals
- Boolean
Fn::FindInMap
- String
?Fn::GetAtt
- Attribute TypeFn::GetAZs
- List<AZ>
Fn::If
- Type of arguments if same, else common supertype of argumentsFn::Join
- String
Fn::Not
- Boolean
Fn::Or
- Boolean
Fn::Select
- Array item typeRef
- ReturnValueShould be able to regex vpc-b61106d4
.
Should be able to regex validate sg-94b3a1f6
.
When I enter this template:
{
"Resources": {
"Volume": {
"Type": "AWS::EC2::Volume",
"Properties": {
"VolumeType": "standard"
}
}
}
}
cfval shows this error:
panic: interface conversion: interface is string, not []interface {}
goroutine 1 [running]:
github.com/jagregory/cfval/schema.Schema.Validate(0x1, 0x0, 0x0, 0x0, 0x0, 0x7f84f932a210, 0xc820010310, 0x7f84f932a2b8, 0xc8200da0c0, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/schema.go:63 +0x143
github.com/jagregory/cfval/schema.Properties.Validate(0xc8200da060, 0x7f84f932a980, 0xc820086bd0, 0x0, 0x0, 0x0, 0x90)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/properties.go:41 +0xa95
github.com/jagregory/cfval/schema.Resource.Validate(0x69eb80, 0x10, 0x0, 0xc8200da060, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/resource.go:18 +0xfa
github.com/jagregory/cfval/schema.resourceValidate(0xc82018f590, 0x10, 0xc820175b00, 0x0, 0x7f84f932a8e0, 0xc820086b40, 0xc820086b40, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:49 +0x284
github.com/jagregory/cfval/schema.TemplateValidate(0xc82018d5e0, 0x7f84f932a8b0, 0xc820175740, 0x7f84f932a8b0, 0x0, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/jagregory/cfval/schema/template.go:15 +0x3c0
main.ValidateCommand.Run(0xc82000a070, 0x0, 0x0, 0xc820173d40)
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:174 +0x788
main.(*ValidateCommand).Run(0x7d3c58, 0xc82000a070, 0x0, 0x0, 0x6e2ff0)
<autogenerated>:6 +0xab
github.com/mitchellh/cli.(*CLI).Run(0xc8200b83c0, 0xc820175830, 0x0, 0x0)
/home/vagrant/cfval/src/github.com/mitchellh/cli/cli.go:153 +0x538
main.main()
/home/vagrant/golang/src/github.com/jagregory/cfval/main.go:201 +0x181
goroutine 5 [runnable]:
os/signal.loop()
/usr/lib/go/src/os/signal/signal_unix.go:20
created by os/signal.init.1
/usr/lib/go/src/os/signal/signal_unix.go:28 +0x37
Using version dba9c5fc82c1dafbe858ad427872e113be8d013e
of cfval
.
This is a bit beyond the current functionality, but it could be useful to be able to pass values for Parameters from the CLI to validate them too.
e.g. cfval -param='VpcID="abc"' template.json
Not sure about the syntax.
We can then throw errors if a parameter looks incorrect ("abc" is not a valid VPC ID
).
Should be able to regex vol-4282672b
.
Currently implemented:
Looks like List<String>
is a synonym for CommaDelimitedList
.
Conditions are currently completely unsupported.
The property CacheSubnetGroupName
of AWS::ElastiCache::CacheCluster
has the wrong type of cacheSecurityGroupName
when it should be cacheSubnetGroupName
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.