Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
strategy:
fail-fast: false
matrix:
service: [dynamodb, ec2, iam, rds, s3, sqs]
service: [dynamodb, ec2, iam, lambda, rds, s3, sqs]
include:
- service: terraform
path: features/terraform/
Expand Down
19 changes: 12 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,21 +177,26 @@ Use these scopes when relevant:

## Virtual Cloud Integration

InfraSpec can use Virtual Cloud (infraspec-api) instead of real AWS for fast, cost-free testing.
InfraSpec uses Virtual Cloud (the embedded AWS emulator) by default for fast, cost-free testing.

### Running with Virtual Cloud
**Note:** Emulation is the default behavior. Use `--live` flag to test against real AWS.

### Running Tests

```bash
# Use the --virtual-cloud flag
./infraspec features/aws/s3/s3_bucket.feature --virtual-cloud
# Default: Uses embedded emulator (no flag needed)
./infraspec features/aws/s3/s3_bucket.feature

# To test against real AWS, use --live
./infraspec features/aws/s3/s3_bucket.feature --live
```

### How It Works

1. CLI detects `--virtual-cloud` flag
1. CLI starts the embedded AWS emulator by default
2. Configures Terraform with custom AWS endpoints via environment variables
3. All AWS API calls route to Virtual Cloud instead of real AWS
4. Virtual Cloud emulates AWS responses
3. All AWS API calls route to the emulator instead of real AWS
4. Emulator returns AWS-compatible responses

### Service Endpoint Configuration

Expand Down
76 changes: 76 additions & 0 deletions examples/aws/lambda/basic-function/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
provider "aws" {
region = var.region
}

terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.72.1"
}
}
}

# IAM role for Lambda
resource "aws_iam_role" "lambda" {
name = "${var.function_name}-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})

tags = var.tags
}

# Attach basic execution policy
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Lambda function code (inline Python)
data "archive_file" "lambda_zip" {
type = "zip"
output_path = "${path.module}/lambda.zip"

source {
content = <<-EOF
def handler(event, context):
return {
'statusCode': 200,
'body': 'Hello from Lambda!'
}
EOF
filename = "index.py"
}
}

# Lambda function
resource "aws_lambda_function" "main" {
function_name = var.function_name
role = aws_iam_role.lambda.arn
handler = var.handler
runtime = var.runtime
timeout = var.timeout
memory_size = var.memory_size

filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256

environment {
variables = var.environment_variables
}

tags = var.tags
}
29 changes: 29 additions & 0 deletions examples/aws/lambda/basic-function/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
output "function_name" {
description = "Name of the Lambda function"
value = aws_lambda_function.main.function_name
}

output "function_arn" {
description = "ARN of the Lambda function"
value = aws_lambda_function.main.arn
}

output "invoke_arn" {
description = "Invoke ARN of the Lambda function"
value = aws_lambda_function.main.invoke_arn
}

output "role_arn" {
description = "ARN of the IAM role"
value = aws_iam_role.lambda.arn
}

output "runtime" {
description = "Runtime of the Lambda function"
value = aws_lambda_function.main.runtime
}

output "handler" {
description = "Handler of the Lambda function"
value = aws_lambda_function.main.handler
}
50 changes: 50 additions & 0 deletions examples/aws/lambda/basic-function/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}

variable "function_name" {
description = "Name of the Lambda function"
type = string
}

variable "runtime" {
description = "Lambda runtime"
type = string
default = "python3.12"
}

variable "handler" {
description = "Lambda handler"
type = string
default = "index.handler"
}

variable "timeout" {
description = "Function timeout in seconds"
type = number
default = 30
}

variable "memory_size" {
description = "Function memory in MB"
type = number
default = 128
}

variable "environment_variables" {
description = "Environment variables for the function"
type = map(string)
default = {
ENVIRONMENT = "test"
}
}

variable "tags" {
description = "Tags for the resources"
type = map(string)
default = {
ManagedBy = "terraform"
}
}
107 changes: 107 additions & 0 deletions examples/aws/lambda/function-url/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
provider "aws" {
region = var.region
}

terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.72.1"
}
}
}

# IAM role for Lambda
resource "aws_iam_role" "lambda" {
name = "${var.function_name}-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})

tags = var.tags
}

# Attach basic execution policy
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Lambda function code (inline Python)
data "archive_file" "lambda_zip" {
type = "zip"
output_path = "${path.module}/lambda.zip"

source {
content = <<-EOF
import json

def handler(event, context):
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'message': 'Hello from Lambda Function URL!',
'path': event.get('rawPath', '/'),
'method': event.get('requestContext', {}).get('http', {}).get('method', 'GET')
})
}
EOF
filename = "index.py"
}
}

# Lambda function
resource "aws_lambda_function" "main" {
function_name = var.function_name
role = aws_iam_role.lambda.arn
handler = "index.handler"
runtime = "python3.12"
timeout = 30
memory_size = 128

filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256

tags = var.tags
}

# Lambda function URL
resource "aws_lambda_function_url" "main" {
function_name = aws_lambda_function.main.function_name
authorization_type = var.authorization_type

cors {
allow_origins = var.cors_allow_origins
allow_methods = var.cors_allow_methods
allow_headers = ["*"]
expose_headers = ["*"]
max_age = 86400
allow_credentials = false
}
}

# Allow public access when authorization_type is NONE
resource "aws_lambda_permission" "function_url" {
count = var.authorization_type == "NONE" ? 1 : 0

statement_id = "AllowFunctionURLPublicAccess"
action = "lambda:InvokeFunctionUrl"
function_name = aws_lambda_function.main.function_name
principal = "*"
function_url_auth_type = "NONE"
}
24 changes: 24 additions & 0 deletions examples/aws/lambda/function-url/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
output "function_name" {
description = "Name of the Lambda function"
value = aws_lambda_function.main.function_name
}

output "function_arn" {
description = "ARN of the Lambda function"
value = aws_lambda_function.main.arn
}

output "function_url" {
description = "URL of the Lambda function"
value = aws_lambda_function_url.main.function_url
}

output "function_url_id" {
description = "ID of the Lambda function URL"
value = aws_lambda_function_url.main.url_id
}

output "authorization_type" {
description = "Authorization type of the function URL"
value = aws_lambda_function_url.main.authorization_type
}
41 changes: 41 additions & 0 deletions examples/aws/lambda/function-url/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}

variable "function_name" {
description = "Name of the Lambda function"
type = string
}

variable "authorization_type" {
description = "Authorization type for the function URL (NONE or AWS_IAM)"
type = string
default = "NONE"

validation {
condition = contains(["NONE", "AWS_IAM"], var.authorization_type)
error_message = "authorization_type must be either NONE or AWS_IAM"
}
}

variable "cors_allow_origins" {
description = "CORS allowed origins"
type = list(string)
default = ["*"]
}

variable "cors_allow_methods" {
description = "CORS allowed methods"
type = list(string)
default = ["GET", "POST", "PUT", "DELETE"]
}

variable "tags" {
description = "Tags for the resources"
type = map(string)
default = {
ManagedBy = "terraform"
}
}
Loading
Loading