← SCP Explorer

Lambda function protection

Prevents unauthorized deletion or modification of Lambda functions, ensuring that critical application components remain intact.

Policy description

What this SCP does

This Service Control Policy (SCP) prevents unauthorized deletion or modification of Lambda functions, ensuring that critical application components remain intact. This is important for maintaining serverless application reliability.

By restricting changes to Lambda functions with the "Prod-" prefix to only users with the Operations department tag, this policy creates a separation of duties that protects production serverless workloads from unauthorized or accidental modifications, while still allowing flexibility for non-production environments.

Validation strategy

How to test this SCP works

To validate this SCP, try to create and modify Lambda functions with different naming patterns and roles.

  • Valid test: Create Lambda functions with both "Prod-" and other prefixes
  • Valid test: Create IAM roles with and without the Operations department tag
  • Expected result: Updates to "Prod-" prefixed functions should succeed when performed by roles tagged with Department=Operations, but fail for other roles, while updates to non-"Prod-" functions should succeed for all roles

This testing approach confirms that only properly tagged roles can modify production Lambda functions, while all other principals are prevented from changing these critical serverless resources.

SCP policy & validation scenarios

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyLambdaFunctionModification", "Effect": "Deny", "Action": [ "lambda:DeleteFunction", "lambda:UpdateFunctionCode", "lambda:UpdateFunctionConfiguration" ], "Resource": "arn:aws:lambda:*:*:function:Prod-*", "Condition": { "StringNotEquals": { "aws:PrincipalTag/Department": "Operations" } } } ] }
# Create a test Lambda function with Prod- prefix aws lambda create-function \ --function-name Prod-TestFunction \ --runtime python3.9 \ --role arn:aws:iam::123456789012:role/lambda-execution-role \ --handler index.handler \ --zip-file fileb://function.zip # Create a test Lambda function without Prod- prefix aws lambda create-function \ --function-name Dev-TestFunction \ --runtime python3.9 \ --role arn:aws:iam::123456789012:role/lambda-execution-role \ --handler index.handler \ --zip-file fileb://function.zip # Try to update the Prod- function (this should be denied without proper tag) aws lambda update-function-code \ --function-name Prod-TestFunction \ --zip-file fileb://updated-function.zip # Try to update the Dev- function (this should be allowed) aws lambda update-function-code \ --function-name Dev-TestFunction \ --zip-file fileb://updated-function.zip
import boto3 import json import io import zipfile # Initialize clients lambda_client = boto3.client('lambda') iam_client = boto3.client('iam') # Create test resources try: # Create a simple Lambda function code zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w') as zip_file: zip_file.writestr('index.py', """ def handler(event, context): print('Hello from Lambda!') return { 'statusCode': 200, 'body': 'Success' } """) zip_buffer.seek(0) # Create IAM roles trust_policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole" } ] } # Create operations role with required tag ops_role = iam_client.create_role( RoleName='OpsLambdaRole', AssumeRolePolicyDocument=json.dumps(trust_policy), Tags=[ { 'Key': 'Department', 'Value': 'Operations' } ] ) ops_role_arn = ops_role['Role']['Arn'] print(f"Created operations role: {ops_role_arn}") # Create regular role without required tag dev_role = iam_client.create_role( RoleName='DevLambdaRole', AssumeRolePolicyDocument=json.dumps(trust_policy) ) dev_role_arn = dev_role['Role']['Arn'] print(f"Created developer role: {dev_role_arn}") # Attach Lambda execution policy to roles lambda_policy_arn = 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' iam_client.attach_role_policy( RoleName='OpsLambdaRole', PolicyArn=lambda_policy_arn ) iam_client.attach_role_policy( RoleName='DevLambdaRole', PolicyArn=lambda_policy_arn ) # Create Lambda functions prod_function = lambda_client.create_function( FunctionName='Prod-TestFunction', Runtime='python3.9', Role=ops_role_arn, Handler='index.handler', Code={'ZipFile': zip_buffer.read()} ) print(f"Created production Lambda function: {prod_function['FunctionName']}") # Reset zip buffer position zip_buffer.seek(0) dev_function = lambda_client.create_function( FunctionName='Dev-TestFunction', Runtime='python3.9', Role=dev_role_arn, Handler='index.handler', Code={'ZipFile': zip_buffer.read()} ) print(f"Created development Lambda function: {dev_function['FunctionName']}") print("Note: To fully test this SCP, you'd need to:") print("1. Assume each role and attempt to modify or delete the Lambda functions") print("2. Operations should succeed with OpsLambdaRole for any function") print("3. Operations should fail with DevLambdaRole for Prod- functions but succeed for Dev- functions") # Clean up try: lambda_client.delete_function(FunctionName='Prod-TestFunction') lambda_client.delete_function(FunctionName='Dev-TestFunction') print("Deleted Lambda functions") except Exception as e: print(f"Error deleting Lambda functions: {e}") try: iam_client.detach_role_policy( RoleName='OpsLambdaRole', PolicyArn=lambda_policy_arn ) iam_client.detach_role_policy( RoleName='DevLambdaRole', PolicyArn=lambda_policy_arn ) iam_client.delete_role(RoleName='OpsLambdaRole') iam_client.delete_role(RoleName='DevLambdaRole') print("Deleted IAM roles") except Exception as e: print(f"Error deleting IAM roles: {e}") except Exception as e: print(f"Error during test setup: {e}")