GCP’s mental model is the cleanest of the three big clouds: a project is a single, hard boundary that holds every resource, every IAM policy, every API quota, every billing line. There is no “tenant + subscription + resource group” hierarchy to memorise — you have organization → folder → project, and the project is the only scope most automation cares about. The auth model is just as clean: every API call is a Google Credential object, resolved through the Application Default Credentials chain (env vars → SA key file → metadata server → gcloud user creds), and the IAM primitive is “principal × role × resource” where principal is one of user:, serviceAccount:, group:, or domain:. Once you internalise project scope and ADC, the modules in google.cloud become mechanical: gcp_<service>_<resource> modules with consistent shape, declarative idempotency, and a dynamic inventory (gcp_compute) that maps GCE labels and metadata into Ansible groups.
This lesson is the exhaustive tour. We start with the GCP mental model — projects, folders, organisations, IAM scopes, labels — and the Application Default Credentials chain that every module uses. We walk google.cloud module-by-module, focusing on the modules you actually use in production: networking (gcp_compute_network, gcp_compute_subnetwork, gcp_compute_firewall, gcp_compute_router, gcp_compute_address), compute (gcp_compute_instance, gcp_compute_instance_template, gcp_compute_instance_group_manager, gcp_compute_target_pool), data (gcp_storage_bucket, gcp_sql_instance, gcp_secret_manager_secret, gcp_kms_key_ring), identity (gcp_iam_role, gcp_iam_service_account, gcp_iam_policy_binding), and Kubernetes (gcp_container_cluster, gcp_container_node_pool). We cover the four authentication modes — ADC default chain, explicit Service Account JSON, gcloud user creds, and Workload Identity Federation for keyless auth from GitHub Actions / GitLab CI / on-prem AAP — and the auth_kind decision matrix. We re-meet the google.cloud.gcp_compute dynamic inventory plugin from a deeper angle than the dynamic inventory lesson, focusing on the GCP-specific knobs (auth_kind, projects:, zones:, filters:, hostnames, vars_prefix). We finish on multi-project patterns, label-driven grouping, idempotency for the awkward modules, GKE-native ops, and packaging a GCP-aware Execution Environment for AAP. Everything targets current Ansible (ansible-core 2.17+, google.cloud 1.4+, the Google Auth Python SDK google-auth / google-auth-oauthlib / google-cloud-* packages, 2026), uses FQCN throughout, and ends with a free hands-on lab that uses a GCP free-tier project plus the always-free e2-micro VM.
Learning objectives
After this lesson you can:
- Explain GCP’s project-scoped model and how IAM, quotas, and APIs hang off it.
- Pick the right
auth_kindfor the host running Ansible:application(ADC),serviceaccount(key file),accesstoken(short-lived),machineaccount(GCE metadata). - Authenticate with a Service Account JSON key (and rotate cleanly) and with Workload Identity Federation (keyless from GitHub Actions / on-prem AAP).
- Drive
google.cloud’s headline modules across networking, compute, data, IAM, and GKE. - Configure the
gcp_computedynamic inventory plugin with the GCP-specific knobs. - Operate a multi-project estate with per-task
project:and per-source inventory files. - Write a label schema that turns the inventory into clean cross-cutting groups (
label_environment_prod,zone_us_central1_a). - Ship a GCP-aware Execution Environment for AAP.
Prerequisites & where this fits
You should already be comfortable with playbooks and tasks, variables and the precedence rules, Jinja templating, roles and collections, and dynamic inventory in general. The companion expert lessons that compound here are Ansible for AWS, Ansible for Azure, Ansible for Kubernetes (for GKE-native ops), and Hybrid Orchestration. In the Ansible Zero-to-Hero programme this is the Cloud expert (GCP) lesson and a textbook EX374-grade topic.
Core concepts
Five mental models carry the whole lesson.
1. Project is the boundary. Every GCP resource lives in exactly one project. Every IAM policy is scoped to organization, folder, project, or resource. Every API quota is per-project. Every API has to be enabled per-project (compute.googleapis.com, container.googleapis.com, …). For Ansible this means project: is a required parameter on essentially every module — set it once via module_defaults, not per task.
2. Application Default Credentials is the auth chain. Every Google client library walks the ADC chain: GOOGLE_APPLICATION_CREDENTIALS env (path to JSON key) → gcloud auth application-default login cache → GCE metadata server (when running on GCE) → external account (Workload Identity Federation). You set the environment; ADC resolves. The Ansible parameter auth_kind lets you pin a specific source.
3. Workload Identity Federation is the keyless future. WIF lets a non-GCP identity (a GitHub Actions OIDC token, a GitLab CI JWT, an AWS IAM role, an Azure managed identity, or any OIDC-issuing IdP) impersonate a GCP service account without a JSON key. The pattern: configure a Workload Identity Pool + Provider in GCP, federate a non-GCP identity, point Ansible at the resulting external_account credential file. Net result: GitHub Actions runs Ansible against GCP with zero long-lived secrets.
4. Labels are the inventory. GCP labels (environment=prod, role=web, team=platform) flow into the gcp_compute plugin’s keyed_groups. A consistent label schema turns “all hosts” into clean cross-cutting groups. GCP also supports resource-level labels on most things (VMs, disks, buckets, SQL instances), so the schema scales beyond compute.
5. Most gcp_* modules are present/absent only. Like Azure, GCP modules don’t expose the seven-state network-module API. state: present reconciles; state: absent deletes. The module computes the diff from current → desired internally. The two awkwardnesses are: (a) properties that are immutable after creation (machine_type changes need a stop, then resize, then start — handled by gcp_compute_instance_machine_type rather than re-running gcp_compute_instance); (b) the name-based identity is global within a project for some resources (firewall rules) and per-zone for others (instances).
Keep these terms straight: project (scope boundary), organization/folder (parent containers), service account (automation identity in GCP IAM), Application Default Credentials (ADC) (the standard auth-chain), auth_kind (Ansible’s selector: application/serviceaccount/accesstoken/machineaccount), Workload Identity Federation (keyless cross-cloud identity), gcp_compute plugin (dynamic inventory), labels (the inventory key), scopes (compute-engine OAuth scopes — almost always cloud-platform for full access).
The GCP mental model
Organization (acme.com)
├── Folder: production
│ ├── Project: prod-eu-app (id: prod-eu-app-7fa2)
│ │ ├── VPC: prod-eu-vpc
│ │ ├── VM: prod-web-eu-1 (zone: europe-west1-b)
│ │ └── SQL: prod-app-db (region: europe-west1)
│ └── Project: prod-us-app
├── Folder: staging
│ └── Project: stg-eu-app
└── Folder: sandbox
└── Project: sandbox-engineer-X
IAM bindings live at: organization | folder | project | resource
IAM principals: user:, serviceAccount:, group:, domain:
Authentication — the four modes
auth_kind |
When to use | What you set | Risk |
|---|---|---|---|
application (ADC) |
Default; let the chain resolve | GOOGLE_APPLICATION_CREDENTIALS env or gcloud auth application-default login |
Depends on what’s in the env |
serviceaccount |
Explicit JSON key file | service_account_file: parameter |
Long-lived JSON key on disk |
accesstoken |
Short-lived OAuth token (e.g. from gcloud auth print-access-token) |
access_token: parameter |
Token expires; refresh logic on you |
machineaccount |
Control node is a GCE VM | nothing — uses GCE metadata server | Almost nothing — no key on disk |
Pattern A — control node on GCE (recommended)
Run AAP on a GCE VM with an attached service account. Modules use auth_kind: machineaccount. Zero credentials anywhere:
- name: Create VPC (uses GCE metadata creds)
google.cloud.gcp_compute_network:
name: prod-eu-vpc
auto_create_subnetworks: false
auth_kind: machineaccount
project: prod-eu-app-7fa2
state: present
Pattern B — Workload Identity Federation (keyless from anywhere)
Configure WIF in GCP:
PROJECT_ID=prod-eu-app-7fa2
POOL_ID=github-actions-pool
PROVIDER_ID=github-provider
SA_EMAIL=ansible-automation@$PROJECT_ID.iam.gserviceaccount.com
# 1. create the pool + OIDC provider
gcloud iam workload-identity-pools create $POOL_ID \
--location=global --display-name="GitHub Actions"
gcloud iam workload-identity-pools providers create-oidc $PROVIDER_ID \
--location=global \
--workload-identity-pool=$POOL_ID \
--issuer-uri=https://token.actions.githubusercontent.com \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository"
# 2. allow your SA to be impersonated by the federation
gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
--role=roles/iam.workloadIdentityUser \
--member="principalSet://iam.googleapis.com/projects/$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')/locations/global/workloadIdentityPools/$POOL_ID/attribute.repository/myorg/myrepo"
In GitHub Actions:
# .github/workflows/ansible.yml
permissions:
id-token: write
contents: read
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github-actions-pool/providers/github-provider
service_account: ansible-automation@prod-eu-app-7fa2.iam.gserviceaccount.com
- run: ansible-playbook play.yml
The auth@v2 action writes an external_account credential file and points GOOGLE_APPLICATION_CREDENTIALS at it. Your Ansible play uses auth_kind: application — it picks up the federated identity transparently. Zero JSON keys committed anywhere.
Pattern C — Service Account JSON key (legacy / on-prem)
gcloud iam service-accounts create ansible-automation
gcloud iam service-accounts keys create ~/sa.json \
--iam-account=ansible-automation@$PROJECT_ID.iam.gserviceaccount.com
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:ansible-automation@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/editor
Then either:
- module_defaults:
group/google.cloud.gcp:
auth_kind: serviceaccount
service_account_file: /etc/ansible/sa.json
project: prod-eu-app-7fa2
…or set GOOGLE_APPLICATION_CREDENTIALS=/etc/ansible/sa.json and use auth_kind: application.
auth_kind decision matrix
| Where Ansible runs | auth_kind |
Why |
|---|---|---|
Engineer laptop with gcloud auth application-default login |
application |
Reads ADC cache |
| GitHub Actions / GitLab CI with WIF | application |
Reads federated external_account |
| AAP Controller on GCE | machineaccount |
Uses metadata server |
| AAP Container Group on GKE with Workload Identity (k8s) | machineaccount |
GKE Workload Identity injects the metadata bridge |
| AAP Controller on-prem with SA key | serviceaccount |
Explicit JSON key file |
| Short-lived demo / break-glass | accesstoken |
gcloud auth print-access-token |
google.cloud — the headline modules
# requirements.yml
collections:
- name: google.cloud
version: ">=1.4.0"
ansible-galaxy collection install -r requirements.yml
pip install requests google-auth google-auth-httplib2
| Module | Purpose | Idempotent? | Notes |
|---|---|---|---|
gcp_compute_network |
VPCs | Yes | auto_create_subnetworks: false for custom mode |
gcp_compute_subnetwork |
Subnets | Yes | Per-region |
gcp_compute_firewall |
Firewall rules | Yes | Project-scoped, applies to a network |
gcp_compute_address |
Static IPs | Yes | Regional or global |
gcp_compute_router |
Cloud Router | Yes | Required for Cloud NAT |
gcp_compute_instance |
VM instances | Idempotent on name: + zone |
Per-zone naming |
gcp_compute_instance_template |
Templates | Yes | The unit of MIG |
gcp_compute_instance_group_manager |
Managed Instance Groups | Yes | Regional or zonal |
gcp_compute_target_pool |
Target pools (legacy LB) | Yes | Use gcp_compute_backend_service for new LBs |
gcp_storage_bucket |
GCS buckets | Yes | Names globally unique |
gcp_sql_instance |
Cloud SQL | Yes | database_version: POSTGRES_16 etc. |
gcp_iam_role |
Custom IAM roles | Yes | Project or org scope |
gcp_iam_service_account |
Service accounts | Yes | Per-project |
gcp_kms_key_ring / gcp_kms_crypto_key |
KMS | Yes | Per-region |
gcp_secret_manager_secret |
Secret Manager | Yes | Use lookups to read secrets |
gcp_container_cluster |
GKE clusters | Idempotent on name: |
Heavy module — Terraform usually wins for cluster creation |
gcp_container_node_pool |
GKE node pools | Yes | Day-2 ops on existing clusters |
A canonical play with module_defaults:
- name: Provision a web tier in eu-prod
hosts: localhost
gather_facts: false
connection: local
module_defaults:
group/google.cloud.gcp:
auth_kind: machineaccount
project: prod-eu-app-7fa2
tasks:
- name: VPC
google.cloud.gcp_compute_network:
name: prod-eu-vpc
auto_create_subnetworks: false
state: present
- name: Subnet
google.cloud.gcp_compute_subnetwork:
name: web-eu-w1
region: europe-west1
ip_cidr_range: 10.42.1.0/24
network:
selfLink: projects/prod-eu-app-7fa2/global/networks/prod-eu-vpc
private_ip_google_access: true
state: present
- name: Firewall — allow HTTPS
google.cloud.gcp_compute_firewall:
name: allow-https
network:
selfLink: projects/prod-eu-app-7fa2/global/networks/prod-eu-vpc
direction: INGRESS
allowed:
- ip_protocol: tcp
ports: ["443"]
source_ranges: ["0.0.0.0/0"]
target_tags: ["web"]
state: present
- name: Web VM
google.cloud.gcp_compute_instance:
name: prod-web-eu-1
machine_type: e2-medium
zone: europe-west1-b
disks:
- auto_delete: true
boot: true
initialize_params:
source_image: projects/ubuntu-os-cloud/global/images/family/ubuntu-2404-lts
disk_size_gb: 30
disk_type: pd-balanced
network_interfaces:
- subnetwork:
selfLink: projects/prod-eu-app-7fa2/regions/europe-west1/subnetworks/web-eu-w1
access_configs:
- name: External NAT
type: ONE_TO_ONE_NAT
tags:
items: ["web"]
labels:
environment: prod
role: web
metadata:
ssh-keys: "ansible:{{ ssh_pubkey }}"
state: present
register: vm
Notice the selfLink: references — every cross-resource pointer in google.cloud is a selfLink (a fully-qualified URL into the GCP API), not just a name. This is verbose but unambiguous.
gcp_compute dynamic inventory — GCP-specific knobs
| Knob | Default | Purpose |
|---|---|---|
auth_kind |
(none — required) | Same selector as modules |
projects: |
(none — required) | List of project IDs to enumerate |
zones: |
(all) | Pre-filter by zone — performance lever |
filters: |
(none) | API-side filter expression (status = RUNNING AND labels.environment = prod) |
hostnames: |
[name] |
What field becomes the Ansible host name |
vars_prefix: |
gcp_ |
Prefix for hostvars |
compose: |
(none) | Same universal lever — Jinja-derived hostvars |
keyed_groups: |
(none) | Same universal lever — groups by key |
groups: |
(none) | Same universal lever — named groups via Jinja |
cache: / cache_plugin: / cache_timeout: |
(none) | Cache the inventory query |
Production-grade GCP inventory file:
# inventory/prod.gcp.yml
plugin: google.cloud.gcp_compute
auth_kind: machineaccount
projects:
- prod-eu-app-7fa2
- prod-us-app-3a91
zones:
- europe-west1-b
- europe-west1-c
- us-central1-a
filters:
- "status = RUNNING AND labels.environment = prod"
hostnames:
- name
compose:
ansible_host: networkInterfaces[0].networkIP
env: labels.environment | default('unknown')
role: labels.role | default('unknown')
keyed_groups:
- prefix: label
key: labels
- prefix: zone
key: zone | basename
- prefix: machine
key: machineType | basename
- prefix: project
key: project_id
groups:
prod_eu: labels.environment == 'prod' and zone is search('europe-')
needs_patch: labels.patched is not defined or labels.patched != 'true'
cache: true
cache_plugin: jsonfile
cache_connection: /var/cache/ansible_inventory
cache_timeout: 600
filters: is the single biggest performance lever — it’s a server-side filter expression in the GCE API’s filter language. Use it aggressively on big projects.
Multi-project patterns
GCP makes multi-project automation easy because projects are first-class.
Option A — single inventory, multiple projects
projects: in one inventory file lists all the projects you want to enumerate. The plugin stitches them together.
Option B — one inventory file per project
Cleaner for large fleets:
# inventory/prod-eu.gcp.yml
plugin: google.cloud.gcp_compute
projects: [prod-eu-app-7fa2]
auth_kind: machineaccount
# inventory/prod-us.gcp.yml
plugin: google.cloud.gcp_compute
projects: [prod-us-app-3a91]
auth_kind: machineaccount
Point inventory: at the directory; merging is automatic.
Option C — one play per project (for provisioning)
For provisioning plays (where you’re calling gcp_compute_* modules), one play per project with module_defaults setting project::
- import_playbook: plays/project-prod-eu.yml
- import_playbook: plays/project-prod-us.yml
- import_playbook: plays/project-stg.yml
Label strategy
Same shape as AWS tags / Azure tags — required first, governed via Organization Policy:
| Label | Required | Purpose |
|---|---|---|
environment |
Yes | prod/stg/dev |
role |
Yes | web/db/worker |
owner |
Yes | Team email (with hyphens — labels can’t have @) |
costcenter |
Yes | Finance attribution |
project_id_app |
Recommended | Distinguishes app-level project IDs |
patchgroup |
Recommended | Drives OS patching automation |
Enforce with the compute.requireOsLogin and custom Organization Policies that mandate label keys.
Idempotency & check-mode for awkward modules
| Module | Idempotency | Sharp edge |
|---|---|---|
gcp_compute_instance |
Idempotent on name: + zone |
machine_type: change requires stop → resize → start (use gcp_compute_instance_machine_type instead) |
gcp_compute_firewall |
Idempotent on name: (project-global) |
Firewalls are project-global; check naming collisions |
gcp_storage_bucket |
Globally unique name: |
Watch for collisions across all of GCP |
gcp_sql_instance |
Idempotent on name: |
Some properties require failover/restart |
gcp_container_cluster |
Idempotent on name: |
Don’t manage with Ansible if Terraform owns the cluster |
gcp_iam_* |
bindings: updates can be additive or replacing — read the docs |
GKE-native ops
For GKE-native ops (deploying workloads into a cluster) use the kubernetes.core collection, not google.cloud. The latter operates the cluster object (creates/scales node pools, upgrades the cluster); the former operates resources inside the cluster (Deployments, Services, ConfigMaps).
- name: Get GKE credentials and run a Deployment
hosts: localhost
gather_facts: false
tasks:
- name: Fetch kubeconfig
ansible.builtin.command:
cmd: gcloud container clusters get-credentials prod-eu --region europe-west1 --project prod-eu-app-7fa2
- name: Apply manifest
kubernetes.core.k8s:
state: present
src: manifests/web-deployment.yaml
Hands-on free lab — GCP free-tier
GCP gives every new account $300 credit + an always-free e2-micro VM in us-central1/-east1/-east4. The lab uses both.
# create a project (or use existing)
gcloud projects create my-ansible-lab --name="Ansible Lab"
gcloud config set project my-ansible-lab
gcloud auth application-default login
gcloud services enable compute.googleapis.com storage.googleapis.com
# install collection + deps
ansible-galaxy collection install google.cloud
pip install requests google-auth google-auth-httplib2
# play.yml
- hosts: localhost
gather_facts: false
connection: local
module_defaults:
group/google.cloud.gcp:
auth_kind: application
project: my-ansible-lab
tasks:
- name: VPC
google.cloud.gcp_compute_network:
name: lab-vpc
auto_create_subnetworks: false
state: present
- name: Subnet
google.cloud.gcp_compute_subnetwork:
name: lab-sub
region: us-central1
ip_cidr_range: 10.42.1.0/24
network:
selfLink: projects/my-ansible-lab/global/networks/lab-vpc
state: present
- name: Firewall
google.cloud.gcp_compute_firewall:
name: lab-allow-ssh
network:
selfLink: projects/my-ansible-lab/global/networks/lab-vpc
direction: INGRESS
allowed:
- ip_protocol: tcp
ports: ["22"]
source_ranges: ["0.0.0.0/0"]
state: present
- name: e2-micro (always-free)
google.cloud.gcp_compute_instance:
name: lab-vm
machine_type: e2-micro
zone: us-central1-a
disks:
- auto_delete: true
boot: true
initialize_params:
source_image: projects/debian-cloud/global/images/family/debian-12
disk_size_gb: 10
disk_type: pd-standard
network_interfaces:
- subnetwork:
selfLink: projects/my-ansible-lab/regions/us-central1/subnetworks/lab-sub
access_configs:
- name: External NAT
type: ONE_TO_ONE_NAT
labels:
environment: lab
role: vm
state: present
ansible-playbook play.yml --diff
ansible-playbook play.yml --diff # second run — changed=0
Inventory test:
# inv.gcp.yml
plugin: google.cloud.gcp_compute
auth_kind: application
projects: [my-ansible-lab]
hostnames: [name]
keyed_groups:
- prefix: label
key: labels
ansible-inventory -i inv.gcp.yml --graph
Tear down:
ansible localhost -m google.cloud.gcp_compute_instance \
-a "name=lab-vm zone=us-central1-a project=my-ansible-lab auth_kind=application state=absent"
Common mistakes & troubleshooting
ImportError: No module named google.auth. The Execution Environment doesn’t have google-auth. Bake requests, google-auth, google-auth-httplib2 into your EE.
PermissionDenied on every API call. The service account / federated identity lacks the right role. Start with roles/editor for lab; lock down to per-API roles (roles/compute.admin, roles/storage.admin) in production.
Inventory returns 0 hosts. Either: (a) enable_plugins doesn’t list google.cloud.gcp_compute; (b) the file isn’t named *.gcp.yml; © auth_kind doesn’t match what your environment actually has; (d) projects: is missing or wrong; (e) filters: excludes everything.
API not enabled. Run gcloud services enable compute.googleapis.com (and any others you need). Ansible doesn’t auto-enable APIs.
Firewall name collision across networks. Firewall names are project-global, not network-scoped. Use prefixes (prod-eu-allow-https).
gcp_compute_instance rebuilds the VM unexpectedly. You changed machine_type: or the boot disk. Use gcp_compute_instance_machine_type for size changes; boot disk is essentially write-once.
Workload Identity Federation token expired mid-play. Long plays past 1h can hit token expiry. Configure ttl: 3600s on the federation provider; for very long plays, refresh the token explicitly in a pre-task or split the work into multiple shorter plays.
shell: gcloud compute instances create … everywhere. Replace with gcp_compute_instance. The CLI is for humans; the module is for automation.
Best practices
auth_kind: machineaccountwhen the control node is GCE;auth_kind: application+ WIF when it’s anywhere else.module_defaultswithgroup/google.cloud.gcpto setauth_kind,project, and (where applicable)service_account_fileonce.projects:in inventory at the source level so each file targets one project.- Label schema enforced via Organization Policy.
filters:aggressively in the inventory — server-side filtering wins.- Cache the inventory.
cache: truewith a 5-10 minute timeout. - Pin collection versions.
google.cloud 1.4+. - Build a GCP EE with
google.cloud,requests,google-auth,google-auth-httplib2, and thegcloudCLI forkubernetes.coreGKE auth. - Leave cluster creation to Terraform; use Ansible for in-cluster ops.
- Mesh execution nodes inside the VPC. Cross-VPC firewall holes are a security anti-pattern.
Security notes
- No long-lived SA JSON keys in production. Workload Identity Federation is the standard for non-GCP CI; Workload Identity (the GKE feature) is the standard for GKE pods; instance metadata is the standard for GCE control nodes.
- Use Cloud Audit Logs. Every Ansible API call shows up in Admin Activity logs (and Data Access logs if enabled). Aggregate to BigQuery for forensics.
- Per-API roles, not
roles/editor. The Ansible service account should have only the roles it needs —roles/compute.admin,roles/storage.admin, etc. - Tag-based / label-based IAM conditions: write a role binding that allows
compute.instances.startonly when the instance labelteam == ${user.team}. Ansible can’t accidentally start the wrong team’s VMs. - Vault any database passwords. Or use Secret Manager and
lookup('community.google.gcp_secret_manager_secret', 'projects/X/secrets/Y/versions/latest'). - Air-gap-friendly EE. Push to Private Automation Hub.
- Block public GCS buckets by default.
iam_configuration: { uniform_bucket_level_access: true }on everygcp_storage_bucket.
Interview & exam Q&A
Q1. What’s the auth chain a google.cloud module walks?
The Application Default Credentials chain: explicit module params → GOOGLE_APPLICATION_CREDENTIALS env (path to JSON or external_account file) → gcloud auth application-default login cache → GCE metadata server. Pinned by auth_kind:.
Q2. Why prefer Workload Identity Federation to a Service Account JSON key? WIF is keyless: a federated OIDC identity (GitHub Actions OIDC token, AWS role, Azure MI) impersonates a GCP service account via short-lived tokens. No JSON key on disk, no rotation burden, audit trail in Cloud Audit Logs shows the federated principal.
Q3. What’s the difference between auth_kind: application and auth_kind: serviceaccount?
application resolves via ADC — let the chain pick the right credential. serviceaccount pins a specific JSON key file via service_account_file:. In production, prefer application so the same play works on engineer laptops, CI, and AAP.
Q4. Why is module_defaults with group/google.cloud.gcp important?
Every gcp_* module needs auth_kind and project. Without module_defaults, every task repeats them, and inevitable drift causes auth bugs. Set them once at play level.
Q5. How does the gcp_compute plugin handle multiple projects?
projects: is a list — the plugin enumerates VMs across each. Or use one inventory file per project; both files are merged when inventory: points at the directory.
Q6. What’s the most performance-impactful inventory knob?
filters: — server-side filter expression in GCE API syntax. A 5,000-instance project filtered by labels.environment = prod returns 200 rows in one API call.
Q7. When would you prefer google.cloud.gcp_container_cluster over Terraform for GKE?
For day-2 ops on a cluster that already exists (scaling node pools, enabling features, upgrading). For creation of new GKE clusters Terraform usually wins because of state-file dependency tracking.
Q8. Difference between GKE Workload Identity and Workload Identity Federation? Workload Identity (GKE feature) lets pods in a GKE cluster impersonate a GCP service account via the cluster’s metadata bridge. Workload Identity Federation (IAM feature) lets external identities (GitHub Actions, AWS, Azure) impersonate a GCP service account via OIDC. Different scopes, same goal: keyless auth.
Q9. What goes into a production GCP EE?
ansible-builder with google.cloud, the SDK (requests, google-auth, google-auth-httplib2, google-cloud-storage, google-cloud-secret-manager if you read secrets), the gcloud CLI (for GKE auth bridge), and your shared utility collections.
Q10. How do you handle multi-project IAM consistently?
Group IAM bindings by team in YAML, loop gcp_iam_policy_binding over them. The data file is your audit trail: who has what role at what scope.
Q11. When does gcp_compute_instance rebuild the VM?
When you change a property that isn’t in-place mutable: machine_type:, the boot disk’s source_image:, network attachments. Use the dedicated machine-type module for size changes; boot disk is write-once.
Q12. Why is selfLink: so prevalent in google.cloud?
Because GCP cross-resource references are URL-shaped — projects/X/global/networks/Y — and unambiguous across projects, regions, and zones. Verbose but precise; you’ll never accidentally reference the wrong VPC in another project.
Q13. How do you read a Secret Manager secret at play time?
community.google.gcp_secret_manager_secret lookup, or google.cloud.gcp_secret_manager_secret module with state: present for management; lookup for reading. Combine with module_defaults so credentials never appear in role defaults.
Q14. What’s the correct auth choice for AAP Container Groups in GKE?
Configure GKE Workload Identity on the cluster, annotate the EE pod’s ServiceAccount with iam.gke.io/gcp-service-account=<SA-email>, set auth_kind: machineaccount in plays. The cluster’s metadata bridge does the rest.
Quick check
- What
auth_kindshould an AAP Controller running on a GCE VM use? - How do you authenticate a GitHub Actions runner to GCP without a JSON key?
- Which inventory plugin knob does server-side filtering?
- Why is
project:always required ongcp_*modules? - What’s the relationship between
google.cloud.gcp_container_clusterandkubernetes.core?
(Answers: machineaccount; Workload Identity Federation with google-github-actions/auth@v2; filters: (uses GCE API filter syntax); because every resource lives in exactly one project — there is no implicit default; the former operates the cluster object, the latter operates resources inside the cluster.)
Exercise
With your free-tier project:
- Create a service account and either run
gcloud auth application-default login(laptop) or attach the SA to a tiny e2-micro and useauth_kind: machineaccount. - Write a play that creates a VPC, subnet, firewall, and an
e2-microalways-free VM with proper labels. - Add an
add_hoststep using the VM’s external IP and await_for_connectionto confirm SSH. - Build a
gcp_computeinventory file pointing at your project; verify--graphshowslabel_environment_labetc. - (Stretch) Set up Workload Identity Federation for a GitHub Actions repo and run the play from CI keylessly.
- Run with
--check --diff. Then for real. Then again —changed=0.
Certification mapping
| Cert | Coverage |
|---|---|
| EX374 — Red Hat Certified Specialist in Ansible Automation | Direct: cloud collections, dynamic inventory, EE. |
| Google Associate Cloud Engineer | Indirect: project / IAM / VPC mental model. |
| Google Professional Cloud DevOps Engineer | Direct: deployment automation, federated auth. |
Glossary
- Project — GCP scope boundary; every resource lives in one.
- Organization / folder — parent containers above project for IAM and policy.
- Application Default Credentials (ADC) — the standard auth chain.
auth_kind— Ansible’s auth-mode selector (application/serviceaccount/accesstoken/machineaccount).- Service account — automation identity in GCP IAM (with optional JSON key).
- Workload Identity Federation — keyless cross-cloud identity via OIDC.
- GKE Workload Identity — pod-level identity inside a GKE cluster.
selfLink— fully-qualified URL reference to a GCP resource.gcp_computeplugin — dynamic inventory plugin ingoogle.cloud.- Labels — key/value metadata on resources; the inventory key.
Next steps
You can now drive GCP from Ansible. With AWS, Azure, and GCP under your belt, continue with Ansible for Windows for the third major OS family, Ansible for Kubernetes for cluster-internal ops on GKE/EKS/AKS, and Hybrid Multi-Cloud Orchestration to compose all three clouds in a single workflow.