【问题标题】:Adding VPC Peering Routes in CloudFormation across different AWS Accounts在 CloudFormation 中跨不同 AWS 账户添加 VPC 对等路由
【发布时间】:2018-08-02 13:56:45
【问题描述】:

使用这个AWS walkthrough,我可以成功地在不同的aws账户之间添加一个vpc对等连接。

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

这一切都很好,但是在两个 VPC 中都没有路由表条目,这种连接是没有意义的。

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

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

你知道怎么做吗?

【问题讨论】:

  • 我将探索 SNS/Lambda 的方式来做到这一点
  • 我很好奇“在请求者帐户中引用”。我正在尝试通过请求者的 Web 界面自动化我必须作为接受者启动的对等连接。他们还公开了用于发起请求的 api,但我不清楚您上面的陈述,以及我必须如何通知请求者。我希望这是有道理的?

标签: amazon-web-services amazon-cloudformation amazon-vpc


【解决方案1】:

可以从第二个模板中在第一个 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,我才意识到这一点,但后来成功地对其进行了测试。

【讨论】:

    【解决方案2】:

    我可以通过以下组合来做到这一点:

    1. 在接受者帐户中

      • 添加了一个 SNS 主题
      • 添加了一个使用 boto3 创建路由条目的 lambda 函数。它接收对等连接 id 和 CIDR,并将其添加到接受方 VPC 的路由表中
      • 添加了从 SNS 主题触发 lambda 的权限
      • 更新了 PeerRole 以允许跨账户访问 sns:Publish on Arn of the topic
      • 添加了一个主题策略以允许在 sns 主题上跨帐户发布
    2. 在请求者帐户上

      • 添加了一个 lambda,它使用 VPC Peer Id 和 CIDR 向 SNS 主题发送消息

    有点冗长,但这里是接受器模板的 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"
                    }
                }
    

    【讨论】:

      【解决方案3】:

      为了后代,我们一直在使用this 在 VPC 之间创建对等互连。

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

        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' ]]
      

      可以独立存在,也可以嵌套在另一个堆栈中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-13
        • 2017-07-05
        • 2020-08-21
        • 1970-01-01
        • 2020-08-24
        • 2018-02-07
        • 2019-08-10
        • 2018-08-18
        相关资源
        最近更新 更多