VPC에서 CloudFormation을 통해 기본 라우팅 테이블에 경로를 추가하려면 어떻게 해야 합니까?

최종 업데이트 날짜: 2022년 9월 9일

AWS CloudFormation에서 Amazon Virtual Private Cloud(VPC)를 생성할 때 기본 라우팅 테이블에 경로를 추가하고 싶습니다.

간략한 설명

CloudFormation을 사용하여 Amazon VPC를 생성하는 경우 CloudFormation은 기본적으로 생성되는 기본 라우팅 테이블을 인식하지 못합니다. 따라서 Amazon VPC와 CloudFormation 간에 라우팅 테이블에 대한 정보를 전달할 수 없습니다. 그 결과, CloudFormation 템플릿에서 라우팅 테이블을 참조할 수 없으므로 기본 라우팅 테이블에서 경로를 추가하거나 제거할 수 없습니다.

해결 방법

참고: AWS Command Line Interface(AWS CLI) 명령을 실행할 때 오류 메시지가 표시되는 경우 AWS CLI 최신 버전을 사용 중인지 확인합니다.

이 문제는 CloudFormation 템플릿에 AWS Lambda 기반 사용자 지정 리소스를 사용하여 해결할 수 있습니다. CloudFormation 스택은 Amazon VPC를 생성합니다. 그런 다음 사용자 지정 리소스는 AWS Lambda 함수를 사용하여 Amazon VPC와 연결된 기본 라우팅 테이블 ID를 검색합니다.

다음 RouteTable-template.yml 템플릿을 사용하여 CloudFormation 스택을 생성합니다.

AWSTemplateFormatVersion: 2010-09-09
Description: Template to add routes to default/main routetable of VPC
Resources:
  MyVPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Env
          Value: Test
  LambdaIAMRole:
    Type: 'AWS::IAM::Role'
    DependsOn: MyVPC
    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:
                  - 'ec2:Describe*'
                Resource: '*'
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: 'arn:aws:logs:*:*:*'
  LambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaIAMRole.Arn
      Runtime: python3.9
      Timeout: 50
      Code:
        ZipFile: |
            from __future__ import print_function
            import json
            import boto3
            import urllib3
            import cfnresponse

            SUCCESS = "SUCCESS"
            FAILED = "FAILED"

            http = urllib3.PoolManager()

            print('Loading function')
            ec2 = boto3.client('ec2')

            def lambda_handler(event, context):
                print("Received event: " + json.dumps(event, indent=2))
                responseData={}
                try:
                    if event['RequestType'] == 'Delete':
                        print("Request Type:",event['RequestType'])
                        print("Delete Request - No Physical resources to delete")
                    elif event['RequestType'] == 'Create':
                        print("Request Type:",event['RequestType'])
                        VPCID=event['ResourceProperties']['VPCID']
                        RouteTableID=get_vpc(VPCID)
                        responseData={'RouteTableID':RouteTableID}
                        print("Sending response to custom resource")
                    elif event['RequestType'] == 'Update':
                        print("Request Type:",event['RequestType'])
                        VPCID=event['ResourceProperties']['VPCID']
                        RouteTableID=get_vpc(VPCID)
                        responseData={'RouteTableID':RouteTableID}
                        print("Sending response to custom resource")
                    responseStatus = 'SUCCESS'
                    print("responseStatus: " + responseStatus)
                    cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
                except Exception as e:
                    print('Failed to process:', e)
                    responseStatus = 'FAILURE'
                    responseData = {'Failure': 'Something bad happened.'}
                    cfnresponse.send(event, context, cfnresponse.FAILURE, responseData, "CustomResourcePhysicalID")

            def get_vpc(VPCID):
                response = ec2.describe_route_tables (
                  Filters=[
                    {
                      'Name': 'association.main',
                      'Values': [ 'true' ]
                    },
                    {
                      'Name': 'vpc-id',
                      'Values': [ VPCID ]
                    }
                  ]
                )
                print("Printing the VPC Route Table ID ....")
                RouteTableID=response['RouteTables'][0]['RouteTableId']
                print(RouteTableID)
                return RouteTableID

            def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
                responseUrl = event['ResponseURL']
                print(responseUrl)
                responseBody = {'Status': responseStatus,
                                'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
                                'PhysicalResourceId': physicalResourceId or context.log_stream_name,
                                'StackId': event['StackId'],
                                'RequestId': event['RequestId'],
                                'LogicalResourceId': event['LogicalResourceId'],
                                'Data': responseData}
                json_responseBody = json.dumps(responseBody)
                print("Response body:\n" + json_responseBody)
                headers = {
                    'content-type' : '',
                    'content-length' : str(len(json_responseBody))
                }
                try:
                    response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
                    print("Status code: " + response.reason)
                except Exception as e:
                    print("send(..) failed executing requests.put(..): " + str(e))


  Lambdatrigger:
    Type: 'Custom::RouteTableLambda'
    Properties:
      ServiceToken: !GetAtt LambdaFunction.Arn
      VPCID: !Ref MyVPC
  MyInternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Env
          Value: Test
  AttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyInternetGateway
  MyRoute:
    Type: 'AWS::EC2::Route'
    Properties:
      RouteTableId: !GetAtt Lambdatrigger.RouteTableID

      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyInternetGateway
Outputs:
  RouteTableID:
    Value: !GetAtt Lambdatrigger.RouteTableID

사용자 지정 리소스가 반환하는 데이터에는 기본 라우팅 테이블 ID가 포함됩니다. AWS::EC2::Route에서 GetAtt를 사용해 ID를 참조하여 기본 라우팅 테이블에 경로를 추가할 수 있습니다. 또한 스택의 출력에는 라우팅 테이블 ID가 표시됩니다.

다음은 사용자 지정 리소스가 CloudFormation 스택으로 전송하는 SUCCESS 응답 본문의 예입니다.

{
  "Status": "SUCCESS",
  "Reason": "See the details in CloudWatch Log Stream: 2022/08/31/[$LATEST]c48b90efb3944c11ad3fb6e1ce5e1f45",
  "PhysicalResourceId": "CustomResourcePhysicalID",
  "StackId": "arn:aws:cloudformation:us-west-1:XXXX:stack/VPC-RT/06c957b0-297e-11ed-afb5-02ca6fd67f8d",
  "RequestId": "55c0f2b8-3044-47f7-aba4-84502b4ef632",
  "LogicalResourceId": "Lambdatrigger",
  "NoEcho": false,
  "Data": {
    "RouteTableID": "rtb-0fba8d15701234567a"
  }
}

이 문서가 도움이 되었나요?


결제 또는 기술 지원이 필요하세요?