# P09 SAA architecture capstone

This optional paid lab deploys an internet-facing ALB and two EC2 instances in an Auto Scaling group across two Availability Zones. Instances have no public IPv4 address. They read `app/message.txt` from a private, versioned S3 bucket through a gateway endpoint using a scoped instance role.

## Cost and permission gate

The ALB, EC2 instances, EBS volumes and public IPv4 use by the load balancer can charge continuously. Confirm current regional prices, obtain account-owner approval, set a same-session deletion deadline, and never deploy unless AWS201 recovery and cleanup will follow immediately. Deployment requires CloudFormation plus acknowledged named-IAM capability.

## Validate and deploy

```bash
set -euo pipefail
export AWS_DEFAULT_REGION="ap-south-1"
stack_name="nw-p09-capstone"
template="template.yaml"

aws sts get-caller-identity --query Arn --output text
aws cloudformation validate-template --template-body "file://${template}"
aws cloudformation estimate-template-cost --template-body "file://${template}"
aws cloudformation deploy \
  --stack-name "$stack_name" \
  --template-file "$template" \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides AllowedHttpCidr=0.0.0.0/0 DesiredCapacity=2 \
  --tags Project=NitWings-P09 Cleanup=stack-owned
```

Restrict `AllowedHttpCidr` to the learner's stable public CIDR when practical. The stack intentionally uses HTTP so AWS200 can separate baseline reachability from the ACM/TLS extension decision; never send credentials or private data through it.

## Seed and prove the healthy path

```bash
application_url="$(aws cloudformation describe-stacks --stack-name "$stack_name" --query 'Stacks[0].Outputs[?OutputKey==`ApplicationUrl`].OutputValue' --output text)"
bucket_name="$(aws cloudformation describe-stacks --stack-name "$stack_name" --query 'Stacks[0].Outputs[?OutputKey==`DataBucketName`].OutputValue' --output text)"
target_group="$(aws cloudformation describe-stacks --stack-name "$stack_name" --query 'Stacks[0].Outputs[?OutputKey==`TargetGroupArn`].OutputValue' --output text)"

printf '%s\n' 'hello-from-version-one' >/tmp/nw-p09-message.txt
aws s3 cp /tmp/nw-p09-message.txt "s3://${bucket_name}/app/message.txt"
aws elbv2 describe-target-health --target-group-arn "$target_group" \
  --query 'TargetHealthDescriptions[].{Target:Target.Id,AZ:Target.AvailabilityZone,State:TargetHealth.State,Reason:TargetHealth.Reason}' --output table
for attempt in 1 2 3 4 5 6; do curl --fail --show-error "$application_url/"; echo; done
```

Passing evidence shows two healthy targets in different AZs and successful responses containing the seeded message. Repeated requests should eventually show both instance IDs; ALB distribution is not strict round-robin, so six responses are evidence collection rather than a guarantee.

## Controlled fault cards

Use [FAULT_CARDS.md](FAULT_CARDS.md) for the exact reversible compute, network, versioned-data and identity exercises. Apply one card at a time, record baseline and UTC start, preserve evidence, make one bounded change, retest, and restore baseline before the next card.

## Cleanup

Empty all object versions and delete markers before deleting the stack because CloudFormation cannot delete a non-empty bucket.

```bash
aws s3api list-object-versions --bucket "$bucket_name" --output json > /tmp/nw-p09-object-versions.json
python3 - "$bucket_name" /tmp/nw-p09-object-versions.json <<'PY'
import json, subprocess, sys
bucket, path = sys.argv[1:]
data = json.load(open(path, encoding="utf-8"))
objects = [
    {"Key": item["Key"], "VersionId": item["VersionId"]}
    for group in ("Versions", "DeleteMarkers") for item in data.get(group, [])
]
if objects:
    subprocess.run([
        "aws", "s3api", "delete-objects", "--bucket", bucket,
        "--delete", json.dumps({"Objects": objects, "Quiet": True})
    ], check=True)
PY

aws cloudformation delete-stack --stack-name "$stack_name"
aws cloudformation wait stack-delete-complete --stack-name "$stack_name"
if aws cloudformation describe-stacks --stack-name "$stack_name" >/dev/null 2>&1; then
  echo "FAIL: stack still exists" >&2
  exit 1
fi
```

Finally inventory resources tagged `Project=NitWings-P09`, ENIs, load balancers, target groups, launch templates, instances, volumes, snapshots, buckets, IAM roles/profiles, alarms and security groups. Record zero owned residuals and review delayed Billing/Cost Explorer data when available. Never delete by name similarity alone.
