¿Cómo agrego rutas a la tabla de enrutamiento principal de mi VPC con CloudFormation?

4 minutos de lectura
0

Quiero añadir rutas a la tabla de enrutamiento principal al crear mi Amazon Virtual Private Cloud (Amazon VPC) en AWS CloudFormation.

Descripción corta

Cuando usa CloudFormation para crear una Amazon VPC, CloudFormation no reconoce la tabla de enrutamiento principal que se crea de forma predeterminada. Por lo tanto, la información sobre la tabla de enrutamiento no se puede transferir entre Amazon VPC y CloudFormation. Como resultado, no puede añadir ni eliminar rutas de la tabla de rutas principal porque no puede hacer referencia a la tabla de enrutamiento de su plantilla de CloudFormation.

Resolución

Nota: Si recibe errores al ejecutar los comandos de la Interfaz de la línea de comandos de AWS (AWS CLI), asegúrese de utilizar la versión más reciente de la AWS CLI.

Para resolver este problema, puede utilizar un recurso personalizado y respaldado por AWS Lambda en la plantilla de CloudFormation. La pila de CloudFormation crea una Amazon VPC. A continuación, el recurso personalizado utiliza una función de AWS Lambda para recuperar el ID de la tabla de enrutamiento principal asociado a su Amazon VPC.

Cree una pila de CloudFormation con la siguiente plantilla RouteTable-Template.yml:

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

Los datos que devuelve el recurso personalizado contienen el ID de la tabla de enrutamiento principal. Puede hacer referencia al ID mediante GetATT en AWS::EC2::Route para añadir rutas a la tabla de enrutamiento principal. Además, las salidas de la pila muestran el ID de la tabla de enrutamiento.

A continuación se muestra un ejemplo del cuerpo de la respuesta SUCCESS que el recurso personalizado envía a la pila de CloudFormation:

{
  "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"
  }
}

OFICIAL DE AWS
OFICIAL DE AWSActualizada hace 2 años