Friday, 27 March 2026

AWS Credentials Setup — Best Practices Guide

 

Overview

This guide walks you through creating a dedicated IAM user for the HealthPulse capstone project with the minimum permissions needed. This follows the Principle of Least Privilege — the user can only do what it needs, nothing more.

Why not use your root account? The root account has unlimited access. If those credentials leak (in a git commit, a CI log, a student's laptop), your entire AWS account is compromised. A scoped IAM user limits the blast radius.


Architecture: What We're Creating

AWS Account (root — NEVER use for Terraform)
│
├── IAM Group: "healthpulse-devops"
│   └── Attached Policies:
│       ├── HealthPulseBaremetalPolicy  (EC2, VPC, EIP)
│       ├── HealthPulseContainerPolicy  (ECS, ECR, ALB)   ← for Task H
│       └── HealthPulseK8sPolicy        (EKS)             ← for Task I
│
└── IAM User: "healthpulse-terraform"
    ├── Member of: healthpulse-devops group
    ├── Access Key: AKIA...  (for CLI/Terraform)
    └── NO console password (programmatic access only)

Step 1: Create the IAM Policy

This policy covers everything needed for Task G (bare-metal) and Task C (VPC infrastructure). It's scoped to only the resource types Terraform will create.

1.1 — Go to IAM in the AWS Console

  1. Log in to AWS Console with your root account or an existing admin user
  2. Navigate to IAM → Policies → Create policy
  3. Click JSON tab and paste the policy below

1.2 — The Policy (Task C: Bare-Metal)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EC2Describe",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EC2Mutate",
      "Effect": "Allow",
      "Action": [
        "ec2:RunInstances",
        "ec2:TerminateInstances",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:RebootInstances",
        "ec2:ModifyInstanceAttribute",
        "ec2:CreateKeyPair",
        "ec2:DeleteKeyPair",
        "ec2:ImportKeyPair",
        "ec2:CreateTags",
        "ec2:DeleteTags"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    },
    {
      "Sid": "VPCNetworking",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateVpc",
        "ec2:DeleteVpc",
        "ec2:ModifyVpcAttribute",
        "ec2:CreateSubnet",
        "ec2:DeleteSubnet",
        "ec2:ModifySubnetAttribute",
        "ec2:CreateInternetGateway",
        "ec2:DeleteInternetGateway",
        "ec2:AttachInternetGateway",
        "ec2:DetachInternetGateway",
        "ec2:CreateRouteTable",
        "ec2:DeleteRouteTable",
        "ec2:CreateRoute",
        "ec2:DeleteRoute",
        "ec2:ReplaceRoute",
        "ec2:AssociateRouteTable",
        "ec2:DisassociateRouteTable",
        "ec2:CreateNatGateway",
        "ec2:DeleteNatGateway"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    },
    {
      "Sid": "SecurityGroups",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateSecurityGroup",
        "ec2:DeleteSecurityGroup",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupIngress",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
        "ec2:UpdateSecurityGroupRuleDescriptionsEgress"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    },
    {
      "Sid": "ElasticIP",
      "Effect": "Allow",
      "Action": [
        "ec2:AllocateAddress",
        "ec2:ReleaseAddress",
        "ec2:AssociateAddress",
        "ec2:DisassociateAddress"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    }
  ]
}
  1. Click Next
  2. Name it: HealthPulseBaremetalPolicy
  3. Description: Terraform permissions for HealthPulse bare-metal EC2 deployment (VPC, Subnet, EC2, EIP, SG)
  4. Click Create policy

1.3 — What Each Section Does

Policy SectionWhy It's Needed
EC2Describeec2:Describe* — read-only wildcard. Terraform calls dozens of Describe* actions (DescribeInstances, DescribeVpcAttribute, DescribeInstanceAttribute, DescribeSubnets, etc.). Wildcard here is safe because Describe actions are read-only and cannot create, modify, or delete anything.
EC2MutateCreate/destroy EC2 instances, import SSH key pair, tag resources. Region-locked.
VPCNetworkingCreate/modify/delete VPC, subnet, internet gateway, route table. Includes ModifyVpcAttribute (for dns_hostnames) and ModifySubnetAttribute (for map_public_ip). Region-locked.
SecurityGroupsCreate/modify security groups for HTTP/SSH firewall rules. Region-locked.
ElasticIPAllocate/release a static public IP for the server. Region-locked.

Region lock: Every mutating action is restricted to us-east-1. If a student accidentally targets eu-west-1, the API will deny it.


Step 2: Create the IAM Group

Groups make it easy to manage permissions for multiple students.

  1. Go to IAM → User groups → Create group
  2. Group name: healthpulse-devops
  3. Attach the policy: HealthPulseBaremetalPolicy
  4. Click Create group

