【问题标题】:Automatically set ListenerRule Priority in CloudFormation template在 CloudFormation 模板中自动设置 ListenerRule 优先级
【发布时间】:2018-10-04 19:33:56
【问题描述】:

我有一个包含 Application Load Balancer ListenerRule 的 CloudFormation 模板。 ListenerRule 的必需属性之一是它的优先级(一个介于 1 和 50000 之间的数字)。每个 ListenerRule 的优先级必须是唯一的。

我需要多次部署同一个模板。每次启动模板时,ListenerRule 的优先级都应该改变。

目前,我已将优先级转换为您可以在启动堆栈时设置的参数,并且效果很好。有没有办法可以自动将 ListenerRule 的优先级设置为下一个可用的优先级?

【问题讨论】:

  • 您对创建 CloudFormation 自定义资源感到满意吗?如果是这样,我可以与您分享我为解决此问题而编写的代码。
  • 我不能说是,因为我认为我从未使用过它们,但我认为该代码无论如何都会非常有用,因为我从未见过解决我所描述的问题的方法。

标签: amazon-web-services amazon-cloudformation elastic-load-balancer


【解决方案1】:

不,目前无法仅使用 AWS::ElasticLoadBalancingV2::ListenerRule 资源自动分配它。但是,可以使用自定义资源来实现。

首先让我们创建实际的自定义资源 Lambda 代码。

allocate_alb_rule_priority.py:

import json
import os
import random
import uuid

import boto3
from botocore.vendored import requests

SUCCESS = "SUCCESS"
FAILED = "FAILED"
# Member must have value less than or equal to 50000
ALB_RULE_PRIORITY_RANGE = 1, 50000


def lambda_handler(event, context):
    try:
        _lambda_handler(event, context)
    except Exception as e:
        # Must raise, otherwise the Lambda will be marked as successful, and the exception
        # will not be logged to CloudWatch logs.
        # Always send a response otherwise custom resource creation/update/deletion will be stuck
        send(
            event,
            context,
            response_status=FAILED if event['RequestType'] != 'Delete' else SUCCESS,
            # Do not fail on delete to avoid rollback failure
            response_data=None,
            physical_resource_id=uuid.uuid4(),
            reason=e,
        )
        raise


def _lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))

    physical_resource_id = event.get('PhysicalResourceId', str(uuid.uuid4()))
    response_data = {}

    if event['RequestType'] == 'Create':
        elbv2_client = boto3.client('elbv2')
        result = elbv2_client.describe_rules(ListenerArn=os.environ['ListenerArn'])

        in_use = list(filter(lambda s: s.isdecimal(), [r['Priority'] for r in result['Rules']]))

        priority = None
        while not priority or priority in in_use:
            priority = str(random.randint(*ALB_RULE_PRIORITY_RANGE))

        response_data = {
            'Priority': priority
        }

    send(event, context, SUCCESS, response_data, physical_resource_id)


def send(event, context, response_status, response_data, physical_resource_id, reason=None):
    response_url = event['ResponseURL']

    response_body = {
        'Status': response_status,
        'Reason': str(reason) if reason else 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
        'PhysicalResourceId': physical_resource_id,
        'StackId': event['StackId'],
        'RequestId': event['RequestId'],
        'LogicalResourceId': event['LogicalResourceId'],
        'Data': response_data,
    }

    json_response_body = json.dumps(response_body)

    headers = {
        'content-type': '',
        'content-length': str(len(json_response_body))
    }

    try:
        requests.put(
            response_url,
            data=json_response_body,
            headers=headers
        )
    except Exception as e:
        print("send(..) failed executing requests.put(..): " + str(e))

根据您的问题,您需要使用相同的模板创建多个堆栈。出于这个原因,我建议将自定义资源放置在仅部署一次的模板中。然后让另一个模板导入它的ServiceToken

allocate_alb_rule_priority_custom_resouce.yml:

Resources:
  AllocateAlbRulePriorityCustomResourceLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: ''
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      Path: /
      Policies:
      - PolicyName: DescribeRulesPolicy
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
          - Effect: Allow
            Action:
            - elasticloadbalancing:DescribeRules
            Resource: "*"
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  AllocateAlbRulePriorityCustomResourceLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: allocate_alb_rule_priority.lambda_handler
      Role: !GetAtt AllocateAlbRulePriorityCustomResourceLambdaRole.Arn
      Code: allocate_alb_rule_priority.py
      Runtime: python3.6
      Timeout: '30'
      Environment:
        Variables:
          ListenerArn: !Ref LoadBalancerListener

Outputs:
  AllocateAlbRulePriorityCustomResourceLambdaArn:
    Value: !GetAtt AllocateAlbRulePriorityCustomResourceLambdaFunction.Arn
    Export:
      Name: AllocateAlbRulePriorityCustomResourceLambdaArn

您会注意到我们将 ListenerArn 传递给 Lambda 函数。这是因为我们想避免新分配的优先级编号冲突。

最后,我们现在可以在要多次部署的模板中使用我们的新自定义资源。

template_meant_to_be_deployed_multiple_times.yml:

  AllocateAlbRulePriorityCustomResource:
    Type: Custom::AllocateAlbRulePriority
    Condition: AutoAllocateAlbPriority
    Properties:
      ServiceToken:
        Fn::ImportValue: AllocateAlbRulePriorityCustomResourceLambdaArn

  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Priority: !GetAtt AllocateAlbRulePriorityCustomResource.Priority
      [...]

这些是 sn-ps,可能无法按原样工作,尽管它们取自工作代码。我希望它能让您大致了解如何实现它。如果您需要更多帮助,请告诉我。

【讨论】:

  • 非常感谢分享这个!我会尽快尝试一下。
  • 我正在使用 cdk python 并定义为你的答案,它有效
  • 这可能是大约 7 年以来第一次使用 Stack Overflow,我复制了一些超过 3 行的代码,它开箱即用。谢谢你救了我这么久。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
相关资源
最近更新 更多