Infrastructure as Code (SAM/CloudFormation)
Why Infrastructure as Code?
Manual setup in AWS Console doesn't scale:
- Hard to reproduce
- Difficult to version
- Error-prone
- Can't automate deployment
Simple Explanation
What it is
Infrastructure as Code means you define cloud resources in files so they can be created and updated the same way every time.
Why we need it
Clicking around the console does not scale for teams. IaC makes environments repeatable and reviewable.
Benefits
- Repeatable setups across dev, staging, and prod.
- Version control for infrastructure changes.
- Faster deployments with fewer manual mistakes.
Tradeoffs
- Learning curve for templates and tooling.
- More upfront structure than ad-hoc console changes.
Real-world examples (architecture only)
- Template defines API + Function + Database in one stack.
- Same template deploys dev and prod with different parameters.
Solution: Define infrastructure in code
# template.yaml
Resources:
MyLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: HelloWorld
Runtime: python3.12
Handler: app.handler
Code:
S3Bucket: my-lambda-code
S3Key: function.zip
This YAML file replaces 10 manual console clicks.
CloudFormation Basics
CloudFormation is AWS's Infrastructure as Code service.
- Describe resources in JSON/YAML
- CloudFormation creates, updates, deletes resources
- Version control your infrastructure
- Reproducible deployments
SAM: Serverless Application Model
SAM is a simplified subset of CloudFormation for serverless apps.
Example SAM Template
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 20
Runtime: python3.12
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handler.handler
CodeUri: src/
Events:
HelloWorldApi:
Type: Api
Properties:
Path: /hello
Method: get
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Items
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: itemId
AttributeType: S
KeySchema:
- AttributeName: itemId
KeyType: HASH
Outputs:
HelloWorldFunctionArn:
Value: !GetAtt HelloWorldFunction.Arn
ApiEndpoint:
Value: !Sub 'https://${ServerlessApi}.execute-api.${AWS::Region}.amazonaws.com/Prod'
Deploying with SAM
Step 1: Install SAM CLI
brew install aws-sam-cli
Step 2: Initialize Project
sam init --runtime python3.12
Step 3: Build
sam build
Packages your code and dependencies.
Step 4: Deploy
sam deploy --guided
Prompts:
- Stack name:
maarifa-app - Region:
us-east-1 - Capabilities: Confirm
SAM creates all resources automatically.
Step 5: Update
sam deploy
Updates existing stack.
Key SAM Resources
Lambda Function
MyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: app.handler
Runtime: python3.12
CodeUri: ./src
Environment:
Variables:
TABLE_NAME: MyTable
Policies:
- DynamoDBCrudPolicy:
TableName: MyTable
API Gateway
Events:
GetItem:
Type: Api
Properties:
Path: /items/{id}
Method: get
RestApiId: !Ref MyApi
DynamoDB Table
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: itemId
AttributeType: S
- AttributeName: createdAt
AttributeType: S
KeySchema:
- AttributeName: itemId
KeyType: HASH
- AttributeName: createdAt
KeyType: RANGE
BillingMode: PAY_PER_REQUEST
S3 Bucket
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-app-bucket-123
Environment Variables
Pass config to Lambda without hardcoding:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
TABLE_NAME: !Ref ItemsTable
BUCKET_NAME: !Ref MyBucket
REGION: !Ref AWS::Region
Access in code:
import os
table_name = os.environ.get("TABLE_NAME")
bucket = os.environ.get("BUCKET_NAME")
Outputs
Export values from stack for reference:
Outputs:
ApiEndpoint:
Value: !GetAtt MyApi.Arn
TableName:
Value: !Ref ItemsTable
FunctionArn:
Value: !GetAtt MyFunction.Arn
Use outputs to:
- Share with other stacks
- Manual reference
- Access in CI/CD pipelines
Parameters
Make templates reusable:
Parameters:
Environment:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]
Resources:
ItemsTable:
Properties:
TableName: !Sub 'Items-${Environment}'
Deploy with parameter:
sam deploy --parameter-overrides Environment=prod
Best Practices
- Version control templates — Use git
- Use parameters — Don't hardcode names
- Organize stacks — One stack per app or component
- Use secrets — Keep sensitive data in AWS Secrets Manager
- Test templates — Validate before deploying
Hands-On: Deploy a Serverless TODO API
Create template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
TodoFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handler.handler
Runtime: python3.12
CodeUri: ./
Events:
CreateTodo:
Type: Api
Properties:
Path: /todos
Method: post
GetTodo:
Type: Api
Properties:
Path: /todos/{id}
Method: get
TodoTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: todos
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
Then:
sam build
sam deploy --guided
All resources created automatically!
Key Takeaway
Infrastructure as Code brings reliability and scalability to AWS deployments. SAM makes it simple for serverless apps.
Project (Cloud-Agnostic)
Define a minimal template that deploys a function and a database table.
Deliverables:
- Describe the resources and their relationships.
- Map the template to AWS (SAM) or GCP (Terraform).
- Explain how parameters make it reusable.
If you want feedback, email your write-up to [email protected].
References
- AWS SAM: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html
- AWS CloudFormation: https://docs.aws.amazon.com/cloudformation/
- Google Cloud Deployment Manager: https://cloud.google.com/deployment-manager/docs
- Terraform Google Provider: https://registry.terraform.io/providers/hashicorp/google/latest/docs