繁体   English   中英

在 CloudFormation 中跨不同 AWS 账户添加 VPC 对等路由

[英]Adding VPC Peering Routes in CloudFormation across different AWS Accounts

使用此AWS 演练,我可以成功地在不同的 aws 帐户之间添加 vpc 对等连接。

连接被自动接受,因为接受者账户中的 IAM 角色设置被授予该权限,并在请求连接时在请求者账户中引用。

这一切都很好,但是如果两个 VPC 中都没有路由表条目,这种连接就毫无意义。

查看示例中的第二个模板; 创建AWS::EC2::VPCPeeringConnection ,有没有办法在第一个模板中创建的 VPC 的路由表条目中添加路由?

我当然可以将路由表 id 传递给第二个模板,但我认为这还不够。 我认为帐户之间必须有额外的信任关系才能允许这样做。

关于如何做到这一点的任何想法?

我能够使用以下组合来做到这一点:

1) In the accepter account
 -- added an SNS topic
 -- added a lambda function that uses boto3 to create the route entry.  It receives the peering connection id and CIDR and adds it to the route table of the accepter VPC
 -- added a permission to tirgger the lambda from the SNS topic
 -- updated the PeerRole to allow cross account access to sns:Publish on Arn of the topic
  -- added a topic policy to allow cross account publish on sns topic

2) On the requester account
  -- added a lambda that sends a message to the SNS topic with the VPC Peer Id, and the CIDR

这有点冗长,但这里是接受者模板的 json 的一部分

"AcceptVPCPeerLambdaExecutionRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "Path": "/",
                "Policies": [
                    {
                        "PolicyName": "CrossAccountVPCPeering",
                        "PolicyDocument": {
                            "Statement": [
                            {
                                "Action": [
                                    "logs:CreateLogGroup",
                                    "logs:CreateLogStream",
                                    "logs:GetLogEvents",
                                    "logs:PutLogEvents",
                                ],
                                "Resource": [ "arn:aws:logs:*:*:*" ],
                                "Effect": "Allow"
                            },
                            {
                                "Effect":"Allow",
                                "Action":["ec2:*Route*"],
                                "Resource":"*"
                            }
                        ]
                        }
                    }
                ],
                "AssumeRolePolicyDocument": {
                    "Statement": [
                    {
                        "Action": [ "sts:AssumeRole" ],
                        "Effect": "Allow",
                        "Principal": {
                          "Service": [ "lambda.amazonaws.com" ]
                        }
                    }]
                }
            }
        },
        "AcceptVPCPeerLambdaFunction": {
            "DependsOn": ["AcceptVPCPeerLambdaExecutionRole"],
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Code": {
                    "ZipFile" : { "Fn::Join" : ["\n", [
                          "import json",
                          "import boto3",
                          "import logging",
                          "logger = logging.getLogger()",
                          "logger.setLevel(logging.INFO)",
                          "def handler(event, context):",
                          "    message = json.loads(event['Records'][0]['Sns']['Message'])",
                          "    #logger.info('got event {}'.format(event))",
                          "    logger.info('message {}'.format(message))",
                          "    client = boto3.client('ec2')",
                          "    response = client.create_route(",
                          "        DestinationCidrBlock=message.get('destCidrBlock'),",
                          "        VpcPeeringConnectionId=message.get('ReqVpcPeeringId'),",
                          {"Fn::Sub" : ["        RouteTableId='${RouteTableId}'", {"RouteTableId" : {"Ref" : "PrivateRouteTable"}}]},
                          "    )",
                          "    logger.info('response code is {} '.format(",
                          "       response['Return']",
                          "    ))",

                        ]]
                    }
                },
                "Description": "Accept A VPC Peering Connection From Requested By Another Account",
                "MemorySize": 128,
                "Handler": "index.handler",
                "Role": {
                    "Fn::GetAtt": [ "AcceptVPCPeerLambdaExecutionRole", "Arn" ]
                },
                "Timeout": 300,
                "Runtime": "python2.7"
            }
        },
        "AcceptVPCPeerSNSTopic": {
            "DependsOn": [ "AcceptVPCPeerLambdaFunction" ],
            "Type": "AWS::SNS::Topic",
            "Properties": {
                "Subscription": [{
                    "Endpoint": {"Fn::GetAtt": [ "AcceptVPCPeerLambdaFunction", "Arn" ]},
                    "Protocol": "lambda"
                }]
            }
        },
"SNSTopicPolicy" : {
            "Type" : "AWS::SNS::TopicPolicy",
            "Properties" :{
                "PolicyDocument" : {
                    "Version":"2012-10-17",
                    "Id":"AWSAccountTopicAccess",
                    "Statement" :[
                        {
                            "Sid":"allow-publish-vpc-peering",
                            "Effect":"Allow",           
                            "Principal" :{
                            "AWS": {"Ref": "PeerRequesterAccounts"}
                        },
                        "Action":["sns:Publish"],
                        "Resource" : "*"
                        }
                    ]
                },
                "Topics" : [ {"Ref" : "AcceptVPCPeerSNSTopic"}]
            }
        }

请求者模板的 Lambda 是

"VpcPeeringConnection": {
            "Type": "AWS::EC2::VPCPeeringConnection",
            "DependsOn" : ["VPC"],
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "PeerVpcId": {
                    "Ref": "PeerVPCId"
                },
                "PeerOwnerId": {
                    "Ref": "PeerVPCAccountId"
                },
                "PeerRoleArn": {
                    "Ref": "PeerRoleArn"
                },
                "Tags" : [
                    {"Key" : "Name", "Value" : "DevOps Account To VPN Account"}
                ]
            }
        },
