665 lines
15 KiB
Markdown
665 lines
15 KiB
Markdown
---
|
|
description: Set up Infrastructure as Code for Rust Lambda functions using SAM, Terraform, or CDK
|
|
---
|
|
|
|
You are helping the user set up Infrastructure as Code (IaC) for their Rust Lambda functions.
|
|
|
|
## Your Task
|
|
|
|
Guide the user through deploying and managing Lambda infrastructure using their preferred IaC tool.
|
|
|
|
## Infrastructure as Code Options
|
|
|
|
### Option 1: AWS SAM (Serverless Application Model)
|
|
|
|
**Best for**:
|
|
- Serverless-focused projects
|
|
- Quick prototyping
|
|
- Built-in local testing
|
|
- Teams familiar with CloudFormation
|
|
|
|
**Advantages**:
|
|
- Official AWS support for Rust with cargo-lambda
|
|
- Built-in local testing with `sam local`
|
|
- Simpler for pure serverless applications
|
|
- Good integration with Lambda features
|
|
|
|
#### Basic SAM Template
|
|
|
|
Create `template.yaml`:
|
|
|
|
```yaml
|
|
AWSTemplateFormatVersion: '2010-09-09'
|
|
Transform: AWS::Serverless-2016-10-31
|
|
Description: Rust Lambda Function
|
|
|
|
Globals:
|
|
Function:
|
|
Timeout: 30
|
|
MemorySize: 512
|
|
Runtime: provided.al2023
|
|
Architectures:
|
|
- arm64
|
|
Environment:
|
|
Variables:
|
|
RUST_LOG: info
|
|
|
|
Resources:
|
|
MyRustFunction:
|
|
Type: AWS::Serverless::Function
|
|
Metadata:
|
|
BuildMethod: rust-cargolambda
|
|
BuildProperties:
|
|
Binary: my-function
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
Events:
|
|
ApiEvent:
|
|
Type: Api
|
|
Properties:
|
|
Path: /hello
|
|
Method: get
|
|
Policies:
|
|
- AWSLambdaBasicExecutionRole
|
|
|
|
ComputeFunction:
|
|
Type: AWS::Serverless::Function
|
|
Metadata:
|
|
BuildMethod: rust-cargolambda
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
MemorySize: 2048
|
|
Timeout: 300
|
|
Events:
|
|
S3Event:
|
|
Type: S3
|
|
Properties:
|
|
Bucket: !Ref ProcessingBucket
|
|
Events: s3:ObjectCreated:*
|
|
|
|
ProcessingBucket:
|
|
Type: AWS::S3::Bucket
|
|
|
|
Outputs:
|
|
ApiUrl:
|
|
Description: "API Gateway endpoint URL"
|
|
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
|
|
```
|
|
|
|
#### SAM Commands
|
|
|
|
```bash
|
|
# Build
|
|
sam build
|
|
|
|
# Test locally
|
|
sam local invoke MyRustFunction -e events/test.json
|
|
|
|
# Start local API
|
|
sam local start-api
|
|
|
|
# Deploy
|
|
sam deploy --guided
|
|
|
|
# Deploy with parameters
|
|
sam deploy \
|
|
--stack-name my-rust-lambda \
|
|
--capabilities CAPABILITY_IAM \
|
|
--region us-east-1
|
|
```
|
|
|
|
#### Multi-Function SAM Template
|
|
|
|
```yaml
|
|
AWSTemplateFormatVersion: '2010-09-09'
|
|
Transform: AWS::Serverless-2016-10-31
|
|
|
|
Globals:
|
|
Function:
|
|
Runtime: provided.al2023
|
|
Architectures:
|
|
- arm64
|
|
Environment:
|
|
Variables:
|
|
RUST_LOG: info
|
|
|
|
Resources:
|
|
# API Handler - IO-optimized
|
|
ApiHandler:
|
|
Type: AWS::Serverless::Function
|
|
Metadata:
|
|
BuildMethod: rust-cargolambda
|
|
BuildProperties:
|
|
Binary: api-handler
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
MemorySize: 512
|
|
Timeout: 30
|
|
Events:
|
|
GetUsers:
|
|
Type: Api
|
|
Properties:
|
|
Path: /users
|
|
Method: get
|
|
Environment:
|
|
Variables:
|
|
DATABASE_URL: !Sub "{{resolve:secretsmanager:${DBSecret}:SecretString:connection_string}}"
|
|
Policies:
|
|
- AWSLambdaBasicExecutionRole
|
|
- AWSSecretsManagerGetSecretValuePolicy:
|
|
SecretArn: !Ref DBSecret
|
|
|
|
# Data Processor - Compute-optimized
|
|
DataProcessor:
|
|
Type: AWS::Serverless::Function
|
|
Metadata:
|
|
BuildMethod: rust-cargolambda
|
|
BuildProperties:
|
|
Binary: data-processor
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
MemorySize: 3008
|
|
Timeout: 300
|
|
Events:
|
|
S3Upload:
|
|
Type: S3
|
|
Properties:
|
|
Bucket: !Ref DataBucket
|
|
Events: s3:ObjectCreated:*
|
|
Filter:
|
|
S3Key:
|
|
Rules:
|
|
- Name: prefix
|
|
Value: raw/
|
|
Policies:
|
|
- AWSLambdaBasicExecutionRole
|
|
- S3ReadPolicy:
|
|
BucketName: !Ref DataBucket
|
|
- S3WritePolicy:
|
|
BucketName: !Ref DataBucket
|
|
|
|
# Event Consumer - SQS triggered
|
|
EventConsumer:
|
|
Type: AWS::Serverless::Function
|
|
Metadata:
|
|
BuildMethod: rust-cargolambda
|
|
BuildProperties:
|
|
Binary: event-consumer
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
MemorySize: 1024
|
|
Timeout: 60
|
|
Events:
|
|
SQSEvent:
|
|
Type: SQS
|
|
Properties:
|
|
Queue: !GetAtt EventQueue.Arn
|
|
BatchSize: 10
|
|
Policies:
|
|
- AWSLambdaBasicExecutionRole
|
|
- SQSPollerPolicy:
|
|
QueueName: !GetAtt EventQueue.QueueName
|
|
|
|
DataBucket:
|
|
Type: AWS::S3::Bucket
|
|
|
|
EventQueue:
|
|
Type: AWS::SQS::Queue
|
|
Properties:
|
|
VisibilityTimeout: 360
|
|
|
|
DBSecret:
|
|
Type: AWS::SecretsManager::Secret
|
|
Properties:
|
|
Description: Database connection string
|
|
GenerateSecretString:
|
|
SecretStringTemplate: '{"username": "admin"}'
|
|
GenerateStringKey: "password"
|
|
PasswordLength: 32
|
|
|
|
Outputs:
|
|
ApiEndpoint:
|
|
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
|
|
DataBucket:
|
|
Value: !Ref DataBucket
|
|
QueueUrl:
|
|
Value: !Ref EventQueue
|
|
```
|
|
|
|
### Option 2: Terraform
|
|
|
|
**Best for**:
|
|
- Multi-cloud or hybrid infrastructure
|
|
- Complex infrastructure requirements
|
|
- Teams already using Terraform
|
|
- More control over AWS resources
|
|
|
|
**Advantages**:
|
|
- Broader ecosystem (300+ providers)
|
|
- State management
|
|
- Module reusability
|
|
- Better for mixed workloads (Lambda + EC2 + RDS, etc.)
|
|
|
|
#### Basic Terraform Configuration
|
|
|
|
Create `main.tf`:
|
|
|
|
```hcl
|
|
terraform {
|
|
required_version = ">= 1.0"
|
|
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = "~> 5.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "aws" {
|
|
region = var.aws_region
|
|
}
|
|
|
|
# IAM Role for Lambda
|
|
resource "aws_iam_role" "lambda_role" {
|
|
name = "${var.function_name}-role"
|
|
|
|
assume_role_policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Action = "sts:AssumeRole"
|
|
Effect = "Allow"
|
|
Principal = {
|
|
Service = "lambda.amazonaws.com"
|
|
}
|
|
}]
|
|
})
|
|
}
|
|
|
|
resource "aws_iam_role_policy_attachment" "lambda_basic" {
|
|
role = aws_iam_role.lambda_role.name
|
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
}
|
|
|
|
# Lambda Function
|
|
resource "aws_lambda_function" "rust_function" {
|
|
filename = "target/lambda/${var.function_name}/bootstrap.zip"
|
|
function_name = var.function_name
|
|
role = aws_iam_role.lambda_role.arn
|
|
handler = "bootstrap"
|
|
source_code_hash = filebase64sha256("target/lambda/${var.function_name}/bootstrap.zip")
|
|
runtime = "provided.al2023"
|
|
architectures = ["arm64"]
|
|
memory_size = var.memory_size
|
|
timeout = var.timeout
|
|
|
|
environment {
|
|
variables = {
|
|
RUST_LOG = var.log_level
|
|
}
|
|
}
|
|
|
|
tracing_config {
|
|
mode = "Active"
|
|
}
|
|
}
|
|
|
|
# CloudWatch Log Group
|
|
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
name = "/aws/lambda/${var.function_name}"
|
|
retention_in_days = 14
|
|
}
|
|
|
|
# API Gateway (Optional)
|
|
resource "aws_apigatewayv2_api" "lambda_api" {
|
|
name = "${var.function_name}-api"
|
|
protocol_type = "HTTP"
|
|
}
|
|
|
|
resource "aws_apigatewayv2_stage" "lambda_stage" {
|
|
api_id = aws_apigatewayv2_api.lambda_api.id
|
|
name = "prod"
|
|
auto_deploy = true
|
|
}
|
|
|
|
resource "aws_apigatewayv2_integration" "lambda_integration" {
|
|
api_id = aws_apigatewayv2_api.lambda_api.id
|
|
integration_type = "AWS_PROXY"
|
|
integration_uri = aws_lambda_function.rust_function.invoke_arn
|
|
}
|
|
|
|
resource "aws_apigatewayv2_route" "lambda_route" {
|
|
api_id = aws_apigatewayv2_api.lambda_api.id
|
|
route_key = "GET /hello"
|
|
target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}"
|
|
}
|
|
|
|
resource "aws_lambda_permission" "api_gateway" {
|
|
statement_id = "AllowAPIGatewayInvoke"
|
|
action = "lambda:InvokeFunction"
|
|
function_name = aws_lambda_function.rust_function.function_name
|
|
principal = "apigateway.amazonaws.com"
|
|
source_arn = "${aws_apigatewayv2_api.lambda_api.execution_arn}/*/*"
|
|
}
|
|
|
|
# Outputs
|
|
output "function_arn" {
|
|
value = aws_lambda_function.rust_function.arn
|
|
}
|
|
|
|
output "api_endpoint" {
|
|
value = aws_apigatewayv2_stage.lambda_stage.invoke_url
|
|
}
|
|
```
|
|
|
|
Create `variables.tf`:
|
|
|
|
```hcl
|
|
variable "aws_region" {
|
|
description = "AWS region"
|
|
type = string
|
|
default = "us-east-1"
|
|
}
|
|
|
|
variable "function_name" {
|
|
description = "Lambda function name"
|
|
type = string
|
|
}
|
|
|
|
variable "memory_size" {
|
|
description = "Lambda memory size in MB"
|
|
type = number
|
|
default = 512
|
|
}
|
|
|
|
variable "timeout" {
|
|
description = "Lambda timeout in seconds"
|
|
type = number
|
|
default = 30
|
|
}
|
|
|
|
variable "log_level" {
|
|
description = "Rust log level"
|
|
type = string
|
|
default = "info"
|
|
}
|
|
```
|
|
|
|
#### Terraform Module for Rust Lambda
|
|
|
|
Create `modules/rust-lambda/main.tf`:
|
|
|
|
```hcl
|
|
resource "aws_iam_role" "lambda_role" {
|
|
name = "${var.function_name}-role"
|
|
|
|
assume_role_policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Action = "sts:AssumeRole"
|
|
Effect = "Allow"
|
|
Principal = {
|
|
Service = "lambda.amazonaws.com"
|
|
}
|
|
}]
|
|
})
|
|
}
|
|
|
|
resource "aws_iam_role_policy_attachment" "lambda_basic" {
|
|
role = aws_iam_role.lambda_role.name
|
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
}
|
|
|
|
resource "aws_lambda_function" "function" {
|
|
filename = var.zip_file
|
|
function_name = var.function_name
|
|
role = aws_iam_role.lambda_role.arn
|
|
handler = "bootstrap"
|
|
source_code_hash = filebase64sha256(var.zip_file)
|
|
runtime = "provided.al2023"
|
|
architectures = [var.architecture]
|
|
memory_size = var.memory_size
|
|
timeout = var.timeout
|
|
|
|
environment {
|
|
variables = var.environment_variables
|
|
}
|
|
|
|
dynamic "vpc_config" {
|
|
for_each = var.vpc_config != null ? [var.vpc_config] : []
|
|
content {
|
|
subnet_ids = vpc_config.value.subnet_ids
|
|
security_group_ids = vpc_config.value.security_group_ids
|
|
}
|
|
}
|
|
|
|
tracing_config {
|
|
mode = var.enable_xray ? "Active" : "PassThrough"
|
|
}
|
|
}
|
|
|
|
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
name = "/aws/lambda/${var.function_name}"
|
|
retention_in_days = var.log_retention_days
|
|
}
|
|
```
|
|
|
|
Usage:
|
|
|
|
```hcl
|
|
module "api_handler" {
|
|
source = "./modules/rust-lambda"
|
|
|
|
function_name = "api-handler"
|
|
zip_file = "target/lambda/api-handler/bootstrap.zip"
|
|
memory_size = 512
|
|
timeout = 30
|
|
architecture = "arm64"
|
|
enable_xray = true
|
|
log_retention_days = 7
|
|
|
|
environment_variables = {
|
|
RUST_LOG = "info"
|
|
DATABASE_URL = data.aws_secretsmanager_secret_version.db.secret_string
|
|
}
|
|
}
|
|
|
|
module "data_processor" {
|
|
source = "./modules/rust-lambda"
|
|
|
|
function_name = "data-processor"
|
|
zip_file = "target/lambda/data-processor/bootstrap.zip"
|
|
memory_size = 3008
|
|
timeout = 300
|
|
architecture = "arm64"
|
|
enable_xray = true
|
|
log_retention_days = 7
|
|
|
|
environment_variables = {
|
|
RUST_LOG = "info"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Terraform Commands
|
|
|
|
```bash
|
|
# Initialize
|
|
terraform init
|
|
|
|
# Plan
|
|
terraform plan -var="function_name=my-rust-lambda"
|
|
|
|
# Apply
|
|
terraform apply -var="function_name=my-rust-lambda" -auto-approve
|
|
|
|
# Destroy
|
|
terraform destroy -var="function_name=my-rust-lambda"
|
|
```
|
|
|
|
### Option 3: AWS CDK (TypeScript/Python)
|
|
|
|
**Best for**:
|
|
- Type-safe infrastructure definitions
|
|
- Complex constructs and patterns
|
|
- Teams comfortable with programming languages
|
|
- Reusable infrastructure components
|
|
|
|
#### CDK Example (TypeScript)
|
|
|
|
```typescript
|
|
import * as cdk from 'aws-cdk-lib';
|
|
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
import * as apigateway from 'aws-cdk-lib/aws-apigatewayv2';
|
|
import * as integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
|
|
export class RustLambdaStack extends cdk.Stack {
|
|
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
|
|
super(scope, id, props);
|
|
|
|
const rustFunction = new lambda.Function(this, 'RustFunction', {
|
|
runtime: lambda.Runtime.PROVIDED_AL2023,
|
|
handler: 'bootstrap',
|
|
code: lambda.Code.fromAsset('target/lambda/my-function/bootstrap.zip'),
|
|
architecture: lambda.Architecture.ARM_64,
|
|
memorySize: 512,
|
|
timeout: cdk.Duration.seconds(30),
|
|
environment: {
|
|
RUST_LOG: 'info',
|
|
},
|
|
tracing: lambda.Tracing.ACTIVE,
|
|
});
|
|
|
|
const api = new apigateway.HttpApi(this, 'RustApi', {
|
|
defaultIntegration: new integrations.HttpLambdaIntegration(
|
|
'RustIntegration',
|
|
rustFunction
|
|
),
|
|
});
|
|
|
|
new cdk.CfnOutput(this, 'ApiUrl', {
|
|
value: api.url!,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
## Comparison Table
|
|
|
|
| Feature | SAM | Terraform | CDK |
|
|
|---------|-----|-----------|-----|
|
|
| Learning Curve | Low | Medium | Medium-High |
|
|
| Rust Support | Excellent | Good | Good |
|
|
| Local Testing | Built-in | Limited | Limited |
|
|
| Multi-Cloud | No | Yes | No |
|
|
| Type Safety | No | HCL | Yes |
|
|
| Community | AWS-focused | Large | Growing |
|
|
| State Management | CloudFormation | Terraform State | CloudFormation |
|
|
|
|
## Integration with cargo-lambda
|
|
|
|
All IaC tools work well with cargo-lambda:
|
|
|
|
```bash
|
|
# Build for deployment
|
|
cargo lambda build --release --arm64 --output-format zip
|
|
|
|
# Then deploy with your IaC tool
|
|
sam deploy
|
|
# or
|
|
terraform apply
|
|
# or
|
|
cdk deploy
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Version Control**: Store IaC templates in Git
|
|
2. **Separate Environments**: Use workspaces/stages for dev/staging/prod
|
|
3. **Secrets Management**: Use AWS Secrets Manager, never hardcode
|
|
4. **Outputs**: Export important values (ARNs, URLs)
|
|
5. **Modules**: Create reusable components
|
|
6. **Testing**: Validate templates before deployment
|
|
7. **CI/CD**: Automate IaC deployment
|
|
8. **State Management**: Secure Terraform state (S3 + DynamoDB)
|
|
9. **Documentation**: Comment complex configurations
|
|
10. **Tagging**: Tag resources for cost tracking
|
|
|
|
## Local Testing with SAM
|
|
|
|
```bash
|
|
# Test function locally
|
|
sam local invoke MyRustFunction -e events/test.json
|
|
|
|
# Start local API Gateway
|
|
sam local start-api
|
|
|
|
# Start local Lambda endpoint
|
|
sam local start-lambda
|
|
|
|
# Generate sample events
|
|
sam local generate-event apigateway aws-proxy > event.json
|
|
sam local generate-event s3 put > s3-event.json
|
|
```
|
|
|
|
## Using with LocalStack
|
|
|
|
For full local AWS emulation:
|
|
|
|
```bash
|
|
# Install LocalStack
|
|
pip install localstack
|
|
|
|
# Start LocalStack
|
|
localstack start
|
|
|
|
# Deploy to LocalStack with SAM
|
|
samlocal deploy
|
|
|
|
# Or with Terraform
|
|
terraform apply \
|
|
-var="aws_region=us-east-1" \
|
|
-var="endpoint=http://localhost:4566"
|
|
```
|
|
|
|
## Migration Path
|
|
|
|
**Starting fresh**:
|
|
- Choose SAM for pure serverless, simple projects
|
|
- Choose Terraform for complex, multi-service infrastructure
|
|
- Choose CDK for type-safe, programmatic definitions
|
|
|
|
**Existing infrastructure**:
|
|
- Import existing resources into Terraform/CDK
|
|
- Use CloudFormation template generation from SAM
|
|
- Gradual migration with hybrid approach
|
|
|
|
## Recommended Structure
|
|
|
|
```
|
|
my-rust-lambda/
|
|
├── src/
|
|
│ └── main.rs
|
|
├── Cargo.toml
|
|
├── template.yaml # SAM
|
|
├── terraform/ # Terraform
|
|
│ ├── main.tf
|
|
│ ├── variables.tf
|
|
│ └── outputs.tf
|
|
├── cdk/ # CDK
|
|
│ ├── lib/
|
|
│ │ └── stack.ts
|
|
│ └── bin/
|
|
│ └── app.ts
|
|
└── events/ # Test events
|
|
├── api-event.json
|
|
└── s3-event.json
|
|
```
|
|
|
|
Help the user choose the right IaC tool based on their needs and guide them through setup and deployment.
|