Azure · Learn by doing

Your first website.
The Azure way.

Store a tiny website, open it over HTTPS, then rebuild it with Terraform and publish changes with Ansible. No virtual machine required.

Beginner friendlyWindows + LinuxCopyable examplesAzure Storage + HTTPS

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.

The storage account is your toy cupboard

An Azure storage account is a cupboard. The $web container is the shelf holding your website. Azure gives that shelf an HTTPS address. Terraform builds the cupboard; Ansible changes the page on the shelf.

Like S3, Blob Storage holds files rather than running Windows, Linux, PHP or Python. This lab hosts one HTML page. Visitors' browsers do the displaying; there is no database or login backend.

Visitor's browserRequests a page
HTTPS website endpointAzure supplies the address
Azure Blob Storageindex.html in $web
The website is public. Files in $web are anonymously readable through the website endpoint even when normal blob anonymous access is disabled. Put only intended public website files there. Unlike our AWS lab, there is no private origin/CDN arrangement here.

Use the supplied web.core.windows.net address for this first lab. A custom domain with HTTPS adds other setup and services. Azure Static Web Apps is a different product with a Free plan; we are using Storage here to learn the closest equivalent to S3.

Microsoft: storage website behaviorWebsite access and limitationsStatic Web Apps plans

1. Open an account and find the credit

  1. Visit Azure Free, choose the free account offer if eligible, and sign in with an account you control. Complete the contact, payment and identity verification steps shown.
  2. Eligible new customers can receive $200 credit for 30 days. The trial credit is separate from monthly free-service allowances. Check your own offer and expiration date in the portal.
  3. Open Subscriptions in the Azure portal. Record which subscription you are using. A subscription is the folder where resources and their bill belong.
  4. Check the trial spending limit. Do not remove it just to complete a tutorial. Continuing after the trial or credit ends requires moving to pay-as-you-go, which changes billing behavior.

Microsoft: offer and eligibilitySpending limit behavior

Secure your sign-in and understand permissions

Enable MFA for your Microsoft account or Microsoft Entra sign-in. Use a dedicated learning subscription. An administrator should give your deployment identity Contributor for resource management and Storage Blob Data Contributor for uploads. Management permissions and file permissions are different keys.

For this simple Terraform lab those roles must cover the learning subscription before the account is created, so data-plane configuration can succeed. That is broad access: do not use this pattern in a shared production subscription. In a later lab we can pre-create a resource group and narrow the roles to it. An existing Owner can assign these roles through Subscription → Access control (IAM); Contributor alone cannot grant roles.

Ready: MFA is enabled; you know your subscription, credit expiry and spending limit; the lab identity has both management and blob-data access.

2. Keep the lab small

The trial spending limit is a gate tied to your offer. A budget is a doorbell that rings when spending crosses a number. They are different.

  1. Open Cost Management → Budgets at subscription scope. Create a monthly $5 budget with actual-cost alerts at 50%, 80% and 100%, plus a forecast alert. Use an email you check.
  2. A newly created subscription may need time before budgets are available. Check the credit balance manually while waiting. Budgets notify you; they do not stop resources, and cost reporting is delayed.
  3. Choose Standard, StorageV2, Hot, locally redundant storage (LRS). Store one small HTML file and make a few test visits. LRS is adequate for this disposable exercise, not a universal production recommendation.
  4. Leave versioning and soft-delete retention off for this disposable content if your policy permits it. Retained copies can keep occupying billed storage. Keep the original HTML locally.
  5. Skip VMs, Front Door, custom domains, paid support, optional security upgrades and diagnostic log destinations in this first exercise. Check each creation summary for extra charges.

Storage website hosting has no separate hosting fee, but storage, requests and outgoing data can cost money. In the calculator, estimate 0.01 GB of Hot LRS storage, 1,000 reads, 100 writes and 0.1 GB of outgoing data per month. These are planning inputs, not caps.

Microsoft: budgets and delaysBlob Storage pricingAzure calculator

3. Build once in the Azure portal

This browser walkthrough is the same on Windows and Linux. Use separate names for the manual and Terraform versions so they cannot collide.

A. Save the page

Save this as UTF-8 index.html. In Windows Notepad choose “All files” so the name does not end in .txt. Open it locally first.

B. Create its home

  1. In the portal, create resource group contoso-lab-manual in East US. A resource group is a box grouping resources for this exercise.
  2. Create a Storage account in that group. Choose a globally unique name such as ctsolabbluekite7f3amanual (3–24 lowercase letters/numbers, no hyphens). Change the random suffix if taken.
  3. Select Standard performance, LRS and StorageV2. Keep secure transfer required and TLS 1.2. Use Microsoft Entra authorization; disable Shared Key access if permitted and prefer Entra authorization in the portal. Keep ordinary blob anonymous access disabled.
  4. For this temporary lab, leave versioning and retention features off where configurable. Use a public network endpoint for browser access and uploads. Private endpoints and network restrictions are a later lesson.
  5. After deployment, open Data management → Static website, enable it, set the index document to index.html and save. Azure creates $web.
  6. Open Containers → $web and upload only index.html using Entra authentication. Set content type to text/html and Cache-Control to public, max-age=60 in the blob properties if available.
  7. Copy the Primary endpoint from Static website and open the HTTPS address in a signed-out/private browser window. You do not need to purchase a certificate.
Prove it: your page works without signing in. That confirms it is public; it does not mean your upload credentials are public.

Microsoft: configure a storage websiteEntra authorization for CLI uploads

4. Windows and Linux setup

Install Azure CLI for Windows and Terraform from their official instructions. Open a new PowerShell window. Install Ubuntu in WSL for Ansible from an administrator PowerShell window, then restart if asked and finish Ubuntu's first-run setup.

