Skip to content
61 changes: 61 additions & 0 deletions integration/combination/test_function_with_capacity_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from unittest.case import skipIf

import pytest

from integration.config.service_names import LAMBDA_MANAGED_INSTANCES
from integration.helpers.base_test import BaseTest
from integration.helpers.resource import current_region_does_not_support


@skipIf(
current_region_does_not_support([LAMBDA_MANAGED_INSTANCES]),
"LambdaManagedInstance is not supported in this testing region",
)
class TestFunctionWithCapacityProvider(BaseTest):
@pytest.fixture(autouse=True)
def companion_stack_outputs(self, get_companion_stack_outputs):
self.companion_stack_outputs = get_companion_stack_outputs

def test_function_with_capacity_provider_custom_role(self):
"""Test Lambda function with CapacityProviderConfig using custom operator role."""
# Phase 1: Prepare parameters from companion stack
parameters = [
self.generate_parameter("SubnetId", self.companion_stack_outputs["LMISubnetId"]),
self.generate_parameter("SecurityGroup", self.companion_stack_outputs["LMISecurityGroupId"]),
self.generate_parameter("KMSKeyArn", self.companion_stack_outputs["LMIKMSKeyArn"]),
]

# Phase 2: Deploy and verify against expected JSON
self.create_and_verify_stack("combination/function_lmi_custom", parameters)

# Phase 3: Verify resource counts
lambda_resources = self.get_stack_resources("AWS::Lambda::Function")
self.assertEqual(len(lambda_resources), 1, "Should create exactly one Lambda function")

capacity_provider_resources = self.get_stack_resources("AWS::Lambda::CapacityProvider")
self.assertEqual(len(capacity_provider_resources), 1, "Should create exactly one CapacityProvider")

iam_role_resources = self.get_stack_resources("AWS::IAM::Role")
self.assertEqual(len(iam_role_resources), 2, "Should create exactly two IAM roles")

def test_function_with_capacity_provider_default_role(self):
"""Test Lambda function with CapacityProviderConfig using default operator role."""
# Phase 1: Prepare parameters from companion stack
parameters = [
self.generate_parameter("SubnetId", self.companion_stack_outputs["LMISubnetId"]),
self.generate_parameter("SecurityGroup", self.companion_stack_outputs["LMISecurityGroupId"]),
self.generate_parameter("KMSKeyArn", self.companion_stack_outputs["LMIKMSKeyArn"]),
]

# Phase 2: Deploy and verify against expected JSON
self.create_and_verify_stack("combination/function_lmi_default", parameters)

# Phase 3: Verify resource counts
lambda_resources = self.get_stack_resources("AWS::Lambda::Function")
self.assertEqual(len(lambda_resources), 1, "Should create exactly one Lambda function")

capacity_provider_resources = self.get_stack_resources("AWS::Lambda::CapacityProvider")
self.assertEqual(len(capacity_provider_resources), 2, "Should create exactly two CapacityProviders")

iam_role_resources = self.get_stack_resources("AWS::IAM::Role")
self.assertEqual(len(iam_role_resources), 3, "Should create exactly three IAM roles")
1 change: 1 addition & 0 deletions integration/config/service_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
STATE_MACHINE_CWE_CWS = "StateMachineCweCws"
STATE_MACHINE_WITH_APIS = "StateMachineWithApis"
LAMBDA_URL = "LambdaUrl"
LAMBDA_MANAGED_INSTANCES = "LambdaManagedInstances"
LAMBDA_ENV_VARS = "LambdaEnvVars"
EVENT_INVOKE_CONFIG = "EventInvokeConfig"
API_KEY = "ApiKey"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"LogicalResourceId": "MyCapacityProvider",
"ResourceType": "AWS::Lambda::CapacityProvider"
},
{
"LogicalResourceId": "MyCapacityProviderCustomRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "MyFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyFunctionRole",
"ResourceType": "AWS::IAM::Role"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"LogicalResourceId": "SimpleCapacityProvider",
"ResourceType": "AWS::Lambda::CapacityProvider"
},
{
"LogicalResourceId": "SimpleCapacityProviderOperatorRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "AdvancedCapacityProvider",
"ResourceType": "AWS::Lambda::CapacityProvider"
},
{
"LogicalResourceId": "AdvancedCapacityProviderOperatorRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "MyFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyFunctionRole",
"ResourceType": "AWS::IAM::Role"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
Parameters:
SubnetId:
Type: String
SecurityGroup:
Type: String
KMSKeyArn:
Type: String

Resources:
MyCapacityProviderCustomRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CapacityProviderOperatorRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:AttachNetworkInterface
- ec2:CreateTags
- ec2:RunInstances
Resource:
- !Sub arn:${AWS::Partition}:ec2:*:*:instance/*
- !Sub arn:${AWS::Partition}:ec2:*:*:network-interface/*
- !Sub arn:${AWS::Partition}:ec2:*:*:volume/*
Condition:
StringEquals:
ec2:ManagedResourceOperator: scaler.lambda.amazonaws.com
- Effect: Allow
Action:
- ec2:DescribeAvailabilityZones
- ec2:DescribeCapacityReservations
- ec2:DescribeInstances
- ec2:DescribeInstanceStatus
- ec2:DescribeInstanceTypeOfferings
- ec2:DescribeInstanceTypes
- ec2:DescribeSecurityGroups
- ec2:DescribeSubnets
Resource: '*'
- Effect: Allow
Action:
- ec2:RunInstances
- ec2:CreateNetworkInterface
Resource:
- !Sub arn:${AWS::Partition}:ec2:*:*:subnet/*
- !Sub arn:${AWS::Partition}:ec2:*:*:security-group/*
- Effect: Allow
Action:
- ec2:RunInstances
Resource:
- !Sub arn:${AWS::Partition}:ec2:*:*:image/*
Condition:
Bool:
ec2:Public: 'true'

MyCapacityProvider:
Type: AWS::Serverless::CapacityProvider
Properties:
CapacityProviderName: !Sub "${AWS::StackName}-cp"
VpcConfig:
SubnetIds:
- !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroup
OperatorRole: !GetAtt MyCapacityProviderCustomRole.Arn

MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Handler: index.handler
CodeUri: ${codeuri}
CapacityProviderConfig:
Arn: !GetAtt MyCapacityProvider.Arn

Metadata:
SamTransformTest: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Parameters:
SubnetId:
Type: String
SecurityGroup:
Type: String
KMSKeyArn:
Type: String

Resources:
SimpleCapacityProvider:
Type: AWS::Serverless::CapacityProvider
Properties:
VpcConfig:
SubnetIds:
- !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroup

MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs22.x
Handler: index.handler
CodeUri: ${codeuri}
CapacityProviderConfig:
Arn: !GetAtt SimpleCapacityProvider.Arn

AdvancedCapacityProvider:
Type: AWS::Serverless::CapacityProvider
Properties:
CapacityProviderName: !Sub "${AWS::StackName}-cp"
VpcConfig:
SubnetIds:
- !Ref SubnetId
SecurityGroupIds:
- !Ref SecurityGroup
InstanceRequirements:
Architectures:
- x86_64
AllowedTypes:
- m5.large
- m5.xlarge
- m5.2xlarge
ScalingConfig:
MaxVCpuCount: 64
AverageCPUUtilization: 70
KmsKeyArn: !Ref KMSKeyArn
Tags:
Environment: Test

Metadata:
SamTransformTest: true
109 changes: 109 additions & 0 deletions integration/setup/companion-stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,106 @@ Resources:
Type: AWS::S3::Bucket
DeletionPolicy: Delete

LMIKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS Key for Lambda Capacity Provider Resource
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root"
Action: kms:*
Resource: '*'
- Sid: Allow Lambda service
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- kms:Decrypt
- kms:DescribeKey
Resource: '*' # Lambda Managed Instances (LMI) VPC Resources

LMIVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-VPC"

LMIPrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref LMIVpc
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs '']
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet"

LMIPrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref LMIVpc
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateRT"

LMIPrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref LMIPrivateSubnet
RouteTableId: !Ref LMIPrivateRouteTable

LMISecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for capacity provider Lambda functions
VpcId: !Ref LMIVpc
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 10.0.0.0/16
Description: Allow HTTPS within VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-LambdaSG"

VPCEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for VPC endpoints
VpcId: !Ref LMIVpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 10.0.0.0/16
Description: Allow HTTPS from within VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-VPCEndpointSG"

CloudWatchLogsEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref LMIVpc
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.logs'
VpcEndpointType: Interface
PrivateDnsEnabled: true
SubnetIds:
- !Ref LMIPrivateSubnet
SecurityGroupIds:
- !Ref VPCEndpointSecurityGroup
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-CloudWatchLogsEndpoint"
Outputs:
PreCreatedVpc:
Description: Pre-created VPC that can be used inside other tests
Expand All @@ -66,5 +166,14 @@ Outputs:
Description: Pre-created S3 Bucket that can be used inside other tests
Value:
Ref: PreCreatedS3Bucket
LMISubnetId:
Description: Private subnet ID for Lambda functions
Value: !Ref LMIPrivateSubnet
LMISecurityGroupId:
Description: Security group ID for Lambda functions
Value: !Ref LMISecurityGroup
LMIKMSKeyArn:
Description: ARN of the KMS key for Capacity Provider
Value: !GetAtt LMIKMSKey.Arn
Metadata:
SamTransformTest: true
Loading