Skip to content

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

  1. Create an AWS Account

    • Visit aws.amazon.com
    • Click "Create an AWS Account"
    • Follow the registration process
  2. 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"
    }
  3. 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

  1. IAM Permissions: Ensure proper permissions are set
  2. Security Groups: Check inbound/outbound rules
  3. VPC Configuration: Verify subnets and routing
  4. Resource Limits: Check service quotas
  5. 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.

VitePress Development Guide