PowerShell · tool checks / WSL installation
az version
terraform version
wsl --install -d Ubuntu

Keep Terraform files in an empty local folder such as C:\Labs\contoso-azure. For Ansible, place the playbook and page together inside Ubuntu, for example ~/contoso-azure-ansible. Install Azure CLI there too and sign in again; Windows and WSL store separate login sessions.

Choose the correct subscription

PowerShell or Linux · sign in and select the lab
az login
az account list --output table
# Replace the placeholder with your own learning subscription ID.
az account set --subscription "YOUR-SUBSCRIPTION-ID"
az account show --query name --output tsv
az provider register --namespace Microsoft.Storage --wait

The registration command enables the Storage resource provider for the subscription; it does not create a storage account. It needs appropriate subscription permissions. This example does not automatically register unrelated providers. Use your normal interactive login and MFA; no service-principal secret or storage account key is needed.

Resource provider registrationWSL installation

5. Rebuild with Terraform

Write the cupboard recipe once. Terraform reads it and tells you which pieces it will build.

Create a second storage account with a new name. This recreates the manual design; it does not adopt the first account. After the code version works, remove the manual group to avoid leftovers.

Save main.tf and index.html in your Terraform folder. This configuration creates contoso-lab-code; stop if that group already belongs to another exercise. Content is uploaded separately so Ansible can own it.

Create terraform.tfvars with your real subscription ID and a unique storage name. Save the supplied .gitignore before committing files.

terraform.tfvars
subscription_id = "YOUR-SUBSCRIPTION-ID"
storage_name    = "ctsolabbluekite7f3acode"
PowerShell or Linux · review then build
terraform init
terraform fmt
terraform validate
terraform plan -out=lab.tfplan
# Review only this lab's additions before applying.
terraform apply lab.tfplan
terraform output

The saved plan applies without another yes/no prompt. Keep state and plan files private: they contain resource/account details and can include sensitive computed values. Keep the generated provider lockfile for repeatable installs.

PowerShell · upload the first page
$labStorage = terraform output -raw storage_name
az storage blob upload --account-name "$labStorage" --container-name '$web' --name index.html --file ./index.html --auth-mode login --overwrite true --content-type 'text/html; charset=utf-8' --content-cache-control 'public, max-age=60'
terraform output -raw site_url

Keep '$web' in single quotes in both shells: the dollar sign is part of the container's name, not a variable. Data roles can take time to propagate; sign in again and retry after checking the role scope.

Prove it: the output HTTPS URL displays your page. A second terraform plan should show no infrastructure changes.

Terraform: static website resource

6. Publish a change with Ansible

The shelf stays put. Ansible follows a little checklist to replace the page on it.

Use Ubuntu, including WSL on Windows. This playbook uses Ansible's built-in command module to call the Azure CLI you already know. It reuses your local interactive login and does not need Azure collection credentials.

Ubuntu / WSL · install Ansible and sign in
sudo apt update
sudo apt install -y python3 python3-venv
python3 -m venv ~/.venvs/contoso-azure
source ~/.venvs/contoso-azure/bin/activate
python -m pip install ansible-core
az login
az account set --subscription "YOUR-SUBSCRIPTION-ID"

Save this playbook beside your local index.html. Change a sentence in the HTML file. Use the storage name from Terraform's output, not the manual account.

Ubuntu / WSL · update the page
ansible-playbook update-site.yml --syntax-check
ansible-playbook update-site.yml -e 'storage_name=ctsolabbluekite7f3acode'

This small playbook deliberately uploads on every run, so it reports a change even if the file is identical. It is repeatable, but it does not perform AWS's content-comparison check. Ansible's --check mode is not a reliable upload preview for this command task.

Prove it: refresh the HTTPS page after about a minute; your sentence changes. Terraform still shows no infrastructure changes. No SSH or VM configuration is involved.

Ansible: command moduleAzure CLI: blob uploads

7. Remove both lab copies

The manual copy

Open resource group contoso-lab-manual and inspect every resource inside it. If it contains only this disposable lab, delete the group and confirm its exact name. This removes its storage account and website files; keep your local page first.

The Terraform copy

PowerShell or Linux · code lab cleanup
terraform plan -destroy -out=cleanup.tfplan
# Review: only the code lab's group and storage resources should disappear.
terraform apply cleanup.tfplan
az logout

Deleting the storage account removes its uploaded content too. Terraform will remove the group it manages; the example does not automatically empty or delete unrelated nonempty groups. If deletion fails, retain the state and resolve the error before trying again.

  1. Verify both lab resource groups are gone in the portal. Check that no other credit experiments remain.
  2. Sign out of Azure CLI in both Windows and WSL if both were used. Remove temporary role assignments when no longer needed; do not delete your only account administrator.
  3. Keep budget alerts and inspect Cost Management after reporting catches up. Cleanup does not erase charges already incurred.

When something goes wrong

SymptomTry this
Storage name unavailableUse another random suffix; lowercase letters/numbers only. Renaming an existing account through Terraform replaces it.
Upload returns 403Check Storage Blob Data Contributor at the correct scope, Entra login and role propagation. Contributor alone is not blob-data access.
Container not foundEnable Static website first; use the literal '$web' container name in single quotes.
Page returns 404 or downloadsConfirm index.html is at the root of $web, index-document spelling matches and content type is text/html.
Terraform registration errorRegister Microsoft.Storage in the selected subscription with an authorized administrator.
Old sentence appearsWait for cache expiry and refresh; make sure you uploaded to the code account.

You have learned subscriptions, resource groups, data permissions, public website endpoints and two forms of automation.