简体   繁体   中英

CloudFormation - Assign a security group to a vpc dynamically based on a vpc tag value

I have an organization account with several managed accounts underneath it. Each managed account has multiple VPCs in them. One of the VPC in each managed account will have a tag "ServiceName":"True" while the others in that account will have a "ServiceName":"False" tag instead.

I'm trying to create a stackset with a stack dedicated to create a security group with ingress rules attached to it and I need to dynamically assign the "VpcId" property of that security group to be the "VpcId" of VPC with the "ServiceName":"True" tag in that account.

Obviously, if I don't specify a VPC ID in the VpcId field, it creates the security group but attach it to the default VPC of that account. I can't specify manually a VPC either since it's going to be ran in multiple accounts. Leaving me with the only option available to search and assign VPCs by running some sort of function to extract the "VpcId".

The stack itself works fine as I ran it in a test environment while specifying a VPC ID. So, it's just a matter getting that "VpcId" dynamically.

In the end, I'm looking to do something that would resemble this:

{
"Parameters": {
    "MyValidVPCID": {
        "Description": "My Valid VPC ID where ServiceName tag equals true. Do some Lambda Kung Fu to get the VPC ID using something that would let me parse the equivalent of aws ec2 describe-vpcs command.",
        "Type": "String"
    }
},
"Resources": {
    "SG": {
        "Type": "AWS::EC2::SecurityGroup",
        "Properties": {
            "GroupDescription": "Security Group Desc.",
            "Tags": [
                {
                    "Key": "Key1",
                    "Value": "ABC"
                },
                {
                    "Key": "Key2",
                    "Value": "DEF"
                }
            ],
    "VpcId" : { "Ref" : "MyValidVPCID" }
        }
    },
    "SGIngressRule01":
    {
        "Type": "AWS::EC2::SecurityGroupIngress",
        "DependsOn": "SG",
        "Properties": {
            "GroupId" : { "Fn::GetAtt": [ "SG", "GroupId" ] },
            "Description": "Rule 1 description",
            "IpProtocol": "tcp",
            "FromPort": 123,
            "ToPort": 456,
            "CidrIp": "0.0.0.0/0"
        }
    }
}

I really don't know if it's a feasible approach or what would be the extra steps needed to recuperate that VpcId based on the tag. That's why if I could get some input from people used to work with CloudFormation, it would help me a lot.

getting that "VpcId" dynamically.

You have to use custom resource for that. You would have to create it as a lambda function which would take any input arguments you want, and using AWS SDK, would query or modify the VPC/Security groups in your stack.

Thanks Marcin for pointing me in the right direction with the custom resources. For those who are wondering what the basic code to make it work looks like, it looks something like this:

Resources:

  FunctionNameLambdaFunctionRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: FunctionNameLambdaFunctionRole
      Path: "/"
      AssumeRolePolicyDocument:
          Version: "2012-10-17"
          Statement:
              - Effect: Allow
                Principal:
                  Service: lambda.amazonaws.com
                Action: sts:AssumeRole
  
  FunctionNameLambdaFunctionRolePolicy:
    Type: "AWS::IAM::Policy"
    Properties:
        PolicyName: admin3cx
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "*"
                Resource: "*"
        Roles:
          - Ref: FunctionNameLambdaFunctionRole

  FunctionNameLambdaFunctionCode:
    Type: "AWS::Lambda::Function"
    DeletionPolicy: Delete
    DependsOn:
      - FunctionNameLambdaFunctionRole
    Properties:
        FunctionName: FunctionNameLambdaFunctionCode
        Role: !GetAtt FunctionNameLambdaFunctionRole.Arn
        Runtime: python3.7
        Handler: index.handler
        MemorySize: 128
        Timeout: 30
        Code:
          ZipFile: |
            import boto3
            import cfnresponse
            ec2 = boto3.resource('ec2')
            client = boto3.client('ec2')
            def handler(event, context):
              responseData = {}
              filters =[{'Name':'tag:ServiceName', 'Values':['True']}]
              vpcs = list(ec2.vpcs.filter(Filters=filters))
              for vpc in vpcs:
                responseVPC = vpc.id
              responseData['ServiceName'] = responseVPC
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")

  FunctionNameLambdaFunctionInvocationCode:
    Type: "Custom::FunctionNameLambdaFunctionInvocationCode"
    Properties:
      ServiceToken: !GetAtt FunctionNameLambdaFunctionCode.Arn

  SGFunctionName:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: Description
      VpcId: !GetAtt FunctionNameLambdaFunctionInvocationCode.ServiceName
      
   ...

Some stuff has been redacted and I made the switch to YAML. The code will be refined obviously. The point was just to make sure I was able to get a return value based on a filter in a Lambda function inside a CloudFormation stack.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM