Stuck? Find the next check.
Choose the symptom you see. Follow a small set of checks before changing the cloud, and learn what each result means.
1. Find the failing layer
Troubleshooting is like following a delivery: was the address wrong, was the door locked, or was nobody home? Start with the exact error. Change one thing at a time, then repeat the original check.
- Confirm this is your authorized learning account and resource.
- Record the time, error and last change privately.
- Inspect before changing permissions or deleting anything.
- Test one hypothesis; verify the result and record it.
- Check what is still running and being billed.
Download a troubleshooting notes template. Redact account IDs, hostnames, IPs, usernames and personal paths before sharing logs. Commands below are not automatically executed by this page.
2. SSH will not connect
SSH applies to the original AWS and Azure server guides, not the SSM-based pipeline. The Google Cloud guide may use gcloud and OS Login instead of a manually managed key. Follow the access model of the guide you actually built.
| What you see | What to inspect next |
|---|---|
| Connection timed out | Server running, correct current address, route, and port 22 restricted to your actual public /32. VPNs can change that address. |
| Connection refused | An endpoint answered but SSH may not be listening there; inspect the server using an authorized console or recovery path. |
| Permission denied (publickey) | Correct image-specific username, matching private key and authorized public key. Network changes rarely fix this. |
| Host identification has changed | Stop. Verify whether the instance was deliberately replaced and verify its new host fingerprint through a trusted channel. Do not blindly delete known_hosts. |
| Identity file not accessible | Your key file does not exist at the copied path; select the real file rather than generating a replacement key for an existing server. |
On Windows, ask for your actual values instead of copying another person's user directory. This tests TCP reachability only; it does not prove SSH authentication.
$labAddress = Read-Host "Your lab server public IP or hostname"
Test-NetConnection -ComputerName $labAddress -Port 22$labKeyPath = Read-Host "Full path to your existing private key"
$labUser = Read-Host "Linux username from your server guide"
$labAddress = Read-Host "Your lab server public IP or hostname"
if (-not (Test-Path -LiteralPath $labKeyPath -PathType Leaf)) {
throw "Private key file not found. Stop and check the path."
}
ssh -v -o ConnectTimeout=10 -o IdentitiesOnly=yes -i "$labKeyPath" "$labUser@$labAddress"read -r -p "Full Linux path to your existing private key: " lab_key
read -r -p "Linux username from your server guide: " lab_user
read -r -p "Your lab server public IP or hostname: " lab_address
if [ -f "$lab_key" ]; then
ssh -v -o ConnectTimeout=10 -o IdentitiesOnly=yes -i "$lab_key" "$lab_user@$lab_address"
else
printf "%s\n" "Private key file not found. Stop and check the path."
fiAccept a new host key only after verifying its fingerprint. Verbose output can reveal local paths and network details; keep it private. WSL uses Linux paths, not PowerShell paths. Do not disable host-key checking.
Choose the correct paths and keys · Google Cloud SSH troubleshooting.
3. The pipeline server is unavailable in SSM
SSM is a management channel initiated by the server. Opening inbound SSH is not the solution. Confirm the same account and us-east-1 region used by the pipeline project.
aws sts get-caller-identity
aws ssm describe-instance-information --region us-east-1 --output tableThis inspection requires permission to describe managed instances. If denied, ask the administrator to inspect or approve the specific read permission. An empty list can mean the wrong region/account or a server that has not registered; it does not prove the VM was deleted.
- In EC2, confirm the expected instance exists, is running, and has passed status checks.
- Check its approved instance profile and SSM permissions. Passing a role to EC2 is different from your permission to start a session.
- Verify the agent is installed and running through an already authorized access path.
- Check outbound HTTPS, DNS and the network route to the regional Systems Manager endpoints.
- Allow initial startup time, then repeat the managed-instance check.
If you already have authorized access to this lab's Ubuntu server, inspect the snap-installed agent used by the pipeline:
sudo snap services amazon-ssm-agent
sudo tail -n 40 /var/log/amazon/ssm/amazon-ssm-agent.logThe pipeline installs the snap package; other images can use a different service name. Do not restart or replace agents on shared servers without approval. A StartSession access denial differs from TargetNotConnected; diagnose permissions versus registration separately. See AWS Session Manager troubleshooting.
4. Terraform cannot plan or access state
Terraform needs both the right instructions and the right identity. These commands use the pipeline's terraform/ subfolder. Other guides may run Terraform from the project root: adjust the folder, not the backend's identity.
terraform version
terraform -chdir=terraform fmt -check
terraform -chdir=terraform validate
terraform -chdir=terraform workspace show
terraform -chdir=terraform state listValidate requires an initialized working directory. State list reads the configured backend and can reveal resource names. It does not delete resources and an empty result is not a clean-bill guarantee.
| What you see | What to inspect next |
|---|---|
| Expired token / credentials unavailable | Renew the approved short-lived login for the intended profile; verify identity again. Never paste access keys into a .tf file. |
| AccessDenied while reading S3 state | Correct state bucket, object key, region, backend role, encryption permissions and organizational policy. Do not create a new empty state as a shortcut. |
| AccessDenied on a cloud resource | The exact denied action and resource, plus permission boundaries or organization policies. Request a scoped correction, not AdministratorAccess. |
| Error acquiring the state lock | An active local/CI run may own it. Check run status and the lock owner/time. Wait or coordinate first. |
| Provider not installed / lockfile mismatch | Use the existing provider lockfile and documented init step; do not upgrade providers mid-incident. |
| Plan proposes recreating everything | Wrong backend/workspace, wrong variables, or lost state. Stop before apply and compare the original backend settings. |
Lock safety: never bypass locking. Force-unlock is a recovery operation only after verifying your own stale lock and that no writer remains; coordinate with the state owner and follow HashiCorp's state-locking guidance. This guide deliberately provides no one-click unlock or state deletion command.
For a GitHub OIDC denial, compare the actual repository, branch and environment against the approved trust policy. Do not replace the trust subject with a wildcard. For a failed apply, inspect current state and a new plan before retrying; part of the infrastructure may already exist and incur charges.
5. Ansible fails or keeps changing things
Ansible's UNREACHABLE means the management connection failed. FAILED means a task ran and encountered a problem. Start by checking the intended host and transport; do not run a playbook against an unreviewed inventory.
ansible --version
ansible-galaxy collection list
ansible-playbook -i inventory.generated.json ansible/site.yml --list-hosts
ansible-playbook -i inventory.generated.json ansible/site.yml --syntax-checkExpect only your dedicated lab instance, not a production host list. Regenerate the inventory using the pipeline guide if the instance was replaced. For its SSM transport, inspect the transfer bucket, region, AWS credentials, Session Manager plugin and amazon.aws collection. SSH-key inventory settings do not repair SSM.
| What you see | What to inspect next |
|---|---|
| Could not resolve module/action | Wrong virtual environment, missing collection, or a different execution environment in AAP. Compare the documented versions. |
| Permission denied writing a system file | Expected become/sudo access and correct connection user. Do not make system directories world-writable. |
| Package manager is locked | Initial cloud setup or another package task may be running. Allow it to finish; do not delete lock files while a process owns them. |
| Template lookup failed | Template file is in the expected project location and the filename matches exactly. Linux filenames are case-sensitive. |
| Changed every run | Timestamp/random input, unguarded shell command, or a template that is not stable. Inspect the changed task rather than hiding its result. |
| AAP job works locally but fails in controller | Inventory, credential type, project revision, execution environment dependencies and job limit. A local venv is not automatically inside the EE. |
Check mode predicts supported task changes, but is not an absolute safety boundary: some tasks skip it and explicitly configured tasks can still run. Diff output can reveal secrets. Review the playbook before using either mode; never enable verbose debug on sensitive production jobs just to collect more output. See Ansible execution guidance.
For the non-secret local foundations lab, this comparison is useful after reviewing its file-only tasks:
ansible-playbook -i inventory.ini site.yml --syntax-check
ansible-playbook -i inventory.ini site.yml --check --diff6. The website or HTTPS check fails
Follow the request in order: name → address → firewall → listener → content. Use the actual hostname you own for the capstone; sample domains are not test targets.
$labHostname = Read-Host "Your own lab hostname, without https://"
Resolve-DnsName $labHostname -Type A
Test-NetConnection -ComputerName $labHostname -Port 443
curl.exe --fail --show-error --head "https://$labHostname/"read -r -p "Your own lab hostname, without https://: " lab_hostname
getent ahostsv4 "$lab_hostname"
curl --fail --show-error --head "https://$lab_hostname/"| What you see | What to inspect next |
|---|---|
| Name cannot resolve | Exact DNS record, authoritative provider, spelling and propagation. Do not change your production nameservers as a test. |
| Timeout on 443 | Correct current VM address, your current allowed /32, reviewed HTTPS rule and a running listener. |
| Certificate name mismatch | Hostname matches the certificate and the DNS-only A record points at the intended server. Do not add -k. |
| Certificate expired | Manual DNS issuance is not automatic renewal. Follow the capstone renewal limitations and certificate guidance. |
| HTTP 403 / 404 | Nginx root, file permissions, requested path and correct virtual host. Do not recursively grant 777 permissions. |
| HTTP works but HTTPS does not | Certificate files, port 443 rule, secure playbook and Nginx configuration. The base pipeline installs HTTP only. |
Inside an authorized server session, inspect the local service without restarting it:
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo ss -ltnp
curl --fail --show-error --head http://127.0.0.1/If loopback HTTP works but the laptop fails, focus on the external path. If loopback fails too, inspect service configuration and logs first. The loopback check cannot validate browser certificate trust.
7. Why am I still being charged?
A VM is not the whole project. Think of compute as the light switch; disks, IP reservations and storage can remain after the light is off. Start in billing with the charged service, region and usage date—not by deleting random resources.
| What you see | What to inspect next |
|---|---|
| AWS instance stopped | EBS disks, snapshots, allocated addresses and unrelated services may remain. Inspect the charge category and all regions where you created resources. |
| Azure VM stopped | Stopped and deallocated are different states. Allocated compute may remain billable; disks, networking and other resources need separate review even after deallocation. |
| Google Cloud VM stopped | Persistent disks and reserved static addresses can remain billable. Stopping is not deleting the project resources. |
| Terraform state list is empty | Wrong backend/workspace or resources created outside Terraform. State is not an account-wide inventory. |
| Destroy succeeded but billing still shows usage | Usage reports can lag and earlier usage remains on the bill. Compare usage dates and verify remaining resources. |
| Credits did not cover a charge | Offer eligibility, region, service, limits, expiration and billing account. Never assume every component qualifies. |
These are read-only inventories, not deletion recipes. They can show unrelated resources: inspect ownership before acting and keep identifiers private.
aws sts get-caller-identity
aws ec2 describe-volumes --region us-east-1 --output table
aws ec2 describe-addresses --region us-east-1 --output tableThese inspect only us-east-1 and only two resource types. Repeat intentionally for regions you used and inspect the billed services in the console. An AccessDenied does not prove nothing exists.
az account show --output table
az resource list --resource-group "REPLACE-WITH-YOUR-LAB-RESOURCE-GROUP" --output tablegcloud config get-value project
gcloud compute disks list --project="REPLACE-WITH-YOUR-LAB-PROJECT-ID"
gcloud compute addresses list --project="REPLACE-WITH-YOUR-LAB-PROJECT-ID"Use your original reviewed teardown or provider guide. Preserve wanted data and never delete shared IAM, storage or an entire resource group without verifying what it contains. If charges remain unexplained, contact the provider's billing support with private evidence.
Official references: AWS instance states, Azure states and billing, Google Cloud stopped-resource costs.
8. Close the loop
Repeat the original failing check after the fix. Record what changed, why it helped, and how you know. If infrastructure changed, inspect a fresh Terraform plan; if configuration changed, inspect a repeat Ansible run. Do not automatically apply a new plan.
- The right account, region and resource were confirmed.
- The original symptom is resolved, not merely hidden.
- No wider network access or unnecessary permissions remain.
- Logs and evidence are redacted before sharing.
- The running lab has a cleanup time, or teardown is verified.
Documentation reviewed October 2, 2026. Commands are educational examples; this guide was not exercised against live cloud accounts. Local file checks do not validate your permissions, network or billing.