AWS Tutorial
Learn how to leverage Amazon Web Services (AWS) for building, deploying, and scaling modern applications.
Overview
Amazon Web Services (AWS) is a comprehensive cloud computing platform that provides a wide range of services including computing power, storage, databases, networking, and more.
Getting Started with AWS
AWS Account Setup
Create an AWS Account
- Visit aws.amazon.com
- Click "Create an AWS Account"
- Follow the registration process
Set up Billing Alerts
json{ "AlarmName": "BillingAlarm", "AlarmDescription": "Alarm when charges exceed $10", "MetricName": "EstimatedCharges", "Namespace": "AWS/Billing", "Statistic": "Maximum", "Period": 86400, "EvaluationPeriods": 1, "Threshold": 10.0, "ComparisonOperator": "GreaterThanThreshold" }
Configure AWS CLI
bash# Install AWS CLI pip install awscli # Configure credentials aws configure # AWS Access Key ID: YOUR_ACCESS_KEY # AWS Secret Access Key: YOUR_SECRET_KEY # Default region name: us-east-1 # Default output format: json
IAM (Identity and Access Management)
Creating Users and Roles
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
Best Practices
- Use least privilege principle
- Enable MFA for root account
- Rotate access keys regularly
- Use roles for EC2 instances
Core AWS Services
EC2 (Elastic Compute Cloud)
Launching an Instance
bash
# Create key pair
aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem
chmod 400 MyKeyPair.pem
# Launch instance
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--count 1 \
--instance-type t2.micro \
--key-name MyKeyPair \
--security-group-ids sg-903004f8 \
--subnet-id subnet-6e7f829e
User Data Script
bash
#!/bin/bash
yum update -y
yum install -y docker
service docker start
usermod -a -G docker ec2-user
# Install Node.js
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install node
# Clone and run application
git clone https://github.com/user/myapp.git
cd myapp
npm install
npm start
Auto Scaling Group
json
{
"AutoScalingGroupName": "my-asg",
"LaunchConfigurationName": "my-launch-config",
"MinSize": 1,
"MaxSize": 5,
"DesiredCapacity": 2,
"VPCZoneIdentifier": "subnet-12345678,subnet-87654321",
"HealthCheckType": "ELB",
"HealthCheckGracePeriod": 300,
"Tags": [
{
"Key": "Name",
"Value": "MyApp",
"PropagateAtLaunch": true
}
]
}
S3 (Simple Storage Service)
Creating and Managing Buckets
bash
# Create bucket
aws s3 mb s3://my-unique-bucket-name
# Upload file
aws s3 cp myfile.txt s3://my-unique-bucket-name/
# Sync directory
aws s3 sync ./dist s3://my-unique-bucket-name/ --delete
# Set bucket policy
aws s3api put-bucket-policy --bucket my-unique-bucket-name --policy file://bucket-policy.json
Bucket Policy for Static Website
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-unique-bucket-name/*"
}
]
}
S3 with CloudFront
yaml
# cloudformation-template.yml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${AWS::StackName}-website'
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: !GetAtt S3Bucket.DomainName
Id: S3Origin
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${OriginAccessIdentity}'
Enabled: true
DefaultRootObject: index.html
DefaultCacheBehavior:
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
Compress: true
ForwardedValues:
QueryString: false
Cookies:
Forward: none
RDS (Relational Database Service)
Creating a Database Instance
bash
# Create DB subnet group
aws rds create-db-subnet-group \
--db-subnet-group-name myapp-subnet-group \
--db-subnet-group-description "Subnet group for MyApp" \
--subnet-ids subnet-12345678 subnet-87654321
# Create database instance
aws rds create-db-instance \
--db-instance-identifier myapp-db \
--db-instance-class db.t3.micro \
--engine postgres \
--engine-version 13.7 \
--master-username admin \
--master-user-password mypassword \
--allocated-storage 20 \
--db-subnet-group-name myapp-subnet-group \
--vpc-security-group-ids sg-12345678
Database Connection
javascript
// database.js
const { Pool } = require('pg')
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
module.exports = pool
Lambda (Serverless Functions)
Creating a Lambda Function
javascript
// lambda-function.js
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2))
try {
// Process the event
const result = await processEvent(event)
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(result)
}
} catch (error) {
console.error('Error:', error)
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({ error: 'Internal Server Error' })
}
}
}
async function processEvent(event) {
// Your business logic here
return { message: 'Success', data: event }
}
Deployment with SAM
yaml
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 30
Runtime: nodejs16.x
Environment:
Variables:
NODE_ENV: production
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: index.handler
Events:
Api:
Type: Api
Properties:
Path: /hello
Method: get
MyTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: String
BillingMode: PAY_PER_REQUEST
Outputs:
ApiUrl:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
API Gateway
REST API Configuration
yaml
# api-gateway.yml
Resources:
MyApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: MyAPI
Description: My REST API
MyResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref MyApi
ParentId: !GetAtt MyApi.RootResourceId
PathPart: users
MyMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref MyApi
ResourceId: !Ref MyResource
HttpMethod: GET
AuthorizationType: NONE
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations'
Container Services
ECS (Elastic Container Service)
Task Definition
json
{
"family": "myapp-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "myapp",
"image": "myapp:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
ECS Service
json
{
"serviceName": "myapp-service",
"cluster": "myapp-cluster",
"taskDefinition": "myapp-task:1",
"desiredCount": 2,
"launchType": "FARGATE",
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": ["subnet-12345678", "subnet-87654321"],
"securityGroups": ["sg-12345678"],
"assignPublicIp": "ENABLED"
}
},
"loadBalancers": [
{
"targetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/myapp-tg/1234567890123456",
"containerName": "myapp",
"containerPort": 3000
}
]
}
EKS (Elastic Kubernetes Service)
Cluster Creation
bash
# Install eksctl
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
# Create cluster
eksctl create cluster \
--name myapp-cluster \
--version 1.24 \
--region us-east-1 \
--nodegroup-name standard-workers \
--node-type t3.medium \
--nodes 3 \
--nodes-min 1 \
--nodes-max 4 \
--managed
Kubernetes Deployment
yaml
# k8s-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: production
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
Monitoring and Logging
CloudWatch
Custom Metrics
javascript
// cloudwatch-metrics.js
const AWS = require('aws-sdk')
const cloudwatch = new AWS.CloudWatch()
async function putMetric(metricName, value, unit = 'Count') {
const params = {
Namespace: 'MyApp',
MetricData: [
{
MetricName: metricName,
Value: value,
Unit: unit,
Timestamp: new Date()
}
]
}
try {
await cloudwatch.putMetricData(params).promise()
console.log(`Metric ${metricName} sent successfully`)
} catch (error) {
console.error('Error sending metric:', error)
}
}
// Usage
putMetric('UserRegistrations', 1)
putMetric('ResponseTime', 150, 'Milliseconds')
Log Groups and Streams
bash
# Create log group
aws logs create-log-group --log-group-name /aws/lambda/myapp
# Create log stream
aws logs create-log-stream \
--log-group-name /aws/lambda/myapp \
--log-stream-name $(date +%Y/%m/%d)/instance-1
X-Ray Tracing
javascript
// x-ray-tracing.js
const AWSXRay = require('aws-xray-sdk-core')
const AWS = AWSXRay.captureAWS(require('aws-sdk'))
// Capture HTTP requests
const express = require('express')
const app = express()
app.use(AWSXRay.express.openSegment('MyApp'))
app.get('/api/users', async (req, res) => {
const segment = AWSXRay.getSegment()
const subsegment = segment.addNewSubsegment('database-query')
try {
const users = await getUsersFromDatabase()
subsegment.close()
res.json(users)
} catch (error) {
subsegment.addError(error)
subsegment.close()
res.status(500).json({ error: 'Internal Server Error' })
}
})
app.use(AWSXRay.express.closeSegment())
Security Best Practices
VPC Configuration
yaml
# vpc-template.yml
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [0, !GetAZs '']
InternetGateway:
Type: AWS::EC2::InternetGateway
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
Security Groups
json
{
"GroupName": "myapp-sg",
"Description": "Security group for MyApp",
"VpcId": "vpc-12345678",
"SecurityGroupRules": [
{
"IpProtocol": "tcp",
"FromPort": 80,
"ToPort": 80,
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": 443,
"ToPort": 443,
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22,
"CidrIp": "10.0.0.0/16"
}
]
}
Secrets Manager
javascript
// secrets-manager.js
const AWS = require('aws-sdk')
const secretsManager = new AWS.SecretsManager()
async function getSecret(secretName) {
try {
const result = await secretsManager.getSecretValue({
SecretId: secretName
}).promise()
return JSON.parse(result.SecretString)
} catch (error) {
console.error('Error retrieving secret:', error)
throw error
}
}
// Usage
const dbCredentials = await getSecret('myapp/database')
const dbConfig = {
host: dbCredentials.host,
username: dbCredentials.username,
password: dbCredentials.password,
database: dbCredentials.database
}
Cost Optimization
Reserved Instances
bash
# Purchase Reserved Instance
aws ec2 purchase-reserved-instances-offering \
--reserved-instances-offering-id 12345678-1234-1234-1234-123456789012 \
--instance-count 1
Spot Instances
json
{
"SpotFleetRequestConfig": {
"IamFleetRole": "arn:aws:iam::account:role/fleet-role",
"AllocationStrategy": "lowestPrice",
"TargetCapacity": 2,
"SpotPrice": "0.05",
"LaunchSpecifications": [
{
"ImageId": "ami-12345678",
"InstanceType": "t3.medium",
"KeyName": "my-key-pair",
"SecurityGroups": [
{
"GroupId": "sg-12345678"
}
],
"SubnetId": "subnet-12345678"
}
]
}
}
Auto Scaling Policies
json
{
"PolicyName": "scale-up-policy",
"AutoScalingGroupName": "myapp-asg",
"PolicyType": "TargetTrackingScaling",
"TargetTrackingConfiguration": {
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": 70.0,
"ScaleOutCooldown": 300,
"ScaleInCooldown": 300
}
}
Infrastructure as Code
CloudFormation
yaml
# infrastructure.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'MyApp Infrastructure'
Parameters:
Environment:
Type: String
Default: production
AllowedValues: [development, staging, production]
InstanceType:
Type: String
Default: t3.micro
AllowedValues: [t3.micro, t3.small, t3.medium]
Resources:
LaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${AWS::StackName}-launch-template'
LaunchTemplateData:
ImageId: ami-12345678
InstanceType: !Ref InstanceType
SecurityGroupIds:
- !Ref SecurityGroup
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y docker
service docker start
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub '${AWS::StackName}-asg'
LaunchTemplate:
LaunchTemplateId: !Ref LaunchTemplate
Version: !GetAtt LaunchTemplate.LatestVersionNumber
MinSize: 1
MaxSize: 5
DesiredCapacity: 2
VPCZoneIdentifier:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
Outputs:
LoadBalancerDNS:
Description: 'Load Balancer DNS Name'
Value: !GetAtt LoadBalancer.DNSName
Export:
Name: !Sub '${AWS::StackName}-LoadBalancerDNS'
Terraform
hcl
# main.tf
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
}
}
resource "aws_launch_template" "app" {
name_prefix = "${var.project_name}-"
image_id = var.ami_id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.app.id]
user_data = base64encode(templatefile("${path.module}/user_data.sh", {
app_name = var.project_name
}))
tag_specifications {
resource_type = "instance"
tags = {
Name = "${var.project_name}-instance"
}
}
}
resource "aws_autoscaling_group" "app" {
name = "${var.project_name}-asg"
vpc_zone_identifier = aws_subnet.public[*].id
target_group_arns = [aws_lb_target_group.app.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
desired_capacity = var.desired_capacity
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
tag {
key = "Name"
value = "${var.project_name}-asg"
propagate_at_launch = false
}
}
Disaster Recovery
Multi-Region Setup
yaml
# disaster-recovery.yml
Resources:
PrimaryRegionStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/templates/app-stack.yml
Parameters:
Environment: production
Region: us-east-1
SecondaryRegionStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/templates/app-stack.yml
Parameters:
Environment: production
Region: us-west-2
Route53HealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
Type: HTTPS
ResourcePath: /health
FullyQualifiedDomainName: !GetAtt PrimaryRegionStack.Outputs.LoadBalancerDNS
RequestInterval: 30
FailureThreshold: 3
Database Backup
bash
#!/bin/bash
# backup-script.sh
DB_INSTANCE_ID="myapp-db"
BACKUP_RETENTION_DAYS=7
# Create manual snapshot
aws rds create-db-snapshot \
--db-instance-identifier $DB_INSTANCE_ID \
--db-snapshot-identifier "${DB_INSTANCE_ID}-manual-$(date +%Y%m%d%H%M%S)"
# Delete old snapshots
aws rds describe-db-snapshots \
--db-instance-identifier $DB_INSTANCE_ID \
--snapshot-type manual \
--query "DBSnapshots[?SnapshotCreateTime<='$(date -d "$BACKUP_RETENTION_DAYS days ago" -Iso-8601)'].DBSnapshotIdentifier" \
--output text | xargs -n1 aws rds delete-db-snapshot --db-snapshot-identifier
Performance Optimization
CloudFront Configuration
json
{
"DistributionConfig": {
"CallerReference": "myapp-distribution",
"Comment": "MyApp CloudFront Distribution",
"DefaultRootObject": "index.html",
"Origins": [
{
"Id": "S3Origin",
"DomainName": "myapp-bucket.s3.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": "origin-access-identity/cloudfront/ABCDEFG1234567"
}
}
],
"DefaultCacheBehavior": {
"TargetOriginId": "S3Origin",
"ViewerProtocolPolicy": "redirect-to-https",
"Compress": true,
"CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
"OriginRequestPolicyId": "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf"
},
"CacheBehaviors": [
{
"PathPattern": "/api/*",
"TargetOriginId": "APIOrigin",
"ViewerProtocolPolicy": "https-only",
"CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
"TTL": 0
}
],
"Enabled": true,
"PriceClass": "PriceClass_100"
}
}
ElastiCache
yaml
# elasticache.yml
Resources:
CacheSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
Description: Subnet group for ElastiCache
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
RedisCluster:
Type: AWS::ElastiCache::ReplicationGroup
Properties:
ReplicationGroupDescription: Redis cluster for MyApp
NumCacheClusters: 2
Engine: redis
CacheNodeType: cache.t3.micro
CacheSubnetGroupName: !Ref CacheSubnetGroup
SecurityGroupIds:
- !Ref CacheSecurityGroup
Port: 6379
MultiAZEnabled: true
AutomaticFailoverEnabled: true
Troubleshooting
Common Issues
- IAM Permissions: Ensure proper permissions are set
- Security Groups: Check inbound/outbound rules
- VPC Configuration: Verify subnets and routing
- Resource Limits: Check service quotas
- Region Availability: Ensure services are available in your region
Debugging Tools
bash
# Check AWS CLI configuration
aws configure list
# Validate CloudFormation template
aws cloudformation validate-template --template-body file://template.yml
# Check service health
aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:region:account:targetgroup/myapp/1234567890123456
# View logs
aws logs tail /aws/lambda/myapp --follow
This comprehensive AWS tutorial covers the essential services and best practices for building scalable, secure, and cost-effective applications on AWS. Continue exploring specific services based on your application requirements.