Why a group? If you have 20 students, you attach the policy to the group once. Each student's IAM user joins the group and automatically gets the permissions. If you need to change permissions later, you update the group policy — all students get the change instantly.


Step 3: Create the IAM User

3.1 — Create User in Console

  1. Go to IAM → Users → Create user
  2. User name: healthpulse-terraform (or per-student: healthpulse-student-01)
  3. DO NOT check "Provide user access to the AWS Management Console" → This user is for CLI/Terraform only, not for clicking around in the console
  4. Click Next
  5. Select Add user to group → choose healthpulse-devops
  6. Click Next → Create user

3.2 — Create Access Keys

  1. Click on the newly created user → Security credentials tab
  2. Scroll to Access keys → Create access key
  3. Use case: Select Command Line Interface (CLI)
  4. Check the acknowledgment box
  5. Click Next → Create access key
  6. CRITICAL: Copy both values now — the Secret Access Key is shown only once:
Access Key ID:     AKIAIOSFODNN7EXAMPLE
Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Store these securely. Do NOT email them, Slack them, or commit them to git. Use a password manager or encrypted file.


Step 4: Configure AWS CLI (OPTIONAL)

On your local machine (or the student's machine):

# Install AWS CLI (if not already installed)
# Windows: Download from https://aws.amazon.com/cli/
# Mac:     brew install awscli
# Linux:   sudo apt install awscli

# Configure credentials
aws configure

It will prompt:

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json

4.1 — Verify It Works

# Check your identity
aws sts get-caller-identity

Expected output:

{
    "UserId": "AIDAIOSFODNN7EXAMPLE",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/healthpulse-terraform"
}
# Test EC2 permissions (should list instances, even if empty)
aws ec2 describe-instances --region us-east-1 --query "Reservations" --output text

# Test that another region is DENIED
aws ec2 describe-instances --region eu-west-1
# → An error occurred (UnauthorizedOperation)  ← This is CORRECT, policy blocks it

Step 5: Use Named Profiles (Best Practice)(OPTIONAL)

Instead of setting credentials as the default, use a named profile. This prevents accidentally running Terraform against the wrong AWS account.

# Configure a named profile
aws configure --profile healthpulse

Enter the same credentials. Now use it explicitly:

# Test with the profile
aws sts get-caller-identity --profile healthpulse

# Set for your terminal session
export AWS_PROFILE=healthpulse      # Linux/Mac
set AWS_PROFILE=healthpulse         # Windows CMD
$env:AWS_PROFILE = "healthpulse"    # Windows PowerShell

In Terraform, reference it in the provider:

provider "aws" {
  region  = var.aws_region
  profile = "healthpulse"    # ← optional, uses named profile
}

Or set the environment variable so Terraform picks it up automatically:

export AWS_PROFILE=healthpulse
terraform plan -var-file=dev.tfvars ...

Step 6: Run Terraform (Simplified)

Now that the VPC/subnet are created by Terraform (no longer need to pass them manually):

cd terraform/baremetal

terraform init

# Plan — only need SSH key and optionally your IP for SSH restriction
terraform plan \
  -var-file=dev.tfvars \
  -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)"

# Apply
terraform apply \
  -var-file=dev.tfvars \
  -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)"

Notice: No more vpc_id or subnet_id variables! Terraform creates the entire network stack.

To restrict SSH to your IP (recommended):

terraform apply \
  -var-file=dev.tfvars \
  -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)" \
  -var="ssh_allowed_cidr=$(curl -s ifconfig.me)/32"


Security Best Practices Summary

PracticeWhy
Never use root credentialsRoot has unlimited power — one leak and your account is gone
Programmatic access onlyNo console password = no one can click around and break things
Region-locked policiesPrevents accidental resource creation in wrong regions (and cost surprises)
Named profilesPrevents "wrong account" mistakes when you have multiple AWS accounts
IAM groupsChange permissions once, applies to all students
Per-student usersAudit trail, individual revocation
Rotate keysIf the program runs > 90 days, rotate access keys
Delete after programRemove all users, keys, and policies when the capstone is done
Never commit credentialsThis is why Task B exists (pre-commit hooks with detect-secrets)

Cost Control

To prevent surprise bills:

Set a Budget Alert

# In AWS Console: Billing → Budgets → Create budget
# Set monthly budget: $50 (or whatever your limit is)
# Alert at: 50%, 80%, 100%
# Notify: your email

What This Capstone Costs (Approximate)

ResourceTaskMonthly Cost
t2.micro EC2G (bare-metal)$0 (free tier) or ~$8.50
Elastic IP (attached)G$0
Elastic IP (detached)G~$3.60/mo
VPC + IGWG$0


Total for Task C only: Free tier eligible or under $15/monthReminder: Always run terraform destroy when done for the day if cost is a concern.