"RequesterVPCPeerLambdaFunction": {
            "DependsOn": ["RequesterVPCPeerLambdaExecutionRole", "VPC", "VpcPeeringConnection"],
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Code": {
                    "ZipFile" : { "Fn::Join" : ["\n", [
                          "import json",
                          "import boto3",
                          "import cfnresponse",
                          "def handler(event, context):",
                          "    message = {",
                          { "Fn::Sub": [ " 'ReqVpcPeeringId' : '${VpcPeeringId}',", { "VpcPeeringId": {"Ref" : "VpcPeeringConnection" }} ]},
                          { "Fn::Sub": [ " 'destCidrBlock' : '${destCidrBlock}'", { "destCidrBlock": {"Ref" : "TestPrivateSubnet1Cidr" }} ]},
                          "    }",
                          "    client = boto3.client('sns')",
                          "    response = client.publish(",
                          { "Fn::Sub": [ "        TargetArn='${TargetArn}',", { "TargetArn": {"Ref" : "AcceptVPCPeerSNSTopicArn" }} ]},
                          "        Message=json.dumps({'default': json.dumps(message)}),",
                          "        MessageStructure='json'",
                          "    )"
                        ]]
                    }
                },
                "Description": "Lambda Function To Publish the VPC Peering Connection Id to The VPN Accepter SNS Topic",
                "MemorySize": 128,
                "Handler": "index.handler",
                "Role": {
                    "Fn::GetAtt": [ "RequesterVPCPeerLambdaExecutionRole", "Arn" ]
                },
                "Timeout": 300,
                "Runtime": "python2.7"
            }
        }

可以从第二个模板中的第一个 VPC 中创建路由表条目。 您可能包含在第二个模板中的相关 CloudFormation 资源示例:

Resources:
  IsolationVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.1.0.0/16"

  PrimaryPrivateSubnet:
    DependsOn:
      - IsolationVPC
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: IsolationVPC
      AvailabilityZone: ${self:provider.region}a
      CidrBlock: 10.1.1.0/24
  PrimaryPrivateSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: IsolationVPC
    DependsOn:
      - IsolationVPC

  PrimaryPublicSubnet:
    DependsOn:
      - IsolationVPC
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: IsolationVPC
      AvailabilityZone: ${self:provider.region}a
      CidrBlock: 10.1.2.0/24
  PrimaryPublicSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: IsolationVPC
    DependsOn:
      - IsolationVPC

  PeeringConnection:
    Type: AWS::EC2::VPCPeeringConnection
    DependsOn:
      - IsolationVPC
    Properties: 
      PeerVpcId: <first VPC ID goes here>
      VpcId:
        Ref: IsolationVPC
  PublicRoutingTableEntry:
    Type: AWS::EC2::Route
    DependsOn:
      - PrimaryPublicSubnetRouteTable
      - PeeringConnection
    Properties:
      RouteTableId:
        Ref: PrimaryPublicSubnetRouteTable
      DestinationCidrBlock: <first VPC CIDR block goes here>
      VpcPeeringConnectionId:
        Ref: PeeringConnection
  PrivateRoutingTableEntry:
    Type: AWS::EC2::Route
    DependsOn:
      - PrimaryPrivateSubnetRouteTable
      - PeeringConnection
    Properties:
      RouteTableId:
        Ref: PrimaryPrivateSubnetRouteTable
      DestinationCidrBlock: <first VPC CIDR block goes here>
      VpcPeeringConnectionId:
        Ref: PeeringConnection
  ReversePublicRoutingTableEntry:
    Type: AWS::EC2::Route
    DependsOn:
      - PeeringConnection
    Properties:
      RouteTableId: <first VPC public route table ID goes here>
      DestinationCidrBlock: 10.1.0.0/16
      VpcPeeringConnectionId:
        Ref: PeeringConnection
  ReversePrivateRoutingTableEntry:
    Type: AWS::EC2::Route
    DependsOn:
      - PeeringConnection
    Properties:
      RouteTableId: <first VPC private route table ID goes here>
      DestinationCidrBlock: 10.1.0.0/16
      VpcPeeringConnectionId:
        Ref: PeeringConnection

直到我阅读了此处提供的示例后才意识到这一点: https : //github.com/lizduke/cloudformationexamples但此后已成功对其进行了测试。

对于后代,我们一直在使用来创建 VPC 之间的对等互连。

它使用我们的通用自定义资源提供程序来创建远程对等路由和标签,并可选择授权进入远程安全组(例如):

  RemotePeeringRoute:
    Type: 'Custom::CreatePeeringRoute'
    Version: 1.0
    DependsOn: PeeringConnection
    Properties:
      ServiceToken: !Sub 'arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:generic-custom-resource-provider'
      RoleArn: !Sub 'arn:${AWS::Partition}:iam::${TargetAccountId}:role/VPCPeeringRole'
      AgentService: ec2
      AgentType: client
      AgentRegion: !Sub '${TargetRegion}'
      AgentCreateMethod: create_route
      AgentDeleteMethod: delete_route
      AgentCreateArgs:
        DestinationCidrBlock: !Sub '${RequesterCidrBlock.CidrBlock}'
        RouteTableId: !Select [ 0, !Split [ ',', !Ref 'TargetRouteTableIds' ]]
        VpcPeeringConnectionId: !Sub '${PeeringConnection}'
      AgentDeleteArgs:
        DestinationCidrBlock: !Sub '${RequesterCidrBlock.CidrBlock}'
        RouteTableId: !Select [ 0, !Split [ ',', !Ref 'TargetRouteTableIds' ]]

可以独立站立或嵌套在另一个堆栈中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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