A tiny page.
A new cloud to learn.
Create a learning project, store a public page, and open it over HTTPS. Then rebuild the bucket with Terraform and update the page with Ansible.
These are sample names, not our infrastructure. Before copying commands, read Customize the examples: paths, keys, IPs and names. Substitute your own authorized account and resources. Existing lab? Retain its original resource names, usernames and state; renamed samples can cause replacement. See the existing-lab warning before applying updated recipes.
A bucket, a file, and its HTTPS address
A Google Cloud project is the label on your toy box. Cloud Storage is the box. Each object is a toy with a name. For this lab, the toy is index.html, and its HTTPS address lets anyone read it.
Cloud Storage is the closest storage equivalent to S3 and Azure Blob Storage. It does not run a Windows or Linux server. This lab publishes one static HTML page; it cannot run a backend, database or user login.
https://storage.googleapis.com/BUCKET/index.html, including the filename. It does not automatically provide a home page at a custom domain or directory-style website routing.A full Cloud Storage website with a custom domain and HTTPS uses an HTTPS load balancer and certificate setup. That adds resources and potentially ongoing costs. We leave it out of this first inexpensive lab and explain it as a later service lesson.
Every file in this dedicated bucket will be public under the supplied policy, and object names can be listed. Store only the example page here. Terraform state, keys, screenshots and personal documents must stay outside it.
Google: HTTPS object endpointsFull website hosting architecturePublic data permissions
1. Open an account and a learning project
- Visit Google Cloud Free and sign in with a Google account you control. Enable two-step verification. Complete the eligibility and payment verification steps shown.
- Eligible new users can receive $300 Welcome credit for 90 days. Check your own credit balance and expiry in Billing. Trial restrictions apply; credits are not a permanent hosting allowance.
- A Free Trial account is not charged during the trial. Upgrading to paid billing allows charges beyond applicable credits and free allowances. Read the upgrade terms; do not upgrade just to make an unrelated tutorial work.
- Create a project such as
Guidewell Learningand link it to your trial billing account. The display name is friendly; the project ID is unique and used by commands. Avoid personal information in IDs. - Use a dedicated project for the manual and code versions of this lab. Do not put other workloads there. Your billing account can pay for multiple projects, so a project is not a separate credit wallet.
Google: signup and trial rulesTrial and free-service allowances
Choose the right access
The identity running this bucket lab needs Storage Admin on the learning project to create buckets and set their IAM policy, plus Service Usage Consumer for the project used by application credentials. An authorized project administrator can assign those roles in IAM. Enabling APIs or billing may need an administrator too.
Storage Admin covers all buckets in that project, so use it only for this isolated exercise. After setup, a content-only uploader can use a narrower object role on the specific bucket. Do not grant visitors Storage Admin; visitors receive only Storage Object Viewer.
2. Watch the whole project, not just the bucket
A budget is a kitchen timer. It can ring when you reach a threshold, but it does not switch off the oven.
- Open Billing → Budgets & alerts → Create budget. Scope a monthly $5 budget to your learning project. Add actual-spend alerts at 50%, 80% and 100%, and a forecast alert if offered.
- Inspect the budget's credit filters. Exclude promotional credits if you want to see underlying usage even while trial credit covers the bill. Also inspect the separate trial credit balance.
- Keep billing emails enabled and check reports after every lab. A budget does not cap spend and reporting is delayed.
- Choose regional Standard storage in
us-central1. Keep one tiny HTML page, no large downloads, no traffic generators and only a few test visits. - For this disposable lab, disable object versioning and soft-delete retention if project policy allows it. The Terraform example sets retention to zero. Keep a local copy before changing retention or deleting content.
- Skip load balancers, Cloud CDN, custom domains, virtual machines and optional logging destinations for now. Those are separate services with separate costs.
Free storage allowances have location, usage and account restrictions. Do not assume a service is free because you can create it. In the pricing calculator try 0.01 GB of Standard regional storage, 1,000 reads, 100 writes and 0.1 GB outgoing data per month; inspect the chosen region and destination. These are estimates, not usage limits.
Google: budgets are alerts, not capsCloud Storage pricingGoogle Cloud calculatorSoft-delete storage retention
3. Build once in the console
A. Save a public example page
Save this as UTF-8 index.html. On Windows choose “All files” in Notepad; make sure it is not index.html.txt. The page has no external images or scripts, so there are no extra hosting dependencies.
B. Create a bucket for public content
- In the Google Cloud console, select your learning project. Open Cloud Storage → Buckets → Create.
- Use a unique name such as
contoso-lab-blue-kite-7f3a-manual. Choose Region →us-central1, Standard storage and uniform bucket-level access. Do not use personal information in the name. - For this intentionally public learning bucket, do not enforce Public Access Prevention at bucket level. If an organization/project policy enforces it, stop; use a permitted sandbox or keep the bucket private. Do not weaken your organization's policy to follow this guide.
- Disable object versioning and soft-delete retention for these disposable files if permitted. Keep Google-managed encryption. Create the bucket.
- Upload only
index.htmlat the bucket root. Set Content-Type totext/html; charset=utf-8and Cache-Control topublic, max-age=60in object metadata. - Open the bucket's Permissions tab, grant principal
allUsersthe role Storage Object Viewer, and review the public-access warning before saving. This allows anyone to read and list this bucket's objects. - Open
https://storage.googleapis.com/YOUR-BUCKET/index.htmlin a private/signed-out browser. Do not usestorage.cloud.google.com, which is a different authenticated endpoint.
4. Windows and Linux setup
Install the Google Cloud CLI for Windows and Terraform using official instructions. Open a new PowerShell window. Install Ubuntu in WSL for Ansible using administrator PowerShell, then finish Ubuntu's initial setup.
gcloud version
terraform version
wsl --install -d UbuntuUse an empty local Terraform folder such as C:\Labs\contoso-gcp. For Ansible, place the playbook and HTML together in Ubuntu, for example ~/contoso-gcp-ansible. Install gcloud inside Ubuntu and sign in there too; Windows login sessions are separate from WSL sessions.
Use the official Google Cloud CLI Ubuntu instructions and HashiCorp's Terraform installer, choosing the correct CPU architecture.
gcloud version
terraform version
mkdir -p ~/contoso-gcp
cd ~/contoso-gcpTwo logins, two jobs
gcloud auth login lets the CLI upload files. gcloud auth application-default login supplies Application Default Credentials (ADC) for Terraform. These are separate credential stores. Both can use your interactive sign-in without downloading service-account keys.
gcloud auth login
gcloud config set project YOUR-PROJECT-ID
gcloud auth application-default login
gcloud auth application-default set-quota-project YOUR-PROJECT-ID
gcloud services enable storage.googleapis.com --project YOUR-PROJECT-ID
gcloud config get-value projectReplace every project placeholder. An authorized administrator must enable the API if your identity cannot. Keep the ADC files outside your project and source control; never upload them to the bucket.
Google: Terraform authenticationApplication Default CredentialsWSL setup
5. Write the bucket recipe in Terraform
Terraform builds the box and attaches a “public reading” label. The page itself is added afterward.
Save main.tf beside your local index.html. This configuration uses regional Standard storage, uniform permissions and zero soft-delete retention. Public Access Prevention is inherited, so an enforced organization policy can still block the public grant.
Create terraform.tfvars using your real project ID and a new globally unique bucket name. Save .gitignore before committing anything.
project_id = "YOUR-PROJECT-ID"
bucket_name = "contoso-lab-blue-kite-7f3a-code"terraform init
terraform fmt
terraform validate
terraform plan -out=lab.tfplan
# Review the new bucket and deliberate allUsers object-viewer grant.
terraform apply lab.tfplan
terraform outputThe saved plan applies without another confirmation prompt. Keep state and saved plans private. Keep the generated lockfile so later runs use the same provider version. Do not upload any Terraform files as website content.
$labBucket = terraform output -raw bucket_name
gcloud storage cp ./index.html "gs://$labBucket/index.html" --content-type 'text/html; charset=utf-8' --cache-control 'public, max-age=60'
terraform output -raw site_urllab_bucket="$(terraform output -raw bucket_name)"
gcloud storage cp ./index.html "gs://$lab_bucket/index.html" --content-type 'text/html; charset=utf-8' --cache-control 'public, max-age=60'
terraform output -raw site_urlterraform plan again; without infrastructure edits, expect no changes.Terraform: bucket resourceTerraform: bucket IAMgcloud upload flags
6. Let Ansible publish your edit
Use Ubuntu, including WSL on Windows. Terraform owns the bucket and permissions; Ansible owns the page upload. This example uses Ansible's built-in command module and your existing gcloud login, so no Google service-account key is needed.
sudo apt update
sudo apt install -y python3 python3-venv
python3 -m venv ~/.venvs/contoso-gcp
source ~/.venvs/contoso-gcp/bin/activate
python -m pip install ansible-core
gcloud auth login
gcloud config set project YOUR-PROJECT-IDSave this playbook beside index.html. Change a sentence, then pass the bucket name from Terraform's output. The playbook publishes only that one file.
ansible-playbook update-site.yml --syntax-check
ansible-playbook update-site.yml -e 'bucket_name=contoso-lab-blue-kite-7f3a-code'This intentionally uploads every run and reports a change, even if the file is identical. It does not compare remote contents. Ansible --check is not a reliable upload preview for this command task. File-comparison logic can be a later automation lesson.
7. Clean up the buckets and credentials
The manual copy
In Cloud Storage, verify the exact manual bucket name, remove its objects and delete the bucket. These steps permanently remove this lab's content because retention was disabled. Keep the HTML locally first.
The Terraform copy
Terraform refuses to delete a nonempty bucket in this example. Print the exact bucket name, verify that it belongs to this disposable lab, then remove its objects before destroying the infrastructure. The recursive command deletes every object in that bucket.
$labBucket = terraform output -raw bucket_name
$labBucket
# Stop if the name is not your disposable contoso-lab- bucket.
gcloud storage rm --recursive "gs://$labBucket/**"
terraform plan -destroy -out=cleanup.tfplan
# Read the deletions before applying.
terraform apply cleanup.tfplanlab_bucket="$(terraform output -raw bucket_name)"
printf '%s\n' "$lab_bucket"
# Stop if the name is not your disposable contoso-lab- bucket.
gcloud storage rm --recursive "gs://$lab_bucket/**"
terraform plan -destroy -out=cleanup.tfplan
# Read the deletions before applying.
terraform apply cleanup.tfplanIf the bucket is already empty, skip the object deletion and proceed with the destroy plan. If you enabled versioning or retention outside this guide, inspect retained versions and policies; do not assume this command removes every retained copy.
gcloud auth application-default revoke
gcloud auth revoke- Confirm both buckets are gone and no load balancers, reserved addresses or other experiments remain.
- If the project was created exclusively for this lab and is now empty, you may shut it down in the console. Terraform here does not manage the project; do not shut down a shared project or billing account.
- Remove temporary role grants if appropriate, keep budget alerts and inspect Billing after usage data catches up. Cleanup does not erase earlier charges.
When something goes wrong
| Symptom | Try this |
|---|---|
| Public grant rejected | Check Public Access Prevention and organization sharing constraints. Use an authorized sandbox; do not bypass them. |
| Terraform cannot authenticate | Renew application-default login in the environment running Terraform; the CLI's auth login is separate. |
| Quota-project error | Check Service Usage Consumer on the intended project and set the ADC quota project. |
| Page returns 403 | Confirm the exact bucket, allUsers object-viewer grant and signed-out URL. Allow time for permissions to propagate. |
| Page downloads instead of displaying | Re-upload with text/html content type. Include index.html in the URL. |
| Old page appears | Wait for the previous cache lifetime, refresh and check which bucket received the upload. |
| Destroy says not empty | Inspect the exact bucket, remaining objects and any retained versions before retrying. |
You have now compared storage, account boundaries, identity, HTTPS delivery and automation across AWS, Azure and Google Cloud.