Reputation: 8415
I am receiving the follow error but do not know where it is coming from, hopefully someone can help.
Template validation error: Template format error: Any Properties member must be a JSON object.
The cloudformation script
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway",
"Resources": {
"KMSKey": {
"Type": "AWS::KMS::Key",
"Properties": {
"Description": "KMS Key Dev",
"Enabled": "True",
"EnableKeyRotation": "True",
"KeyPolicy": {
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": {
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
}
}
},
"IamRoleLambda": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "*"
}
},
"IamPolicyLambda": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "dev-lambda",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:*"
},
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": {
"Ref": "KMSKey"
}
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DetachNetworkInterface",
"ec2:DeleteNetworkInterface",
"elastiCache:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::{domain.com}/",
"arn:aws:s3:::{domain.com}/Serverless/*"
]
}
]
},
"Roles": [
{
"Ref": "IamRoleLambda"
}
]
}
},
"RedisCluster": {
"Type": "AWS::ElastiCache::CacheCluster",
"Properties": {
"AutoMinorVersionUpgrade": "False",
"AZMode": "cross-az",
"CacheNodeType": "cache.m3.medium",
"VpcSecurityGroupIds": {
"Ref": "VpcSecurityGroup"
},
"ClusterName": "Dev",
"Engine": "redis",
"EngineVersion": "2.8",
"NumCacheNodes": "1",
"Tags": [
{
"Key": "CostCenter",
"Value": "0000000000000000"
},
{
"Key": "Application",
"Value": "Appname"
},
{
"Key": "Function",
"Value": "cache"
},
{
"Key": "Environment",
"Value": "dev"
}
]
}
},
"VpcSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "DEV VPC Security group form",
"SecurityGroupEgress": {
"Ref": "SecurityGroupEgress"
},
"SecurityGroupInress": {
"Ref": "SecurityGroupIngress"
},
"Tags": [
{
"Key": "CostCenter",
"Value": "0000000000000000"
},
{
"Key": "Application",
"Value": "Appname"
},
{
"Key": "Function",
"Value": "cache"
},
{
"Key": "Environment",
"Value": "dev"
}
],
"VpcId": "vpc-8c3113e2"
}
},
"SecurityGroupEgress": {
"Type": "AWS::EC2::SecurityGroupEgress",
"Properties": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "443",
"ToPort": "443",
"IpProtocol": "tcp"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "80",
"ToPort": "80",
"IpProtocol": "tcp"
},
{
"DestinationSecurityGroupId": {
"Fn:GetAtt": [
"VpcSecurityGroup"
]
},
"FromPort": "6379",
"ToPort": "6379",
"IpProtocol": "tcp"
}
]
},
"SecurityGroupIngress": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "443",
"ToPort": "443",
"IpProtocol": "tcp"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "80",
"ToPort": "80",
"IpProtocol": "tcp"
},
{
"DestinationSecurityGroupId": {
"Fn:GetAtt": [
"VpcSecurityGroup"
]
},
"FromPort": "6379",
"ToPort": "6379",
"IpProtocol": "tcp"
}
]
}
},
"Outputs": {
"IamRoleArnLambda": {
"Description": "ARN of the lambda IAM role",
"Value": {
"Fn::GetAtt": [
"IamRoleLambda",
"Arn"
]
}
}
}
}
Upvotes: 0
Views: 2623
Reputation: 20390
The error describes that the Properties member requires a JSON object. Looking at your code, two of your Properties members are defined as JSON arrays. The underlying issue is that AWS::EC2::SecurityGroupIngress
and AWS::EC2::SecurityGroupEgress
resources define a single security group rule, whereas you're attempting to define multiple rules within a single resource.
I see that you're trying to self-reference the security group for a custom port (6379), in addition to fully-open ports 80 and 443. As noted in the documentation, this is the correct use-case for using AWS::EC2::SecurityGroupEgress
and AWS::EC2::SecurityGroupIngress
resources:
Important
If you want to cross-reference two security groups in the ingress and egress rules of those security groups, use the
AWS::EC2::SecurityGroupEgress
andAWS::EC2::SecurityGroupIngress
resources to define your rules. Do not use the embedded ingress and egress rules in theAWS::EC2::SecurityGroup
. If you do, it causes a circular dependency, which AWS CloudFormation doesn't allow.
To accomplish this without defining several additional resources, you can leave the fully-open port rules inline with the resource (since they don't cause a circular dependency), and just create additional AWS::EC2::SecurityGroup[In|E]gress
resources for the ones you want locked down to the security group:
"VpcSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "DEV VPC Security group form",
"SecurityGroupEgress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "443",
"ToPort": "443",
"IpProtocol": "tcp"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "80",
"ToPort": "80",
"IpProtocol": "tcp"
}
],
"SecurityGroupIngress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "443",
"ToPort": "443",
"IpProtocol": "tcp"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "80",
"ToPort": "80",
"IpProtocol": "tcp"
}
],
"Tags": [
{
"Key": "CostCenter",
"Value": "0000000000000000"
},
{
"Key": "Application",
"Value": "Appname"
},
{
"Key": "Function",
"Value": "cache"
},
{
"Key": "Environment",
"Value": "dev"
}
],
"VpcId": "vpc-8c3113e2"
}
},
"SecurityGroupEgress": {
"Type": "AWS::EC2::SecurityGroupEgress",
"Properties": {
"DestinationSecurityGroupId": {
"Fn::GetAtt": [
"VpcSecurityGroup",
"GroupId"
]
},
"FromPort": "6379",
"ToPort": "6379",
"IpProtocol": "tcp"
}
},
"SecurityGroupIngress": {
"Type": "AWS::EC2::SecurityGroupIngress",
"Properties": {
"SourceSecurityGroupId": {
"Fn::GetAtt": [
"VpcSecurityGroup",
"GroupId"
]
},
"FromPort": "6379",
"ToPort": "6379",
"IpProtocol": "tcp"
}
}
[...etc...]
You'll also want to make sure to be careful with the spelling and syntax of the Fn::GetAtt function, which I've corrected above for you.
Upvotes: 1