Terraform Guide

GCP Terraform diagram generator: the google provider repo, drawn with real icons

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

GCP repositories in Terraform have a distinct shape. A project or two as the frame, a VPC network with regional subnetworks, GKE or Cloud Run for compute, BigQuery and Pub/Sub carrying the data side, and a long tail of service accounts and IAM members that outnumber everything else. A useful diagram keeps the first list and counts the second.

6 min readFor teams running Google Cloud through the google and google-beta providers

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

144/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Icons are what make a GCP diagram readable

A GCP architecture diagram is read by shape recognition before it is read as text. Datadef maps the google provider resource types to their real marks: google_storage_bucket to Cloud Storage, google_compute_network drawn as a VPC network, google_compute_instance to Compute Engine, google_container_cluster labelled GKE cluster, google_cloud_run_service and google_cloud_run_v2_service to Cloud Run, google_cloudfunctions_function and google_cloudfunctions2_function to Cloud Functions, google_sql_database_instance to Cloud SQL with its database_version and tier, google_bigquery_dataset to BigQuery, google_pubsub_topic to Pub/Sub, google_dataflow_job to Dataflow, google_dataproc_cluster to Dataproc, google_composer_environment to Cloud Composer.

A resource type outside the catalog is drawn with the Terraform mark rather than a wrong guess, on the principle that a wrong Google service icon lies while a Terraform mark is always true. A registry module gets its icon guessed from the source string instead: a source matching vpc or network alongside google or gcp resolves to the Compute Engine mark, one matching bucket resolves to Cloud Storage. The library behind this holds 226 Google Cloud marks; if you need the files for hand-drawn work they are in the GCP icon set.

Containment on GCP, drawn as containment

The classic failure of an auto-generated GCP diagram is turning every parent reference into an arrow, which produces a starburst pointing at the network. Several google relationships are containment rather than flow, and they are classified as such before drawing: a subnetwork referencing its network, a BigQuery table referencing its dataset_id, a Pub/Sub subscription referencing its topic.

Those render as grouping and placement, so arrows are reserved for things that actually move data or control. The identity tail is handled the same way in the other direction: google_service_account and google_project_iam_member are classified as wiring and counted in a note, because on a real project they can outnumber the infrastructure two to one.

Projects, environments, and where the values come from

Project ids on GCP almost always arrive through variables rather than literals, which is why a parser that only reads resource blocks produces a diagram full of unresolved placeholders. Variables are followed here instead of evaluated: through call-site bindings, through locals, and through tfvars files including per-environment ones, down to a literal value when one exists.

The environment names themselves are discovered from the tfvars layout, so a repository deploying to dev, staging, and prod produces one shape with the real per-environment numbers on the nodes rather than three near-identical pictures.

Connect the repository read-only from GitHub, GitLab, or Azure DevOps, pick the branch or tag, and the first diagram lands without a service account, without gcloud, without state, and without terraform init. It regenerates on a daily sync, embeds in a README or Confluence page as a live image, and exports as PNG or JPEG. For a data platform on GCP, the GCP data platform guide covers the layout conventions.

A worked example: seven resources, five nodes

Take an ingestion module that declares a BigQuery dataset and one table in it, a Pub/Sub topic with a subscription, a Dataflow job, a service account, and one project IAM binding. Seven resource blocks, and a naive graph draws seven equal squares plus the module and the provider.

The parse produces one zone for the module holding five nodes and one note. The dataset, the topic, and the Dataflow job are architecture and keep their marks. The table and the subscription are supporting detail, and because each references its parent through an attribute classified as containment, dataset_id for the table and topic for the subscription, they are placed inside their parents rather than pointing arrows at them. The service account and the IAM binding never become nodes at all; they are counted in a line under the zone.

The arrow that does get drawn is the one that matters, from the Dataflow job to what it reads, resolved through the reference in its own arguments and labelled with the attribute that carried it.

declared                          drawn
--------------------------------  ---------------------------------
google_bigquery_dataset.raw       BigQuery box
google_bigquery_table.events      inside the dataset (containment)
google_pubsub_topic.ingest        Pub/Sub box
google_pubsub_subscription.to_df  inside the topic (containment)
google_dataflow_job.stream        Dataflow box, arrow to the topic
google_service_account.runner     note: 2 wiring resources not worth
google_project_iam_member.bq            boxes: 1 service account,
                                        1 project iam member

FAQ

Does it need a Google Cloud service account or gcloud access?

No. The diagram is parsed from the .tf and .tfvars files of the connected repository, so the only credential involved is read access to the repository itself. There is no terraform init, no plan, no state file, no gcloud invocation, and nothing that reaches your Google Cloud project or its billing account.

Which google provider resource types get their own icons?

The architecture-bearing ones, including Cloud Storage, Compute Engine, VPC networks and subnetworks, GKE, Cloud Run, Cloud Functions, Cloud SQL, BigQuery datasets and tables, Pub/Sub topics and subscriptions, Dataflow, Dataproc, and Cloud Composer. Types outside the catalog are drawn with the Terraform mark instead of a wrong guess.

How are service accounts and IAM members handled?

As wiring. They are counted in a note rather than drawn as boxes, because on a real Google Cloud repository they routinely outnumber the actual infrastructure and would bury it. The full inventory stays in the generated module reference table.

Why are subnetworks not drawn with arrows to the network?

Because that relationship is containment, not flow. Each type declares which of its attributes point at a parent, and a reference carried by one of those attributes is drawn as placement rather than as an arrow. A subnetwork referencing its network, a BigQuery table referencing its dataset_id, and a Pub/Sub subscription referencing its topic are all handled that way, which keeps arrows meaningful.

Our project ids come from variables. Do they resolve?

Where a literal exists, yes. Variables are followed through call-site bindings, locals, variable defaults, and tfvars files including per-environment ones, until they reach a literal value. Where a value differs between environments and nothing decides which the node should carry, nothing is printed, because a single-environment value shown on a shared node would read as a fact about all of them.