No it's currently not possible to have it automatically allocated using only the AWS::ElasticLoadBalancingV2::ListenerRule
resource. However, it can be achieved using a custom resource.
First let's create the actual custom resource Lambda code.
allocate_alb_rule_priority.py:
import json
import os
import random
import uuid
import boto3
import urllib3
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(json.dumps(event))
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']
http = urllib3.PoolManager()
body = {
'Status': response_status,
'Reason': reason or '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,
}
encoded_body = json.dumps(body).encode('utf-8')
headers = {
'content-type': '',
'content-length': str(len(encoded_body)),
}
http.request('PUT', response_url, body=encoded_body, headers=headers)
According to your question, you need to create multiple stacks with the same template. For that reason I suggest the Custom Resource is placed within a template that is deployed only once. Then have the other template import its 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.8
Timeout: '30'
Environment:
Variables:
ListenerArn: !Ref LoadBalancerListener
Outputs:
AllocateAlbRulePriorityCustomResourceLambdaArn:
Value: !GetAtt AllocateAlbRulePriorityCustomResourceLambdaFunction.Arn
Export:
Name: AllocateAlbRulePriorityCustomResourceLambdaArn
You can notice that we're passing a ListenerArn to the Lambda function. It's because we want to avoid priority number collision on new allocation.
Lastly, we can now use our new custom resource in the template that is meant to be deployed multiple times.
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
[...]
These are snippets and may not work as-is, although they were taken from working code. I hope it gives you a general idea of how it can be achieved. Let me know if you need more help.