Provisioning Azure and GCP Resources
Terraform's multi-cloud, provider-plugin architecture means the same core language and workflow apply to Azure and GCP just as they do to AWS, but each provider mirrors its own cloud's resource model and authentication conventions closely. The azurerm provider organizes almost everything under a resource group, a concept unique to Azure's management hierarchy, while the google provider organizes resources under a project, GCP's top-level billing and IAM boundary. Understanding these organizing concepts is essential before writing any resource blocks for either cloud.
Cricket analogy: Terraform's provider-plugin model is like the same laws of cricket applying whether you're playing at Lord's or the MCG, but each ground has its own local conventions — Lord's slope, MCG's drop-in pitch — you must respect before setting a field.
Azure: resource groups as the organizing unit
Almost every azurerm resource requires a resource_group_name argument, because Azure requires all resources to belong to exactly one resource group, which acts as a logical container for lifecycle management, access control, and cost tracking. A typical Azure Terraform configuration therefore starts by declaring an azurerm_resource_group resource (or referencing an existing one via data source), and every subsequent resource references its name and location.
Cricket analogy: Every azurerm resource needing resource_group_name is like every player at an IPL franchise needing to belong to exactly one squad roster — you can't field a player without first registering the franchise, and every subsequent signing references that squad.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "main" {
name = "rg-webapp-prod"
location = "East US"
}
resource "azurerm_virtual_network" "main" {
name = "vnet-webapp"
address_space = ["10.1.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_linux_virtual_machine" "web" {
name = "vm-web-01"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_B2s"
admin_username = "azureadmin"
network_interface_ids = [azurerm_network_interface.web.id]
admin_ssh_key {
username = "azureadmin"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}The azurerm provider's near-empty features {} block is not decorative — it's a required argument that configures provider-wide behaviors (like whether deleted resource groups are purged) and Terraform will refuse to initialize the provider without it, even with all defaults.
GCP: projects, regions, and zones
The google provider is configured with a project ID and a default region/zone, and most resources inherit location settings from the provider block unless overridden per resource. GCP's naming convention tends to be resource-type-first (google_compute_instance, google_storage_bucket, google_sql_database_instance), and authentication is typically handled via a service account key file or, preferably, Application Default Credentials resolved from the environment (e.g. Workload Identity in CI).
Cricket analogy: The google provider's project ID with a default region mirrors a bilateral series' host board setting the default venue and pitch conditions, while individual matches (resources) can still be assigned a different ground when specifically scheduled otherwise.
provider "google" {
project = "acme-prod-247501"
region = "us-central1"
zone = "us-central1-a"
}
resource "google_compute_network" "main" {
name = "vpc-webapp"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "main" {
name = "subnet-webapp"
ip_cidr_range = "10.2.0.0/24"
region = "us-central1"
network = google_compute_network.main.id
}
resource "google_compute_instance" "web" {
name = "web-01"
machine_type = "e2-medium"
boot_disk {
initialize_params {
image = "debian-cloud/debian-12"
}
}
network_interface {
subnetwork = google_compute_subnetwork.main.id
access_config {}
}
}A common cross-cloud pitfall: assuming resource argument names and required scaffolding transfer directly between providers. Azure requires a resource group for virtually everything; GCP requires a project and, for networking, an explicit auto_create_subnetworks = false if you intend to manage subnets yourself rather than use the legacy auto-mode network. Skipping these provider-specific requirements is one of the most common sources of first-time multi-cloud Terraform errors.
Multi-cloud configurations
It is entirely valid to declare azurerm, google, and aws providers in the same Terraform configuration when a workload genuinely spans clouds — for example, replicating data to a secondary cloud for disaster recovery. In practice, most teams still isolate each cloud's resources into its own root module or workspace to keep blast radius and state file size manageable, using outputs and remote state data sources to pass values (like a VPN gateway IP) between the two configurations when cross-cloud references are actually needed.
Cricket analogy: Declaring azurerm, google, and aws providers together is like a franchise fielding players contracted across three different domestic leagues in one squad for a genuine multi-format tour, though most teams still keep each league's roster in its own file.
- azurerm resources are almost universally scoped to a resource group, which must exist before other resources reference it.
- The azurerm provider requires a features {} block even when accepting all defaults.
- google provider resources are scoped to a project and inherit region/zone defaults from the provider block.
- GCP resource type names are prefixed with google_ and follow a resource-type-first naming convention.
- Multi-cloud configurations can declare multiple cloud providers together, but most teams isolate each cloud into its own root module for manageable blast radius.
- Provider-specific scaffolding (resource groups on Azure, projects/auto_create_subnetworks on GCP) is a common source of first-time errors when moving between clouds.
Practice what you learned
1. What organizing concept does almost every azurerm resource require a reference to?
2. What is required in the azurerm provider block even when using all default behaviors?
3. What top-level concept scopes resources in the google provider?
4. What GCP networking setting must you set to false if you intend to manage subnets explicitly rather than use the legacy auto-mode network?
5. How do most teams typically manage a workload that spans AWS, Azure, and GCP simultaneously?
Was this page helpful?
You May Also Like
Provisioning AWS Resources
A practical walkthrough of configuring the AWS provider and provisioning core compute, networking, and storage resources with Terraform.
Providers Explained
Providers are Terraform plugins that translate HCL resource definitions into API calls against a specific platform, such as AWS, Azure, GCP, or Kubernetes.
Terraform vs Other IaC Tools
Compare Terraform against Pulumi, AWS CloudFormation, Ansible, and Chef/Puppet to understand where Terraform fits in the infrastructure-as-code landscape.
Data Sources Explained
Learn how Terraform data sources let you read information about existing infrastructure or external systems without managing it, so configurations can reference real-world values.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics