Advanced Terraform Templates for Google Cloud Platform
Use these ready-made templates to collect evidence from your GCP environment using Advanced Mode
Written By Micah
Whether you're pulling together evidence for an upcoming audit or keeping your compliance program continuously up to date, Strike Graph's Terraform for Google Cloud Platform integration gives you a powerful way to collect system-generated evidence directly from your GCP environment. For more complex evidence needs, Advanced Mode gives you a access to the broader HCL capabilities to write multi-block configurations that go well beyond what's possible with the standard form.
This page contains a library of ready-to-use Advanced Mode templates organized by evidence category. Each template is pre-built around a specific compliance evidence need — just copy the code, replace var.project_id with your GCP project ID, review the data blocks, and submit.
Before you begin
These templates are provided as-is and for example purposes only. Your infrastructure is likely different than these examples expect, and so some level of customization should be expected.
Switching to Advanced Mode
When you open the Terraform for Google Cloud Platform evidence attachment modal, click the Advanced button in the top-right corner of the form to switch from the standard data block editor to the complex HCL support. You can switch back to Basic Mode at any time by clicking the Basic button in the same location.
Replacing var.project_id
Every template on this page uses var.project_id as a placeholder for your GCP project ID. Before submitting, replace every instance of var.project_id with your actual project ID as a quoted string. For example:
# Replace this: scope = "projects/${var.project_id}" # With something like this: scope = "projects/61233aF7a99"Customizing filename
In the locals block for each template, a filename must be declared. You can adjust this to your own desired naming convention. This will be the filename of the JSON attachment generated.
GCP prerequisites
Most templates on this page rely on the Cloud Asset API and a service account with specific IAM roles. Each template header lists its exact requirements, but the most common prerequisites are:
Cloud Asset API (
cloudasset.googleapis.com) enabled in the projectroles/cloudasset.vieweron the projectAdditional read-only roles depending on the resources being queried (e.g.,
cloudsql.instances.get,compute.instances.get)
All templates use read-only data sources — no resources are created or modified.
How chaining works in Advanced Mode
Several templates on this page use a chaining pattern: one data block fetches a list of resources, and a second data block uses for_each to look up detailed attributes for each item in that list. Terraform automatically handles the dependency ordering between the two blocks.
For example, this pattern first fetches all Cloud SQL instance names, then retrieves full settings for each:
data "google_cloud_asset_search_all_resources" "sql_instances" { scope = "projects/${var.project_id}" asset_types = ["sqladmin.googleapis.com/Instance"] } data "google_sql_database_instance" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.sql_instances.results : element(split("/", r.name), length(split("/", r.name)) - 1) ]) name = each.value project = "${var.project_id}" }Templates by category
Access control (user lists and admin access)
Operating system user list
Example evidence description: "Provide a system-generated list of all internal operating systems users."
This template enumerates all Compute Engine instances in the project and captures OS Login configuration and attached service accounts for each. When OS Login is enabled, OS-level user access is delegated to Google IAM bindings rather than local OS accounts.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get
Note: OS-level user accounts (e.g., /etc/passwd) are not accessible via Terraform data sources. This template captures the authentication mechanism in place for each instance, which serves as the system-generated equivalent. When OS Login is not enabled, SSH keys in instance or project metadata control access.
data "google_cloud_asset_search_all_resources" "compute_instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.compute_instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = ${var.project_id}}locals { filename = "operating-system-user-list" evidence = { metadata = { project_id = ${var.project_id} collected_at = timestamp() } instances = { for name, inst in data.google_compute_instance.details : name => { name = inst.name zone = inst.zone machine_type = inst.machine_type os_login_enabled = lookup(inst.metadata, "enable-oslogin", "NOT SET") os_login_2fa = lookup(inst.metadata, "enable-oslogin-2fa", "NOT SET") service_account = [for sa in inst.service_account : { email = sa.email scopes = sa.scopes }] tags = inst.tags labels = inst.labels } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.compute_instances.results) } }}Admin access to operating system
Example evidence description: "Provide a system-generated list of administrative users to the operating systems in scope."
Similar to the OS user list above, but focused on surfacing admin-level access signals — specifically the OS Login enablement status and attached service accounts for each Compute instance.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get
data "google_cloud_asset_search_all_resources" "compute_instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.compute_instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = ${var.project_id}}locals { filename = "admin-access-operating-system" evidence = { metadata = { project_id = ${var.project_id} collected_at = timestamp() } instances = { for name, inst in data.google_compute_instance.details : name => { name = inst.name zone = inst.zone machine_type = inst.machine_type metadata = inst.metadata os_login_enabled = lookup(inst.metadata, "enable-oslogin", "NOT SET") service_account = [for sa in inst.service_account : { email = sa.email scopes = sa.scopes }] tags = inst.tags labels = inst.labels can_ip_forward = inst.can_ip_forward } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.compute_instances.results) } }}Network/cloud user list
Example evidence description: "Provide a system-generated list of all users with access to the internal network or cloud environment."
This template enumerates all service accounts and service account keys in the project. In GCP, service account keys represent active programmatic credentials — each key is a distinct authentication path into the project.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "service_accounts" { scope = "projects/${var.project_id}" asset_types = ["iam.googleapis.com/ServiceAccount"]}data "google_cloud_asset_search_all_resources" "service_account_keys" { scope = "projects/${var.project_id}" asset_types = ["iam.googleapis.com/ServiceAccountKey"]}locals { filename = "network-cloud-user-list" evidence = { metadata = { project_id = ${var.project_id} collected_at = timestamp() } service_accounts = { for r in data.google_cloud_asset_search_all_resources.service_accounts.results : r.display_name => { name = r.display_name full_resource_name = r.name description = r.description additional_attributes = r.additional_attributes } } service_account_keys = { for r in data.google_cloud_asset_search_all_resources.service_account_keys.results : r.display_name => { name = r.display_name full_resource_name = r.name additional_attributes = r.additional_attributes } } summary = { total_service_accounts = length(data.google_cloud_asset_search_all_resources.service_accounts.results) total_keys = length(data.google_cloud_asset_search_all_resources.service_account_keys.results) note = "Service account keys represent active credentials." } }}Admin access to network/cloud
Example evidence description: "Provide a system-generated list of the administrative users to in-scope networks or cloud environments."
In GCP, service accounts are the primary programmatic identities with admin-level access. This template lists all service accounts in scope along with their metadata.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "service_accounts" { scope = "projects/${var.project_id}" asset_types = ["iam.googleapis.com/ServiceAccount"]}locals { filename = "admin-access-network-cloud" evidence = { service_accounts = { for r in data.google_cloud_asset_search_all_resources.service_accounts.results : r.display_name => { name = r.display_name full_resource_name = r.name description = r.description additional_attributes = r.additional_attributes } } summary = { total_service_accounts = length(data.google_cloud_asset_search_all_resources.service_accounts.results) note = "In GCP, service accounts are the primary programmatic identities with admin-level access." } }}Admin access to application
Example evidence description: "Provide a system-generated list of administrative users for applications in scope."
This template enumerates App Engine services and Cloud Run services in the project, which represent the application-layer resources in scope. Application admin access is determined by IAM bindings at the project or resource level.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "appengine_services" { scope = "projects/${var.project_id}" asset_types = ["appengine.googleapis.com/Service"]}data "google_cloud_asset_search_all_resources" "cloudrun_services" { scope = "projects/${var.project_id}" asset_types = ["run.googleapis.com/Service"]}locals { filename = "admin-access-application" evidence = { appengine_services = { for r in data.google_cloud_asset_search_all_resources.appengine_services.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location additional_attributes = r.additional_attributes } } cloudrun_services = { for r in data.google_cloud_asset_search_all_resources.cloudrun_services.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location additional_attributes = r.additional_attributes } } summary = { total_appengine = length(data.google_cloud_asset_search_all_resources.appengine_services.results) total_cloudrun = length(data.google_cloud_asset_search_all_resources.cloudrun_services.results) note = "Application admin access is determined by IAM bindings at the project or resource level. These services represent the application-layer resources in scope." } }}VPN user list / Admin access to VPN
Example evidence descriptions: "Provide a system-generated list of all users with access to the VPN" / "Provide a system-generated list of the administrative users to the VPN."
Important note: GCP Cloud VPN is a site-to-site IPsec solution — tunnels connect networks, not individual users. There is no concept of "VPN users" at the GCP layer, however this may be presented in evidence collected from other tools if you use a service such as NordLayer for VPN access. Admin access to VPN resources is controlled by IAM roles (e.g., roles/compute.networkAdmin) rather than individual user accounts. These templates enumerate the VPN infrastructure in scope.
For remote access VPN user lists (e.g., Cisco AnyConnect, Palo Alto GlobalProtect), consult the third-party VPN solution used by your organization.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "vpn_gateways" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/VpnGateway"]}data "google_cloud_asset_search_all_resources" "target_vpn_gateways" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/TargetVpnGateway"]}data "google_cloud_asset_search_all_resources" "external_vpn_gateways" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/ExternalVpnGateway"]}data "google_cloud_asset_search_all_resources" "vpn_tunnels" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/VpnTunnel"]}data "google_cloud_asset_search_all_resources" "routers" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Router"]}locals { filename = "admin-access-vpn" evidence = { ha_vpn_gateways = { for r in data.google_cloud_asset_search_all_resources.vpn_gateways.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description labels = r.labels additional_attributes = r.additional_attributes } } classic_vpn_gateways = { for r in data.google_cloud_asset_search_all_resources.target_vpn_gateways.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } external_vpn_gateways = { for r in data.google_cloud_asset_search_all_resources.external_vpn_gateways.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } vpn_tunnels = { for r in data.google_cloud_asset_search_all_resources.vpn_tunnels.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description labels = r.labels additional_attributes = r.additional_attributes } } cloud_routers = { for r in data.google_cloud_asset_search_all_resources.routers.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } summary = { ha_vpn_gateway_count = length(data.google_cloud_asset_search_all_resources.vpn_gateways.results) classic_vpn_gateway_count = length(data.google_cloud_asset_search_all_resources.target_vpn_gateways.results) external_vpn_gateway_count = length(data.google_cloud_asset_search_all_resources.external_vpn_gateways.results) vpn_tunnel_count = length(data.google_cloud_asset_search_all_resources.vpn_tunnels.results) cloud_router_count = length(data.google_cloud_asset_search_all_resources.routers.results) note = "GCP Cloud VPN provides site-to-site IPsec connectivity only. There are no individual VPN users — tunnels connect networks, not users. Admin access to VPN resources is controlled by project-level IAM roles (roles/compute.networkAdmin)." } }}Database user list / Admin access to database
Example evidence descriptions: "Provide a system-generated list of users with access to the internal database" / "Provide a system-generated list of the administrative users to all databases in scope."
This template enumerates all Cloud SQL instances and captures instance-level access controls, including authorized networks, SSL configuration, IAM authentication flags, and connection settings.
Important note: Terraform does not have a data source for individual Cloud SQL users (google_sql_user). To collect a complete per-instance user list, supplement this template with another evidence item that collects evidence such as the following gcloud command for each instance:
gcloud sql users list --instance=INSTANCE_NAME --project=PROJECT_IDPrerequisites: Cloud Asset API, roles/cloudasset.viewer, cloudsql.instances.get
data "google_cloud_asset_search_all_resources" "sql_instances" { scope = "projects/${var.project_id}" asset_types = ["sqladmin.googleapis.com/Instance"]}data "google_sql_database_instance" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.sql_instances.results : element(split("/", r.name), length(split("/", r.name)) - 1) ]) name = each.value project = ${var.project_id}}locals { filename = "access-to-database" evidence = { database_instances = { for inst_name, inst in data.google_sql_database_instance.details : inst_name => { instance_name = inst.name database_version = inst.database_version region = inst.region connection_name = inst.connection_name service_account = inst.service_account_email_address authorized_networks = try([ for net in inst.settings[0].ip_configuration[0].authorized_networks : { name = net.name value = net.value expiration_time = net.expiration_time } ], []) require_ssl = try(inst.settings[0].ip_configuration[0].require_ssl, null) ssl_mode = try(inst.settings[0].ip_configuration[0].ssl_mode, null) ipv4_enabled = try(inst.settings[0].ip_configuration[0].ipv4_enabled, null) private_network = try(inst.settings[0].ip_configuration[0].private_network, null) } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.sql_instances.results) note = "Individual database user accounts cannot be enumerated via Terraform data sources." } }}Code repository access list
Example evidence description: "Provide a system-generated list of all users with access to the source code repository."
This template enumerates Google Cloud Source Repositories within the project.
Important note: This template only covers Cloud Source Repositories. If your organization uses GitHub, GitLab, or Bitbucket, those platforms must be collected separately via their respective integrations in Strike Graph.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "source_repos" { scope = "projects/${var.project_id}" asset_types = ["sourcerepo.googleapis.com/Repo"]}locals { filename = "access-code-repository" evidence = { cloud_source_repos = { for r in data.google_cloud_asset_search_all_resources.source_repos.results : r.display_name => { name = r.display_name full_resource_name = r.name additional_attributes = r.additional_attributes } } summary = { total_repos = length(data.google_cloud_asset_search_all_resources.source_repos.results) } }}Role library
Example evidence description: "Provide documentation showing the transactions and functions allowed by each role."
This template retrieves all custom IAM roles defined at the project level and lists the included permissions for each. This serves as the role library or inventory showing what access each role grants.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, iam.roles.get
data "google_cloud_asset_search_all_resources" "custom_roles" { scope = "projects/${var.project_id}" asset_types = ["iam.googleapis.com/Role"]}data "google_iam_role" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.custom_roles.results : "projects/your-project-id/roles/${element(split("/", r.name), length(split("/", r.name)) - 1)}" ]) name = each.value}locals { filename = "role-library" evidence = { custom_roles = { for role_name, role in data.google_iam_role.details : role_name => { name = role.name title = role.title description = role.description stage = role.stage included_permissions = role.included_permissions } } summary = { total_custom_roles = length(data.google_cloud_asset_search_all_resources.custom_roles.results) note = "This lists project-level custom IAM roles and their permissions. Predefined roles (roles/*) are managed by Google and can be looked up individually if needed." } }}Network security
Firewall rules
Example evidence description: "Provide the firewall rule configurations for the cloud service network."
This template retrieves all VPC firewall rules in the project, including allowed/denied protocols and ports, direction, and network tags.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "firewall_rules" { scope = "projects/${var.project_id}"" asset_types = ["compute.googleapis.com/Firewall"]}locals { filename = "firewall-rules" evidence = { firewall_rules = { for r in data.google_cloud_asset_search_all_resources.firewall_rules.results : r.display_name => { name = r.display_name asset_type = r.asset_type location = r.location network_tags = r.network_tags labels = r.labels description = r.description additional_attributes = r.additional_attributes } } summary = { total_firewall_rules = length(data.google_cloud_asset_search_all_resources.firewall_rules.results) } }}Inbound and outbound traffic rules
Example evidence description: "Provide the list of all protocols, ports, and services that have been approved by management."
Similar to the firewall rules template above, this collects all VPC firewall rules and surfaces their traffic direction, allowed/denied protocols and ports, and source/destination ranges.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "firewall_rules" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Firewall"]}locals { filename = "inbound-outbound-traffic-rules" evidence = { traffic_rules = { for r in data.google_cloud_asset_search_all_resources.firewall_rules.results : r.display_name => { name = r.display_name description = r.description network_tags = r.network_tags labels = r.labels additional_attributes = r.additional_attributes } } summary = { total_rules = length(data.google_cloud_asset_search_all_resources.firewall_rules.results) note = "GCP firewall rules include direction (INGRESS/EGRESS), allowed/denied protocols and ports, source/destination ranges, and priority. Review additional_attributes for the full rule specification." } }}DMZ / network gateway configuration
Example evidence description: "Provide evidence of the network configurations that are in place to limit inbound traffic to only the authorized system or application components."
This template retrieves all VPC firewall rules to document what inbound traffic controls are in place. In GCP, INGRESS firewall rules are stateful and applied at the VPC network level.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "firewall_rules" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Firewall"]}locals { filename = "network-gateway-configuration" evidence = { dmz_inbound_rules = { for r in data.google_cloud_asset_search_all_resources.firewall_rules.results : r.display_name => { name = r.display_name description = r.description network_tags = r.network_tags labels = r.labels additional_attributes = r.additional_attributes } } summary = { total_firewall_rules = length(data.google_cloud_asset_search_all_resources.firewall_rules.results) note = "GCP firewall rules are stateful and applied at the VPC network level. INGRESS rules control inbound traffic. Rules with lower priority numbers take precedence. Review additional_attributes for allowed/denied protocols and ports." } }}Intrusion detection configuration
Example evidence description: "Provide the intrusion detection / intrusion prevention system (IDS/IPS) configurations. This should include the alerting rules."
This template captures Cloud Armor security policies (including WAF rules, rate limiting, and adaptive protection settings) and any Cloud IDS endpoints. Cloud Armor provides WAF, rate limiting, and adaptive DDoS protection at the load balancer layer; Cloud IDS provides network-level intrusion detection using Palo Alto Networks threat intelligence.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.securityPolicies.get
data "google_cloud_asset_search_all_resources" "security_policies" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/SecurityPolicy"]}data "google_compute_security_policy" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.security_policies.results : element(split("/", r.name), length(split("/", r.name)) - 1) ]) name = each.value project = "${var.project_id}"}data "google_cloud_asset_search_all_resources" "cloud_ids_endpoints" { scope = "projects/${var.project_id}" asset_types = ["ids.googleapis.com/Endpoint"]}locals { filename = "intrusion-detection" evidence = { cloud_armor_policies = { for policy_name, policy in data.google_compute_security_policy.details : policy_name => { name = policy.name description = policy.description type = policy.type adaptive_protection = try({ enabled = try( policy.adaptive_protection_config[0].layer_7_ddos_defense_config[0].enable, false ) }, { enabled = false }) rules = [ for rule in policy.rule : { priority = rule.priority action = rule.action description = rule.description preview = rule.preview match = try({ versioned_expr = try(rule.match[0].versioned_expr, null) config_src_ip_ranges = try(rule.match[0].config[0].src_ip_ranges, null) expr = try({ expression = rule.match[0].expr[0].expression }, null) }, null) rate_limit_options = try({ rate_limit_threshold = try({ count = rule.rate_limit_options[0].rate_limit_threshold[0].count interval_sec = rule.rate_limit_options[0].rate_limit_threshold[0].interval_sec }, null) conform_action = try(rule.rate_limit_options[0].conform_action, null) exceed_action = try(rule.rate_limit_options[0].exceed_action, null) }, null) } ] rule_count = length(policy.rule) } } cloud_ids_endpoints = { for r in data.google_cloud_asset_search_all_resources.cloud_ids_endpoints.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description } } summary = { cloud_armor_policy_count = length(data.google_cloud_asset_search_all_resources.security_policies.results) cloud_ids_endpoint_count = length(data.google_cloud_asset_search_all_resources.cloud_ids_endpoints.results) note = "Cloud Armor provides WAF, rate limiting, adaptive protection, and IP/geo access control at the load balancer layer. Cloud IDS provides network-level intrusion detection using Palo Alto Networks threat intelligence. DDoS protection is always-on for external HTTPS load balancers." } }}Authentication and session security
Remote system authentication (VPN configuration)
Example evidence description: "Provide the network server configuration enforcing VPN for remote connections."
This template documents the VPN infrastructure (gateways and tunnels) enforcing encrypted tunnels for remote connections. All GCP Cloud VPN tunnels use IKEv1 or IKEv2 with IPsec encryption — all traffic traversing VPN tunnels is encrypted in transit.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_cloud_asset_search_all_resources" "vpn_gateways" { scope = "projects/${var.project_id} asset_types = ["compute.googleapis.com/VpnGateway"]}data "google_cloud_asset_search_all_resources" "vpn_tunnels" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/VpnTunnel"]}locals { filename = "remove-system-auth" evidence = { vpn_tunnels = { for r in data.google_cloud_asset_search_all_resources.vpn_tunnels.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } vpn_gateways = { for r in data.google_cloud_asset_search_all_resources.vpn_gateways.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location additional_attributes = r.additional_attributes } } summary = { vpn_tunnel_count = length(data.google_cloud_asset_search_all_resources.vpn_tunnels.results) vpn_gateway_count = length(data.google_cloud_asset_search_all_resources.vpn_gateways.results) note = "All GCP Cloud VPN tunnels use IKEv1 or IKEv2 with IPsec encryption. HA VPN supports only IKEv2. Supported ciphers: AES-CBC-128, AES-CBC-256, AES-GCM-128, AES-GCM-256. Authentication uses pre-shared keys (PSK). All traffic traversing VPN tunnels is encrypted in transit." } }}Session protection (SSL/TLS configuration)
Example evidence description: "Provide evidence that communication protections are in place at the session level."
This template collects SSL policy configurations (minimum TLS version and cipher suites), HTTPS proxy resources, and SSL proxy resources. SSL policies control session-level protection for load balancers.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.sslPolicies.get
Tip: If no SSL policies are returned, your load balancers may be using GCP's default SSL policy (COMPATIBLE profile, TLS 1.0 minimum).
data "google_cloud_asset_search_all_resources" "ssl_policies" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/SslPolicy"]}data "google_cloud_asset_search_all_resources" "https_proxies" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/TargetHttpsProxy"]}data "google_cloud_asset_search_all_resources" "ssl_proxies" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/TargetSslProxy"]}data "google_compute_ssl_policy" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.ssl_policies.results : element(split("/", r.name), length(split("/", r.name)) - 1) ]) name = each.value project = "${var.project_id}"}locals { filename = "session-protection" evidence = { ssl_policies = { for policy_name, policy in data.google_compute_ssl_policy.details : policy_name => { name = policy.name description = policy.description min_tls_version = policy.min_tls_version profile = policy.profile custom_features = policy.custom_features enabled_features = policy.enabled_features } } https_proxies = { for r in data.google_cloud_asset_search_all_resources.https_proxies.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } ssl_proxies = { for r in data.google_cloud_asset_search_all_resources.ssl_proxies.results : r.display_name => { name = r.display_name full_resource_name = r.name location = r.location description = r.description additional_attributes = r.additional_attributes } } summary = { ssl_policy_count = length(data.google_cloud_asset_search_all_resources.ssl_policies.results) https_proxy_count = length(data.google_cloud_asset_search_all_resources.https_proxies.results) ssl_proxy_count = length(data.google_cloud_asset_search_all_resources.ssl_proxies.results) note = "SSL policies control minimum TLS version and cipher suites for HTTPS and SSL proxy load balancers. If no SSL policies are defined, GCP uses a default policy (COMPATIBLE profile, TLS 1.0 minimum). For stronger session protection, use TLS_1_2 minimum with MODERN or RESTRICTED profile." } }}Password and credential settings
Password settings (OS)
Example evidence description: "Provide the operating system layer password settings."
When OS Login is enabled, authentication is delegated to Google IAM — no local OS passwords are used, and password policies are inherited from Google Workspace / Cloud Identity. This template captures the OS Login configuration for each Compute instance.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get
data "google_cloud_asset_search_all_resources" "compute_instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.compute_instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "${var.project_id}"}locals { filename = "password-settings-os" evidence = { os_login_by_instance = { for name, inst in data.google_compute_instance.details : name => { instance_name = inst.name zone = inst.zone os_login_enabled = lookup(inst.metadata, "enable-oslogin", "NOT SET") os_login_2fa = lookup(inst.metadata, "enable-oslogin-2fa", "NOT SET") metadata = inst.metadata } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.compute_instances.results) note = "When OS Login is enabled, OS authentication is delegated to Google IAM — no local OS passwords are used. Password policies are inherited from Google Workspace / Cloud Identity. When OS Login is not enabled, SSH key-based authentication via instance metadata is used (no password-based login)." } }}Password settings (network/cloud)
Example evidence description: "Provide the network or cloud layer password settings."
GCP IAM does not use traditional username/password authentication at the cloud layer. Authentication is via Google accounts (Workspace/Cloud Identity) or service account keys. This template captures service account key metadata and project configuration to document the credential posture at the cloud layer.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer
data "google_project" "current" { project_id = "${var.project_id}"}data "google_cloud_asset_search_all_resources" "service_account_keys" { scope = "projects/${var.project_id}" asset_types = ["iam.googleapis.com/ServiceAccountKey"]}data "google_cloud_asset_search_all_resources" "project_resource" { scope = "projects/${var.project_id}" asset_types = ["cloudresourcemanager.googleapis.com/Project"]}locals { filename = "password-settings-network-cloud" evidence = { project_info = { project_id = data.google_project.current.project_id project_number = data.google_project.current.number name = data.google_project.current.name labels = data.google_project.current.labels } service_account_key_inventory = { for r in data.google_cloud_asset_search_all_resources.service_account_keys.results : r.display_name => { name = r.display_name full_resource_name = r.name additional_attributes = r.additional_attributes } } summary = { total_keys = length(data.google_cloud_asset_search_all_resources.service_account_keys.results) note = "GCP IAM uses Google account authentication (Workspace/Cloud Identity). Password policies and complexity rules are configured in the Google Admin console, not within GCP project settings." } }}Password settings (database)
Example evidence description: "Provide the database layer password settings."
Database password settings in Cloud SQL are managed via database_flags. This template collects all relevant flags, SSL/TLS settings, and activation policies for each Cloud SQL instance. Relevant flags vary by database engine — see the inline note in the output for details by engine type.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, cloudsql.instances.get
data "google_cloud_asset_search_all_resources" "sql_instances" { scope = "projects/${var.project_id}" asset_types = ["sqladmin.googleapis.com/Instance"]}data "google_sql_database_instance" "details" { for_each = toset([ for r in data.google_cloud_asset_search_all_resources.sql_instances.results : element(split("/", r.name), length(split("/", r.name)) - 1) ]) name = each.value project = "${var.project_id}"}locals { filename = "password-settings-database" evidence = { database_password_settings = { for inst_name, inst in data.google_sql_database_instance.details : inst_name => { instance_name = inst.name database_version = inst.database_version all_database_flags = try( { for flag in inst.settings[0].database_flags : flag.name => flag.value }, {} ) password_related_flags = try( { for flag in inst.settings[0].database_flags : flag.name => flag.value if can(regex("password|auth|login|credential", lower(flag.name))) }, {} ) require_ssl = try(inst.settings[0].ip_configuration[0].require_ssl, null) ssl_mode = try(inst.settings[0].ip_configuration[0].ssl_mode, null) activation_policy = try(inst.settings[0].activation_policy, null) tier = try(inst.settings[0].tier, null) note = "Password policies in Cloud SQL are configured via database_flags. MySQL: validate_password_policy, validate_password_length, default_password_lifetime. PostgreSQL: password_encryption, log_connections. SQL Server: Windows/contained database auth." } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.sql_instances.results) } }}Asset and infrastructure inventory
Asset inventory
Example evidence description: "Provide the current hardware and software asset lists."
This template enumerates cloud infrastructure assets across the project: Compute instances, persistent disks, VPC networks, and subnetworks — including machine types, disk sizes, labels, and attached service accounts.
Important note: This template covers cloud infrastructure assets only. Physical hardware (laptops, network equipment) and licensed software must be tracked in a separate asset management system.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get, compute.disks.get
data "google_cloud_asset_search_all_resources" "instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "your-project-id"}data "google_cloud_asset_search_all_resources" "disks" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Disk"]}data "google_compute_disk" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.disks.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "your-project-id"}data "google_cloud_asset_search_all_resources" "networks" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Network"]}data "google_cloud_asset_search_all_resources" "subnetworks" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Subnetwork"]}locals { filename = "cloud-infra-asset-inventory" evidence = { compute_instances = { for name, inst in data.google_compute_instance.details : name => { name = inst.name zone = inst.zone machine_type = inst.machine_type status = inst.current_status labels = inst.labels tags = inst.tags service_accounts = [for sa in inst.service_account : sa.email] boot_disk = try({ device_name = inst.boot_disk[0].device_name auto_delete = inst.boot_disk[0].auto_delete mode = inst.boot_disk[0].mode }, null) } } persistent_disks = { for disk_name, disk in data.google_compute_disk.details : disk_name => { name = disk.name zone = disk.zone type = disk.type size_gb = disk.size labels = disk.labels users = disk.users } } networks = { for r in data.google_cloud_asset_search_all_resources.networks.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } subnetworks = { for r in data.google_cloud_asset_search_all_resources.subnetworks.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.instances.results) total_disks = length(data.google_cloud_asset_search_all_resources.disks.results) total_networks = length(data.google_cloud_asset_search_all_resources.networks.results) total_subnetworks = length(data.google_cloud_asset_search_all_resources.subnetworks.results) note = "This inventory covers cloud infrastructure only. Physical hardware (laptops, network equipment) and licensed software are tracked in a separate asset management system." } }}Publicly accessible systems
Example evidence description: "Provide a list of all publicly accessible systems on the network."
This template identifies resources with potential public exposure: Compute instances with external IP addresses, reserved external and global IP addresses, and load balancer forwarding rules. It automatically filters to show only instances that have at least one external IP configured.
Important note: An external IP does not guarantee that a system is publicly reachable — VPC firewall rules may still block inbound traffic. Cross-reference this output with your firewall rules (evidence key 89) to determine actual exposure.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get
data "google_cloud_asset_search_all_resources" "instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "your-project-id"}data "google_cloud_asset_search_all_resources" "external_addresses" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Address"]}data "google_cloud_asset_search_all_resources" "global_addresses" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/GlobalAddress"]}data "google_cloud_asset_search_all_resources" "forwarding_rules" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/ForwardingRule"]}data "google_cloud_asset_search_all_resources" "global_forwarding_rules" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/GlobalForwardingRule"]}locals { filename = "public-systems" instances_with_external_ip = { for name, inst in data.google_compute_instance.details : name => { instance_name = inst.name zone = inst.zone machine_type = inst.machine_type external_ips = flatten([ for ni in inst.network_interface : [ for ac in ni.access_config : { nat_ip = ac.nat_ip network_tier = ac.network_tier } ] ]) tags = inst.tags labels = inst.labels } if length(flatten([ for ni in inst.network_interface : ni.access_config ])) > 0 } evidence = { instances_with_external_ip = local.instances_with_external_ip reserved_external_addresses = { for r in data.google_cloud_asset_search_all_resources.external_addresses.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } global_addresses = { for r in data.google_cloud_asset_search_all_resources.global_addresses.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } forwarding_rules = { for r in data.google_cloud_asset_search_all_resources.forwarding_rules.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } global_forwarding_rules = { for r in data.google_cloud_asset_search_all_resources.global_forwarding_rules.results : r.display_name => { name = r.display_name location = r.location description = r.description additional_attributes = r.additional_attributes } } summary = { instances_with_external_ip_count = length(local.instances_with_external_ip) reserved_external_address_count = length(data.google_cloud_asset_search_all_resources.external_addresses.results) global_address_count = length(data.google_cloud_asset_search_all_resources.global_addresses.results) forwarding_rule_count = length(data.google_cloud_asset_search_all_resources.forwarding_rules.results) global_forwarding_rule_count = length(data.google_cloud_asset_search_all_resources.global_forwarding_rules.results) note = "An external IP or forwarding rule indicates potential public exposure. Cross-reference with firewall rules to determine whether inbound traffic is actually permitted." } }}Encryption
Server encryption configuration
Example evidence description: "Provide the operating system/server or virtual machine (VM) encryption configuration for a sample server in scope."
This template collects disk-level encryption details for all persistent disks in the project, distinguishing between Google-managed keys, Customer-Managed Encryption Keys (CMEK via Cloud KMS), and Customer-Supplied Encryption Keys (CSEK). It also retrieves full configuration details for any KMS crypto keys in the project, including rotation period and algorithm.
Note: All GCP persistent disks are encrypted at rest by default using Google-managed AES-256 keys. KMS key details require roles/cloudkms.viewer in addition to the standard prerequisites.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.disks.get, roles/cloudkms.viewer (for KMS key details)
data "google_cloud_asset_search_all_resources" "disks" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Disk"]}data "google_compute_disk" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.disks.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "${var.project_id}"}data "google_cloud_asset_search_all_resources" "kms_keys" { scope = "projects/${var.project_id}" asset_types = ["cloudkms.googleapis.com/CryptoKey"]}data "google_kms_crypto_key" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.kms_keys.results : element(split("/", r.name), length(split("/", r.name)) - 1) => { name = element(split("/", r.name), length(split("/", r.name)) - 1) key_ring = join("/", slice( split("/", replace(r.name, "//cloudkms.googleapis.com/", "")), 0, 6 )) } } name = each.value.name key_ring = each.value.key_ring}locals { filename = "encryption-at-rest" evidence = { disk_encryption = { for disk_name, disk in data.google_compute_disk.details : disk_name => { name = disk.name zone = disk.zone type = disk.type size_gb = disk.size encryption_type = ( disk.disk_encryption_key != null && length(disk.disk_encryption_key) > 0 ? ( try(disk.disk_encryption_key[0].kms_key_self_link, null) != null ? "CMEK" : try(disk.disk_encryption_key[0].sha256, null) != null ? "CSEK" : "Google-managed" ) : "Google-managed" ) kms_key_self_link = try(disk.disk_encryption_key[0].kms_key_self_link, null) labels = disk.labels users = disk.users } } kms_keys = { for key_name, key in data.google_kms_crypto_key.details : key_name => { name = key.name purpose = key.purpose rotation_period = key.rotation_period labels = key.labels } } summary = { total_disks = length(data.google_cloud_asset_search_all_resources.disks.results) total_kms_keys = length(data.google_cloud_asset_search_all_resources.kms_keys.results) note = "All GCP persistent disks are encrypted at rest by default using Google-managed AES-256 keys. Disks marked 'CMEK' use customer-managed keys via Cloud KMS. Disks marked 'CSEK' use customer-supplied keys." } }}Authentication
Multi-factor authentication (MFA) evidence
Example evidence description: "Provide the settings that show that multi-factor authentication is turned on and enabled for internal users with access to critical systems."
This template captures two dimensions of MFA evidence within GCP: project metadata (which can reflect organizational security policies) and OS Login 2FA configuration per Compute instance. OS Login with 2FA enforces a second factor at the SSH layer for VM access.
Important note: MFA enforcement for Google accounts is configured at the Google Workspace / Cloud Identity level, not within GCP project settings. Full MFA evidence for your organization's user accounts requires the Google Admin console or Cloud Identity API, which are outside the scope of GCP Terraform data sources.
Prerequisites: Cloud Asset API, roles/cloudasset.viewer, compute.instances.get
data "google_project" "current" { project_id = "${var.project_id}"}data "google_cloud_asset_search_all_resources" "compute_instances" { scope = "projects/${var.project_id}" asset_types = ["compute.googleapis.com/Instance"]}data "google_compute_instance" "details" { for_each = { for r in data.google_cloud_asset_search_all_resources.compute_instances.results : "${element(split("/", r.name), length(split("/", r.name)) - 1)}/${r.location}" => { name = element(split("/", r.name), length(split("/", r.name)) - 1) location = r.location } } name = each.value.name zone = each.value.location project = "your-project-id"}data "google_cloud_asset_search_all_resources" "project_resource" { scope = "projects/${var.project_id}" asset_types = ["cloudresourcemanager.googleapis.com/Project"]}locals { filename = "cloud-mfa" evidence = { project_info = { project_id = data.google_project.current.project_id project_number = data.google_project.current.number name = data.google_project.current.name labels = data.google_project.current.labels } project_resource_metadata = { for r in data.google_cloud_asset_search_all_resources.project_resource.results : r.display_name => { name = r.display_name additional_attributes = r.additional_attributes } } os_login_2fa_by_instance = { for name, inst in data.google_compute_instance.details : name => { instance_name = inst.name os_login_enabled = lookup(inst.metadata, "enable-oslogin", "NOT SET") os_login_2fa = lookup(inst.metadata, "enable-oslogin-2fa", "NOT SET") } } summary = { total_instances = length(data.google_cloud_asset_search_all_resources.compute_instances.results) note = "MFA enforcement for Google accounts is configured at the Google Workspace / Cloud Identity level, not within GCP project settings. OS Login 2FA (enable-oslogin-2fa metadata) adds a second factor for SSH access to individual VMs. Full MFA evidence requires the Workspace Admin console or Cloud Identity API." } }}Troubleshooting
"No results returned" for a resource type
If a data block runs successfully but returns an empty list, check that:
The resource type is actually used in the project
The Cloud Asset API is enabled (
gcloud services enable cloudasset.googleapis.com)The service account has the correct IAM roles for the resource type in question
"Error: failed to get shared config profile" or provider errors
This typically means there is an issue with the GCP credential configuration attached to the integration. Navigate to the Integrations Manager, find your Terraform for Google Cloud Platform integration, and click Reconnect to re-establish the credential connection.
HCL validation errors on submit
Advanced Mode validates that your configuration contains at least one data block and does not include forbidden block types (resource, provider, module, terraform). Review the warning message returned to identify the specific issue. Common causes are mismatched braces or accidentally including a block type that isn't permitted.
Some attributes come back as null or empty
GCP resource attributes in Terraform are not always populated — particularly for older or less commonly used resource types. If a specific attribute is missing, check the Terraform Google Provider documentation for that data source to confirm whether the attribute is supported.