The following template uses an AWS Lambda (Lambda) function and custom resource to append a new security group to a list of existing security groups. This function is useful when you want to build a list of security groups dynamically, so that your list includes both new and existing security groups. For example, you can pass a list of existing security groups as a parameter value, append the new value to the list, and then associate all your values with an EC2 instance. For more information about the Lambda function resource type, see AWS::Lambda::Function.
In the example, when CloudFormation creates the AllSecurityGroups
custom resource, CloudFormation invokes the
AppendItemToListFunction
Lambda function. CloudFormation passes the list of existing security groups and a new
security group (NewSecurityGroup
) to the function, which appends the new security group to the list and
then returns the modified list. CloudFormation uses the modified list to associate all security groups with the
MyEC2Instance
resource.
JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"ExistingSecurityGroups": {
"Type": "List<AWS::EC2::SecurityGroup::Id>"
},
"ExistingVPC": {
"Type": "AWS::EC2::VPC::Id",
"Description": "The VPC ID that includes the security groups in the ExistingSecurityGroups parameter."
},
"InstanceType": {
"Type": "String",
"Default": "t2.micro",
"AllowedValues": [
"t2.micro",
"m1.small"
]
}
},
"Mappings": {
"AWSInstanceType2Arch": {
"t2.micro": {
"Arch": "HVM64"
},
"m1.small": {
"Arch": "HVM64"
}
},
"AWSRegionArch2AMI": {
"us-east-1": {
"HVM64": "ami-0ff8a91507f77f867",
"HVMG2": "ami-0a584ac55a7631c0c"
},
"us-west-2": {
"HVM64": "ami-a0cfeed8",
"HVMG2": "ami-0e09505bc235aa82d"
},
"us-west-1": {
"HVM64": "ami-0bdb828fd58c52235",
"HVMG2": "ami-066ee5fd4a9ef77f1"
},
"eu-west-1": {
"HVM64": "ami-047bb4163c506cd98",
"HVMG2": "ami-0a7c483d527806435"
},
"eu-central-1": {
"HVM64": "ami-0233214e13e500f77",
"HVMG2": "ami-06223d46a6d0661c7"
},
"ap-northeast-1": {
"HVM64": "ami-06cd52961ce9f0d85",
"HVMG2": "ami-053cdd503598e4a9d"
},
"ap-southeast-1": {
"HVM64": "ami-08569b978cc4dfa10",
"HVMG2": "ami-0be9df32ae9f92309"
},
"ap-southeast-2": {
"HVM64": "ami-09b42976632b27e9b",
"HVMG2": "ami-0a9ce9fecc3d1daf8"
},
"sa-east-1": {
"HVM64": "ami-07b14488da8ea02a0",
"HVMG2": "NOT_SUPPORTED"
},
"cn-north-1": {
"HVM64": "ami-0a4eaf6c4454eda75",
"HVMG2": "NOT_SUPPORTED"
}
}
},
"Resources": {
"SecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Allow HTTP traffic to the host",
"VpcId": {
"Ref": "ExistingVPC"
},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"CidrIp": "0.0.0.0/0"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"CidrIp": "0.0.0.0/0"
}
]
}
},
"AllSecurityGroups": {
"Type": "Custom::Split",
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"AppendItemToListFunction",
"Arn"
]
},
"List": {
"Ref": "ExistingSecurityGroups"
},
"AppendedItem": {
"Ref": "SecurityGroup"
}
}
},
"AppendItemToListFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"",
[
"var response = require('cfn-response');",
"exports.handler = function(event, context) {",
" var responseData = {Value: event.ResourceProperties.List};",
" responseData.Value.push(event.ResourceProperties.AppendedItem);",
" response.send(event, context, response.SUCCESS, responseData);",
"};"
]
]
}
},
"Runtime": "nodejs20.x"
}
},
"MyEC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
{
"Fn::FindInMap": [
"AWSInstanceType2Arch",
{
"Ref": "InstanceType"
},
"Arch"
]
}
]
},
"SecurityGroupIds": {
"Fn::GetAtt": [
"AllSecurityGroups",
"Value"
]
},
"InstanceType": {
"Ref": "InstanceType"
}
}
},
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
}
]
}
}
},
"Outputs": {
"AllSecurityGroups": {
"Description": "Security Groups that are associated with the EC2 instance",
"Value": {
"Fn::Join": [
", ",
{
"Fn::GetAtt": [
"AllSecurityGroups",
"Value"
]
}
]
}
}
}
}
YAML
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
ExistingSecurityGroups:
Type: List<AWS::EC2::SecurityGroup::Id>
ExistingVPC:
Type: AWS::EC2::VPC::Id
Description: The VPC ID that includes the security groups in the ExistingSecurityGroups parameter.
InstanceType:
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- m1.small
Mappings:
AWSInstanceType2Arch:
t2.micro:
Arch: HVM64
m1.small:
Arch: HVM64
AWSRegionArch2AMI:
us-east-1:
HVM64: ami-0ff8a91507f77f867
HVMG2: ami-0a584ac55a7631c0c
us-west-2:
HVM64: ami-a0cfeed8
HVMG2: ami-0e09505bc235aa82d
us-west-1:
HVM64: ami-0bdb828fd58c52235
HVMG2: ami-066ee5fd4a9ef77f1
eu-west-1:
HVM64: ami-047bb4163c506cd98
HVMG2: ami-0a7c483d527806435
eu-central-1:
HVM64: ami-0233214e13e500f77
HVMG2: ami-06223d46a6d0661c7
ap-northeast-1:
HVM64: ami-06cd52961ce9f0d85
HVMG2: ami-053cdd503598e4a9d
ap-southeast-1:
HVM64: ami-08569b978cc4dfa10
HVMG2: ami-0be9df32ae9f92309
ap-southeast-2:
HVM64: ami-09b42976632b27e9b
HVMG2: ami-0a9ce9fecc3d1daf8
sa-east-1:
HVM64: ami-07b14488da8ea02a0
HVMG2: NOT_SUPPORTED
cn-north-1:
HVM64: ami-0a4eaf6c4454eda75
HVMG2: NOT_SUPPORTED
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP traffic to the host
VpcId: !Ref ExistingVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
AllSecurityGroups:
Type: Custom::Split
Properties:
ServiceToken: !GetAtt AppendItemToListFunction.Arn
List: !Ref ExistingSecurityGroups
AppendedItem: !Ref SecurityGroup
AppendItemToListFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: !Join
- ''
- - var response = require('cfn-response');
- exports.handler = function(event, context) {
- ' var responseData = {Value: event.ResourceProperties.List};'
- ' responseData.Value.push(event.ResourceProperties.AppendedItem);'
- ' response.send(event, context, response.SUCCESS, responseData);'
- '};'
Runtime: nodejs20.x
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !FindInMap
- AWSRegionArch2AMI
- !Ref AWS::Region
- !FindInMap
- AWSInstanceType2Arch
- !Ref InstanceType
- Arch
SecurityGroupIds: !GetAtt AllSecurityGroups.Value
InstanceType: !Ref InstanceType
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
Outputs:
AllSecurityGroups:
Description: Security Groups that are associated with the EC2 instance
Value: !Join
- ', '
- !GetAtt AllSecurityGroups.Value