Quick Reference Card

# ─── FIRST TIME SETUP ───
aws configure --profile healthpulse
export AWS_PROFILE=healthpulse

# ─── VERIFY CREDENTIALS ───
aws sts get-caller-identity

# ─── GENERATE SSH KEY ───
ssh-keygen -t ed25519 -f ~/.ssh/healthpulse-key -N "" -C "healthpulse-capstone"

# ─── TERRAFORM (Task G) ───
cd terraform/baremetal
terraform init
terraform plan -var-file=dev.tfvars -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)"
terraform apply -var-file=dev.tfvars -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)"

# ─── CHECK OUTPUTS ───
terraform output

# ─── SSH INTO SERVER ───
ssh -i ~/.ssh/healthpulse-key ubuntu@$(terraform output -raw public_ip)

# ─── DESTROY WHEN DONE ───
terraform destroy -var-file=dev.tfvars -var="ssh_public_key=$(cat ~/.ssh/healthpulse-key.pub)"

Tuesday, 17 March 2026

Capstone Project: Mastering the Modern Web Stack (HealthPulse Portal)

 

🚀 Capstone Project: Mastering the Modern Web Stack (HealthPulse Portal)

Welcome to the final frontier of your DevOps journey!

In our previous labs, we mastered the "Old Guard": Java, Maven, Jenkins, and Tomcat. But the industry is moving fast. Modern cloud-native applications are shifting toward leaner, faster, and more modular stacks.

Today, we are diving into the HealthPulse Portal—a production-grade React application designed to teach you how to bridge the gap between Frontend Development and High-End DevSecOps.

🏗️ The Core Architecture: The "Big Five"

Think of this stack as a high-performance engine. Each part has a specific role in ensuring the app is fast, secure, and easy to maintain.




1. Vite: The High-Speed Engine

What it replaces: Think of Vite as your Maven or Gradle, but optimized for the frontend.

The Difference: While Maven can be slow to compile, Vite is "instant." It uses Native ESM to start your development server in milliseconds.

DevOps Value: Faster build times in your CI/CD pipeline means faster feedback loops.

2. TypeScript: The Security Officer

What it is: JavaScript with "Rules."

The Difference: Vanilla JavaScript is like the Wild West. TypeScript is like Java—it requires you to define types (Strings, Integers, Objects).

DevOps Value: It catches bugs during the build phase before the code even reaches a SonarQube scan.

3. React 18: The Architect

What it is: A library for building UI "Components."

The Difference: Instead of one massive HTML file, everything is a small, reusable piece (e.g., LoginButton.tsx).

Analogy: If your app is a house, React is the blueprint that tells the browser how to assemble the rooms.

4. Tailwind CSS & shadcn/ui: The Styling Duo

Tailwind: Instead of writing massive .css files, you apply styles directly in your code (e.g., class="bg-blue-500").

shadcn/ui: This is your "Component Toolkit." Need a beautiful dashboard table? You don't build it from scratch; you pull it from shadcn.

🛠️ The DevOps Pipeline: How We Ship It

This is where your previous training shines. Even though the language has changed from Java to TypeScript, the Lifecycle remains the same.

The Multi-Stage Docker Build

In the Java world, we created a .war file. For HealthPulse, we use a Multi-Stage Dockerfile:

Stage 1 (Build): A Node.js container runs npm run build to create static files in a dist/ folder.

Stage 2 (Serve): We move those files into a lightweight Nginx container.

Why? This keeps our production image tiny and secure, drastically reducing the "attack surface."

Testing & Quality Gates

Vitest: Your new JUnit. It handles unit tests for your logic.

Playwright: This is "End-to-End" (E2E) testing. It opens a virtual browser and "clicks" buttons to ensure the user flow actually works.

SonarQube: Just like in Java, it scans your TypeScript for "Code Smells" and security vulnerabilities.

🔒 Infrastructure & Monitoring

Terraform: We use "Infrastructure as Code" (IaC) to build our AWS environment. No manual clicking!

Kubernetes (K3s): Our app lives in a cluster, managed by manifests you’ll find in the /kubernetes folder.

Datadog: Our "CCTV." If the /health endpoint returns an error, Datadog alerts us immediately.

🏁 Summary for Students

You aren't just building a website; you are building a production-grade, automated deployment machine.

• React/TS is the product.

• Vite/Docker is the packaging.

• Terraform/K8s is the warehouse.

• Jenkins/GitLab is the delivery truck.

Ready to start? Clone the repo, check the PROJECT-BRIEF.md, and run your first npm install. Let's build something great!

GITLAB CI

TASK J: GitLab CI — CI Pipeline Guide Overview This guide gets your Continuous Integration (CI) pipeline running end-to-end. You will compl...