PCI DSS v4.0.1 - Google Cloud
This page lists all 115 policies in the PCI DSS v4.0.1 pack for Google Cloud, as published in pci-dss-google-cloud version 1.0.0.
Policies by control
1.2.8 — 1.2.8: Configuration files for NSCs are secured from unauthorized access and are kept consistent with active network configurations
1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- cloudsql-authorized-networks-restricted
- firewall-explicit-direction
- firewall-explicit-target-scoping
- firewall-no-http-ingress
- firewall-no-public-ingress
- firewall-restrict-rdp-ingress
- firewall-restrict-ssh-ingress
- gke-private-endpoint
1.3.2 — 1.3.2: Outbound traffic from the CDE is restricted
1.4.1 — 1.4.1: NSCs are implemented between trusted and untrusted networks *
1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- ai-platform-notebook-no-direct-internet-access
- bigquery-dataset-public-access-check
- bucket-public-access-prevention
- bucket-public-read-prohibited
- cloud-run-service-configuration-check
- cloud-sql-instance-not-publicly-accessible
- cloud-storage-bucket-iam-policy-grantee-check
- cloud-storage-bucket-public-write-prohibited
- cloudfunctions-iam-no-public
- cloudsql-authorized-networks-restricted
- compute-engine-instance-not-publicly-accessible
- database-migration-service-not-publicly-accessible
- dataproc-cluster-master-nodes-no-public-ip
- elasticsearch-in-vpc-only
- firewall-no-public-ingress
- firewall-restrict-rdp-ingress
- firewall-restrict-ssh-ingress
- gke-nodes-private
- gke-private-endpoint
- managed-instance-group-launch-template-public-ip-disabled
- private-service-connect
2.2 — 2.2 System components are configured and managed securely
3.2.1 — 3.2.1: Account data storage is kept to a minimum through implementation of data retention and disposal policies, procedures, and processes
3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
- ai-platform-endpoint-configuration-kms-key-configured
- ai-platform-notebook-instance-kms-key-configured
- artifactregistry-customer-kms
- bigquery-dataset-cmek
- bigquery-table-cmek
- bucket-cmek
- cloud-logging-encrypted
- cloud-sql-storage-encrypted
- cloudfunctions-kms-env-vars
- compute-instance-encrypted-attached-disk
- compute-instance-encrypted-boot-disk
- dataflow-kms
- elasticsearch-encrypted-at-rest
- filestore-cmek
- gke-secrets-encryption
- instance-template-disk-cmek
- persistent-disk-cmek
- pubsub-encrypted-kms
- secretmanager-secret-cmek
3.6.1 — 3.6.1: Procedures are defined and implemented to protect cryptographic keys used to protect stored account data against disclosure
3.6.1.3 — 3.6.1.3: Access to cleartext cryptographic key components is restricted to the fewest number of custodians necessary
3.7.1 — 3.7.1: Key-management policies and procedures are implemented to include generation of strong cryptographic keys used to protect stored account data
3.7.4 — 3.7.4: Key management policies and procedures are implemented for cryptographic key changes for keys that have reached the end of their cryptoperiod, as defined by the associated application vendor or key owner
3.7.5 — 3.7.5: Key management policies procedures are implemented to include the retirement, replacement, or destruction of keys used to protect stored account data *
4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
- certificate-manager-certificate-lifecycle-management
- cloud-build-trigger-source-repo-url-check
- cloud-cdn-origin-tls
- cloudsql-ssl
- database-migration-service-not-publicly-accessible
- firewall-no-http-ingress
- lb-managed-ssl-certificate
- load-balancer-http-to-https-redirection
- load-balancer-tls-https-listeners-only
6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
- app-engine-managed-updates-enabled
- cloudfunctions-runtime-versions
- cloudsql-patching
- compute-engine-instance-os-config-managed
- compute-engine-os-config-compliance-association-compliant
- compute-osconfig-vulnerability
- os-config-patch-coverage
- os-config-patch-deployment-configuration
6.4.2 — 6.4.2: For public-facing web applications, an automated technical solution is deployed that continually detects and prevents web-based attacks
7.2.1 — 7.2.1: An access control model is defined and includes granting access
- bucket-iam-least-privilege
- iam-no-broad-roles
- iam-no-custom-role-excessive-permissions
- iam-policy-no-statements-with-admin-access
- iam-policy-no-wildcard-permissions
- iam-user-no-direct-policies-check
- kms-key-iam
7.2.2 — 7.2.2: Access is assigned to users, including privileged users
- bucket-iam-least-privilege
- compute-engine-instance-service-account-attached
- iam-no-broad-roles
- iam-no-custom-role-excessive-permissions
- iam-policy-no-statements-with-admin-access
- iam-policy-no-wildcard-permissions
- iam-user-no-direct-policies-check
- pubsub-subscription-iam-least-privilege
- pubsub-topic-iam-least-privilege
7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
- bigquery-dataset-public-access-check
- bucket-public-access-prevention
- bucket-public-read-prohibited
- bucket-uniform-bucket-level-access
- cloud-run-service-configuration-check
- cloud-storage-bucket-iam-policy-grantee-check
- cloud-storage-bucket-public-write-prohibited
- cloudfunctions-iam-no-public
- persistent-disk-snapshot-not-publicly-restorable
- private-service-connect
- pubsub-subscription-iam-least-privilege
- pubsub-topic-iam-least-privilege
8.2.1 — 8.2.1: All users are assigned a unique ID before access to system components or cardholder data is allowed *
8.2.2 — 8.2.2: Group, shared, or generic IDs, or other shared authentication credentials are only used when necessary on an exception basis, and are managed
- cloud-sql-restricted-usernames
- compute-engine-instance-service-account-attached
- service-account-key
- service-account-restricted-names
8.3.2 — 8.3.2: Strong cryptography is used to render all authentication factors unreadable during transmission and storage on all system components
8.3.6 — 8.3.6: 6 If passwords/passphrases are used as authentication factors to meet Requirement 8.3.6, they meet the minimum level of complexity *
8.6.3 — 8.6.3: 3 Passwords/passphrases for any application and system accounts are protected against misuse
10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
- api-gateway-logging-configuration
- audit-logs-sink-enabled
- bigquery-audit-logging-enabled
- bigtable-change-streams
- bucket-access-logging
- cloud-armor-logging
- cloud-audit-logs-data-access-events-enabled
- cloud-build-logging
- cloud-cdn-logging-enabled
- cloud-sql-logging-enabled
- cloudfunctions-logging
- elasticsearch-logs-to-cloud-logging
- load-balancer-logging-enabled
- vpc-flow-logs-enabled
10.2.1.1 — 10.2.1.1: Audit logs capture all individual user access to cardholder data *
10.3.2 — 10.3.2: Audit log files are protected to prevent modifications by individuals *
10.3.3 — 10.3.3: Audit log files, including those for externalfacing technologies, are promptly backed up to a secure, central, internal log server(s) or other media that is difficult to modify
- audit-logs-sink-enabled
- cloud-audit-logs-multi-region-trail-enabled
- elasticsearch-logs-to-cloud-logging
10.4.1 — 10.4.1: Potentially suspicious or anomalous activities are quickly identified to minimize impact *
10.5.1 — 10.5.1: Retain audit log history for at least 12 months, with at least the most recent three months immediately available for analysis *
11.3.1 — 11.3.1: Internal vulnerability scans are performed
11.5.1 — 11.5.1: Intrusion-detection and/or intrusionprevention techniques are used to detect and/or prevent intrusions into the network *
11.5.2 — 11.5.2: A change-detection mechanism (for example, file integrity monitoring tools) is deployed
Policy details
ai-platform-endpoint-configuration-kms-key-configured
Severity: medium · Enforcement: advisory
Ensure AI Platform endpoint configuration uses Cloud KMS
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure the encryptionSpec property with a kmsKeyName when creating AI Platform endpoints. Ensure the KMS key is in the same region as the endpoint and grant the Vertex AI service account access to the key.
Pulumi TypeScript example:
import * as gcp from "@pulumi/gcp";
// Create a KMS key for endpoint encryption
const keyRing = new gcp.kms.KeyRing("ai-keyring", {
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("ai-endpoint-key", {
keyRing: keyRing.id,
rotationPeriod: "7776000s", // 90 days
});
// Create an AI Platform endpoint with CMEK
const endpoint = new gcp.vertex.AiEndpoint("my-endpoint", {
location: "us-central1",
displayName: "My Secure Endpoint",
// Configure customer-managed encryption key
encryptionSpec: {
kmsKeyName: cryptoKey.id,
},
});
ai-platform-notebook-instance-kms-key-configured
Severity: medium · Enforcement: advisory
Ensure AI Platform notebook instance uses Cloud KMS
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure the kmsKey property for notebook instances or virtualMachine.virtualMachineConfig.encryptionConfig.kmsKey for runtimes. Grant the Notebooks service account encrypt/decrypt permissions on the KMS key.
Pulumi TypeScript example:
import * as gcp from "@pulumi/gcp";
// Create a KMS key for notebook encryption
const keyRing = new gcp.kms.KeyRing("notebook-keyring", {
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("notebook-key", {
keyRing: keyRing.id,
rotationPeriod: "7776000s", // 90 days
});
// Option 1: Notebook Instance with CMEK
const notebookInstance = new gcp.notebooks.Instance("my-notebook", {
location: "us-central1-a",
machineType: "n1-standard-4",
// Configure customer-managed encryption key
kmsKey: cryptoKey.id,
vmImage: {
project: "deeplearning-platform-release",
imageFamily: "tf-latest-cpu",
},
});
// Option 2: Notebook Runtime with CMEK
const notebookRuntime = new gcp.notebooks.Runtime("my-runtime", {
location: "us-central1",
virtualMachine: {
virtualMachineConfig: {
machineType: "n1-standard-4",
dataDisk: {
initializeParams: {
diskSizeGb: 100,
diskType: "PD_STANDARD",
},
},
// Configure customer-managed encryption key
encryptionConfig: {
kmsKey: cryptoKey.id,
},
},
},
});
ai-platform-notebook-no-direct-internet-access
Severity: high · Enforcement: advisory
Ensure AI Platform notebook has no direct internet access
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Configure AI Platform notebook instances with noPublicIp: true and deploy within a VPC subnet. Use VPC Service Controls and Private Google Access for controlled connectivity to Google services without public IP addresses.
Pulumi TypeScript example:
import * as gcp from "@pulumi/gcp";
const notebook = new gcp.notebooks.Instance("my-notebook", {
location: "us-central1-a",
machineType: "n1-standard-4",
// Disable public IP to prevent direct internet access
noPublicIp: true,
// Deploy in a VPC subnet for network isolation
network: "projects/my-project/global/networks/my-vpc",
subnet: "projects/my-project/regions/us-central1/subnetworks/my-subnet",
// Configure boot disk
vmImage: {
project: "deeplearning-platform-release",
imageFamily: "tf-latest-cpu",
},
});
api-gateway-logging-configuration
Severity: high · Enforcement: advisory
Ensure API Gateway has proper logging configuration with service accounts and audit logs
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Configure API Gateway with proper logging setup:
- Create a dedicated service account for API Gateway backend authentication
- Configure ApiConfig with gatewayConfig.backendConfig.googleServiceAccount
- Grant the service account appropriate IAM roles for backend access (e.g., roles/run.invoker for Cloud Run)
- Enable audit logging for apigateway.googleapis.com service
Pulumi TypeScript example:
import * as gcp from "@pulumi/gcp";
// Step 1: Create service account
const apiGatewaySA = new gcp.serviceaccount.Account("api-gateway-sa", {
accountId: "api-gateway-sa",
displayName: "API Gateway Service Account",
});
// Step 2: Grant backend access permissions
const invokerBinding = new gcp.iam.Member("api-gateway-invoker", {
project: "my-project",
role: "roles/run.invoker", // Or roles/cloudfunctions.invoker for Cloud Functions
member: apiGatewaySA.member,
});
// Step 3: Configure API Gateway with service account
const apiConfig = new gcp.apigateway.ApiConfig("my-api-config", {
api: api.id,
apiConfigId: "my-config",
gatewayConfig: {
backendConfig: {
googleServiceAccount: apiGatewaySA.email, // Critical for logging
},
},
openapiDocuments: [{
document: {
path: "openapi.yaml",
contents: pulumi.asset.StringAsset(openapiSpec),
},
}],
});
// Step 4: Enable audit logging
const auditConfig = new gcp.projects.IAMAuditConfig("api-gateway-audit", {
project: "my-project",
service: "apigateway.googleapis.com",
auditLogConfigs: [
{ logType: "ADMIN_READ" },
{ logType: "DATA_READ" },
{ logType: "DATA_WRITE" },
],
});
app-engine-managed-updates-enabled
Severity: high · Enforcement: advisory
Ensure App Engine managed updates are enabled
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Configure App Engine versions with automaticScaling instead of manualScaling or basicScaling to enable managed updates:
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Standard App Version with automatic scaling
const app = new gcp.appengine.StandardAppVersion("app-auto-scaled", {
service: "myapp",
runtime: "nodejs20",
versionId: "v1",
entrypoint: { shell: "node ./app.js" },
deployment: {
zip: { sourceUrl: "https://storage.googleapis.com/bucket/app.zip" },
},
automaticScaling: {
maxConcurrentRequests: 10,
minIdleInstances: 1,
maxIdleInstances: 3,
},
});
// NON-COMPLIANT: Manual scaling prevents managed updates
// manualScaling: { instances: 3 } // Don't use this
// NON-COMPLIANT: Basic scaling limits managed updates
// basicScaling: { maxInstances: 5 } // Don't use this
artifactregistry-customer-kms
Severity: high · Enforcement: advisory
Require Artifact Registry repositories to use customer-managed Cloud KMS keys for encryption
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Configure Customer-Managed KMS Key
const repository = new gcp.artifactregistry.Repository("my-repository", {
repositoryId: "my-repo",
format: "DOCKER",
kmsKeyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Specify customer-managed KMS key
// ... other config
});
artifactregistry-immutable-images
Severity: medium · Enforcement: advisory
Require Artifact Registry repositories to disallow mutable images for security and compliance
- 2.2 — 2.2 System components are configured and managed securely
Remediation
Fix: Enable Immutable Tags for Docker Repositories
const repository = new gcp.artifactregistry.Repository("my-repository", {
repositoryId: "my-repo",
format: "DOCKER",
dockerConfig: {
immutableTags: true, // Prevent tag overwrites to ensure image integrity
},
// ... other config
});
audit-logs-sink-enabled
Severity: medium · Enforcement: advisory
Enable Cloud Audit Logs Cloud Logging integration for monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
- 10.3.3 — 10.3.3: Audit log files, including those for externalfacing technologies, are promptly backed up to a secure, central, internal log server(s) or other media that is difficult to modify
Remediation
Create log sinks to route audit logs to Cloud Logging destinations. Configure gcp.logging.ProjectSink, gcp.logging.OrganizationSink, or gcp.logging.FolderSink resources with filters to capture audit logs (e.g., filter including ‘cloudaudit.googleapis.com’). Route logs to Cloud Logging buckets, BigQuery, or Pub/Sub for centralized analysis and retention.
bigquery-audit-logging-enabled
Severity: high · Enforcement: advisory
Enable BigQuery audit logging for monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
- 10.2.1.1 — 10.2.1.1: Audit logs capture all individual user access to cardholder data *
Remediation
Configure projects.IAMAuditConfig resource with service: "bigquery.googleapis.com" and enable DATA_READ and DATA_WRITE log types in auditLogConfigs. Create a log sink (ProjectSink or OrganizationSink) with filter protoPayload.serviceName="bigquery.googleapis.com" to export audit logs for long-term retention.
Example TypeScript:
import * as gcp from "@pulumi/gcp";
const bigQueryAuditConfig = new gcp.projects.IAMAuditConfig("bigquery-audit", {
project: "your-project-id",
service: "bigquery.googleapis.com",
auditLogConfigs: [
{ logType: "DATA_READ" },
{ logType: "DATA_WRITE" },
{ logType: "ADMIN_READ" }
]
});
const bigQueryLogSink = new gcp.logging.ProjectSink("bigquery-logs", {
destination: "storage.googleapis.com/my-audit-logs-bucket",
filter: 'protoPayload.serviceName="bigquery.googleapis.com"',
uniqueWriterIdentity: true
});
bigquery-dataset-cmek
Severity: high · Enforcement: advisory
Ensures BigQuery datasets are encrypted with customer-managed keys (CMEK)
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure defaultEncryptionConfiguration with kmsKeyName to use a Cloud KMS key for dataset encryption.
bigquery-dataset-public-access-check
Severity: high · Enforcement: advisory
Ensure BigQuery datasets are not publicly accessible
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove public access from BigQuery datasets by ensuring IAM policies do not grant allUsers or allAuthenticatedUsers permissions. Review dataset accesses configuration and IAM bindings/members. Grant access only to specific service accounts, users, or groups based on least privilege principles.
Example TypeScript:
import * as gcp from "@pulumi/gcp";
const dataset = new gcp.bigquery.Dataset("secure-dataset", {
datasetId: "my_dataset",
location: "US",
accesses: [
{
role: "READER",
userByEmail: "analyst@example.com"
},
{
role: "roles/bigquery.dataEditor",
userByEmail: serviceAccount.email
}
// Do NOT include allUsers or allAuthenticatedUsers
]
});
// Using IAM binding for specific members only
const datasetViewer = new gcp.bigquery.DatasetIamBinding("dataset-viewer", {
datasetId: dataset.datasetId,
role: "roles/bigquery.dataViewer",
members: [
"user:jane@example.com",
"serviceAccount:myapp@project.iam.gserviceaccount.com"
// Do NOT use allUsers or allAuthenticatedUsers
]
});
bigquery-maintenance-settings-check
Severity: medium · Enforcement: advisory
Ensure BigQuery maintenance settings are configured
- 3.2.1 — 3.2.1: Account data storage is kept to a minimum through implementation of data retention and disposal policies, procedures, and processes
Remediation
Configure BigQuery datasets with appropriate maintenance settings:
import * as gcp from "@pulumi/gcp";
const dataset = new gcp.bigquery.Dataset("my-dataset", {
datasetId: "my_dataset",
description: "Dataset purpose and maintenance requirements",
defaultTableExpirationMs: 7776000000, // 90 days
defaultPartitionExpirationMs: 7776000000,
labels: {
environment: "production",
dataRetention: "90days",
},
});
bigquery-table-cmek
Severity: high · Enforcement: advisory
Ensures BigQuery tables are encrypted with customer-managed keys (CMEK)
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure encryptionConfiguration with kmsKeyName to use a Cloud KMS key for table encryption.
bigtable-change-streams
Severity: medium · Enforcement: advisory
Require Bigtable tables to have change streams enabled for change tracking
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Fix: Enable Change Stream Retention
const bigtableTable = new gcp.bigtable.Table("my-table", {
instanceName: "my-bigtable-instance",
changeStreamRetention: "72h0m0s", // Enable change streams with 3-day retention
// ... other config
});
bucket-access-logging
Severity: medium · Enforcement: advisory
Ensures Cloud Storage buckets have access logging enabled with logBucket and logObjectPrefix configured
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
bucket-cmek
Severity: high · Enforcement: advisory
Ensures Cloud Storage buckets use customer-managed KMS keys for encryption
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure bucket.encryption.defaultKmsKeyName with a valid customer-managed KMS key in the format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY
bucket-iam-least-privilege
Severity: high · Enforcement: advisory
Enforce least privilege access for Cloud Storage bucket IAM policies
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Fix: Use Specific Least Privilege Roles
// Instead of overly broad roles like storage.admin or storage.objectAdmin
// Use specific roles based on actual needs:
// For read-only access
const viewerBinding = new gcp.storage.BucketIAMBinding("viewer-binding", {
bucket: bucket.name,
role: "roles/storage.objectViewer", // Read-only access to objects
members: ["user:viewer@example.com"],
});
// For write-only access
const creatorBinding = new gcp.storage.BucketIAMBinding("creator-binding", {
bucket: bucket.name,
role: "roles/storage.objectCreator", // Write-only access (no read)
members: ["serviceAccount:app@project.iam.gserviceaccount.com"],
});
// Avoid these overly broad roles:
// - roles/storage.admin
// - roles/storage.objectAdmin
// - roles/owner
// - roles/editor
bucket-lifecycle
Severity: medium · Enforcement: advisory
Require Cloud Storage buckets to have lifecycle management policies configured
- 3.2.1 — 3.2.1: Account data storage is kept to a minimum through implementation of data retention and disposal policies, procedures, and processes
Remediation
Fix: Configure Lifecycle Rules
const bucket = new gcp.storage.Bucket("my-bucket", {
location: "US",
lifecycleRules: [
{
action: {
type: "Delete", // Delete old objects
},
condition: {
age: 365, // After 365 days
},
},
{
action: {
type: "SetStorageClass",
storageClass: "NEARLINE", // Archive to cheaper storage
},
condition: {
age: 90, // After 90 days
},
},
],
// ... other config
});
bucket-public-access-prevention
Severity: high · Enforcement: advisory
Ensures Cloud Storage buckets have public access prevention enforced
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Set publicAccessPrevention: "enforced" on the bucket.
bucket-public-read-prohibited
Severity: high · Enforcement: advisory
Ensure Cloud Storage bucket public read is prohibited
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove public read access from Cloud Storage buckets by ensuring IAM policies and ACLs do not grant allUsers or allAuthenticatedUsers read permissions. Set ‘publicAccessPrevention’ to ’enforced’ to prevent public access.
bucket-uniform-bucket-level-access
Severity: high · Enforcement: advisory
Ensures Cloud Storage buckets enable uniform bucket-level access
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Set uniformBucketLevelAccess: true on the bucket.
certificate-manager-certificate-lifecycle-management
Severity: medium · Enforcement: advisory
Ensure proper certificate lifecycle management to prevent expiration
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Use managed certificates for automatic renewal to prevent expiration-related outages. Managed certificates handle renewal automatically through DNS or Load Balancer authorization.
import * as gcp from "@pulumi/gcp";
// RECOMMENDED: Managed certificate with automatic renewal via DNS
const managedDnsCert = new gcp.certificatemanager.Certificate("managed-dns-cert", {
name: "managed-dns-cert",
description: "Auto-renewed certificate via DNS authorization",
managed: {
domains: ["example.com", "www.example.com"],
dnsAuthorizations: [dnsAuthResource.id]
}
});
// RECOMMENDED: Managed certificate with automatic renewal via Load Balancer
const managedLbCert = new gcp.certificatemanager.Certificate("managed-lb-cert", {
name: "managed-lb-cert",
description: "Auto-renewed certificate via Load Balancer",
managed: {
domains: ["api.example.com"]
// No dnsAuthorizations needed for Load Balancer authorization
}
});
// NOT RECOMMENDED: Self-managed certificate requires manual renewal
const selfManagedCert = new gcp.certificatemanager.Certificate("self-managed-cert", {
name: "custom-cert",
selfManaged: {
pemCertificate: certPem,
pemPrivateKey: keyPem
}
});
cloud-armor-logging
Severity: medium · Enforcement: advisory
Ensure Cloud Armor security policies are attached to backend services with logging enabled
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Enable logging on backend services that use Cloud Armor security policies. Cloud Armor events are logged through the backend service’s access logs, not the security policy itself.
import * as gcp from "@pulumi/gcp";
// Step 1: Create Cloud Armor security policy
const securityPolicy = new gcp.compute.SecurityPolicy("security-policy", {
name: "my-security-policy",
rules: [{
action: "deny(403)",
priority: 1000,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["192.168.1.0/24"]
}
}
}]
});
// Step 2: CRITICAL - Attach to backend service with logging enabled
const backend = new gcp.compute.BackendService("backend", {
name: "backend-service",
securityPolicy: securityPolicy.selfLink,
logConfig: {
enable: true, // REQUIRED for Cloud Armor logging
sampleRate: 1.0 // 1.0 = 100% logging, 0.5 = 50% sampling
}
});
cloud-audit-logs-data-access-events-enabled
Severity: high · Enforcement: advisory
Enable Cloud Audit Logs data access events for monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
- 10.2.1.1 — 10.2.1.1: Audit logs capture all individual user access to cardholder data *
Remediation
Configure projects.IAMAuditConfig resources with auditLogConfigs including DATA_READ and DATA_WRITE log types for services handling sensitive data to monitor unauthorized access.
Example - Enable Data Access Audit Logging:
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Enable data access logging for all services
const auditConfigAll = new gcp.projects.IAMAuditConfig("audit-all-services", {
project: "my-project-id",
service: "allServices", // Apply to all GCP services
auditLogConfigs: [
{
logType: "ADMIN_READ" // Administrative read operations
},
{
logType: "DATA_READ", // Compliant: Data read logging enabled
exemptedMembers: [] // No exemptions
},
{
logType: "DATA_WRITE" // Compliant: Data write logging enabled
}
]
});
// COMPLIANT: Enable data access logging for specific services
const auditConfigStorage = new gcp.projects.IAMAuditConfig("audit-storage", {
project: "my-project-id",
service: "storage.googleapis.com",
auditLogConfigs: [
{
logType: "DATA_READ", // Compliant: Monitor bucket/object reads
exemptedMembers: ["user:admin@example.com"] // Optional exemptions
},
{
logType: "DATA_WRITE" // Compliant: Monitor bucket/object writes
}
]
});
const auditConfigBigQuery = new gcp.projects.IAMAuditConfig("audit-bigquery", {
project: "my-project-id",
service: "bigquery.googleapis.com",
auditLogConfigs: [
{
logType: "DATA_READ" // Compliant: Monitor query operations
},
{
logType: "DATA_WRITE" // Compliant: Monitor data modifications
}
]
});
// NON-COMPLIANT: Only ADMIN_READ enabled, missing data access logging
const auditConfigIncomplete = new gcp.projects.IAMAuditConfig("audit-incomplete", {
project: "my-project-id",
service: "allServices",
auditLogConfigs: [
{
logType: "ADMIN_READ" // Only admin operations logged
// Missing DATA_READ and DATA_WRITE
}
]
});
// NON-COMPLIANT: No audit configuration at all
// (missing IAMAuditConfig resource)
cloud-audit-logs-integrity-monitoring-enabled
Severity: high · Enforcement: advisory
Ensure Cloud Audit Logs integrity monitoring is enabled
- 10.3.2 — 10.3.2: Audit log files are protected to prevent modifications by individuals *
Remediation
Configure log buckets with locked retention policies to ensure audit log integrity. Set retentionDays to at least 90 days and enable locked: true on gcp.logging.LogBucket resources. Locked retention policies prevent modification or deletion, ensuring audit logs remain trustworthy. Route audit logs to BigQuery or Cloud Storage for tamper-evident long-term storage.
cloud-audit-logs-multi-region-trail-enabled
Severity: high · Enforcement: advisory
Ensure Cloud Audit Logs multi-region trail is enabled
- 10.3.3 — 10.3.3: Audit log files, including those for externalfacing technologies, are promptly backed up to a secure, central, internal log server(s) or other media that is difficult to modify
Remediation
Configure Cloud Audit Logs to export to a multi-region or dual-region Cloud Storage bucket. Update the logging sink destination to use a bucket with location set to ‘US’, ‘EU’, ‘ASIA’, or a dual-region location for high availability.
cloud-build-logging
Severity: medium · Enforcement: advisory
Require Cloud Build triggers to have secure logging configurations
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Fix: Configure Secure Logging
const trigger = new cloudbuild.Trigger("my-trigger", {
build: {
logsBucket: "gs://my-logs-bucket/build-logs", // Configure centralized logs bucket
options: {
logging: "CLOUD_LOGGING_ONLY", // Use Cloud Logging for audit trails
},
// ... other config
},
// ... other config
});
cloud-build-trigger-envvar-gcpcred-check
Severity: high · Enforcement: advisory
Ensure Cloud Build trigger environment variables do not contain GCP credentials
- 8.6.3 — 8.6.3: 3 Passwords/passphrases for any application and system accounts are protected against misuse
Remediation
Remove GCP credentials from Cloud Build trigger substitutions, build.options.envs, and step environment variables. Store credentials in Secret Manager and reference them using availableSecrets configuration. Grant Cloud Build service account access to the secrets.
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Using Secret Manager references
const compliantTrigger = new gcp.cloudbuild.Trigger("compliant-trigger", {
location: "us-central1",
triggerTemplate: {
branchName: "main",
repoName: "my-repo",
},
substitutions: {
_ENV: "production",
_REGION: "us-central1",
},
filename: "cloudbuild.yaml",
});
// NON-COMPLIANT: Storing credentials in substitutions
const badTrigger = new gcp.cloudbuild.Trigger("bad-trigger", {
location: "us-central1",
triggerTemplate: {
branchName: "main",
repoName: "my-repo",
},
substitutions: {
_GCP_API_KEY: "AIzaSyD...", // Violates policy
_SERVICE_ACCOUNT_KEY: '{"type":"service_account",...}', // Violates policy
},
filename: "cloudbuild.yaml",
});
// NON-COMPLIANT: Storing credentials in build options
const badTriggerWithEnvs = new gcp.cloudbuild.Trigger("bad-trigger-envs", {
name: "my-trigger",
build: {
options: {
envs: [
"ENV = production",
"GCP_CREDENTIALS = {...}", // Violates policy
],
},
steps: [{
name: "gcr.io/cloud-builders/gcloud",
args: ["version"],
}],
},
});
cloud-build-trigger-source-repo-url-check
Severity: medium · Enforcement: advisory
Ensure Cloud Build trigger source repository URLs use secure protocols
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Update source repository URLs in sourceToBuild.uri, gitFileSource.uri, and other URL fields to use HTTPS protocol. Replace http://, git://, or ftp:// protocols with https:// or ssh:// (git@). Configure proper authentication for the repository connections.
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Using HTTPS protocol
const compliantTrigger = new gcp.cloudbuild.Trigger("compliant-trigger", {
name: "my-trigger",
location: "us-central1",
sourceToBuild: {
uri: "https://github.com/my-org/my-repo", // Secure HTTPS
ref: "refs/heads/main",
repoType: "GITHUB",
},
filename: "cloudbuild.yaml",
});
// COMPLIANT: Using SSH protocol
const compliantSshTrigger = new gcp.cloudbuild.Trigger("compliant-ssh-trigger", {
name: "my-ssh-trigger",
location: "us-central1",
sourceToBuild: {
uri: "git@github.com:my-org/my-repo.git", // Secure SSH
ref: "refs/heads/main",
repoType: "GITHUB",
},
filename: "cloudbuild.yaml",
});
// NON-COMPLIANT: Using insecure HTTP protocol
const badTrigger = new gcp.cloudbuild.Trigger("bad-trigger", {
name: "bad-trigger",
location: "us-central1",
sourceToBuild: {
uri: "http://github.com/my-org/my-repo", // Violates policy - insecure HTTP
ref: "refs/heads/main",
repoType: "GITHUB",
},
filename: "cloudbuild.yaml",
});
// NON-COMPLIANT: Using insecure git:// protocol
const badGitTrigger = new gcp.cloudbuild.Trigger("bad-git-trigger", {
name: "bad-git-trigger",
location: "us-central1",
sourceToBuild: {
uri: "git://github.com/my-org/my-repo.git", // Violates policy - insecure git://
ref: "refs/heads/main",
repoType: "GITHUB",
},
filename: "cloudbuild.yaml",
});
cloud-cdn-armor
Severity: medium · Enforcement: advisory
Require Cloud CDN to have Cloud Armor configuration for DDoS protection
- 6.4.2 — 6.4.2: For public-facing web applications, an automated technical solution is deployed that continually detects and prevents web-based attacks
Remediation
Fix: Attach Cloud Armor Security Policy to CDN
// Create a Cloud Armor security policy
const securityPolicy = new gcp.compute.SecurityPolicy("cdn-security-policy", {
rules: [{
action: "deny(403)",
priority: 1000,
match: {
versionedExpr: "SRC_IPS_V1",
config: {
srcIpRanges: ["9.9.9.0/24"],
},
},
description: "Deny access to example IP range",
}],
});
// For BackendService: attach security policy
const backendService = new gcp.compute.BackendService("cdn-backend", {
enableCdn: true,
securityPolicy: securityPolicy.id, // Attach Cloud Armor policy for DDoS protection
// ... other config
});
// For BackendBucket: attach edge security policy
const backendBucket = new gcp.compute.BackendBucket("cdn-bucket-backend", {
enableCdn: true,
edgeSecurityPolicy: securityPolicy.id, // Attach Cloud Armor policy for DDoS protection
// ... other config
});
cloud-cdn-logging-enabled
Severity: high · Enforcement: advisory
Ensure Cloud CDN has logging enabled for audit and monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Configure Backend Services with logConfig.enable: true when enableCdn: true. Set an appropriate sampleRate (0.0 to 1.0) to balance log volume with coverage. Logs are automatically sent to Cloud Logging for centralized management.
Pulumi TypeScript example:
import * as gcp from "@pulumi/gcp";
// Create a backend service with Cloud CDN enabled and logging configured
const backendService = new gcp.compute.BackendService("my-backend", {
name: "my-backend-service",
protocol: "HTTP",
timeoutSec: 30,
// Enable Cloud CDN
enableCdn: true,
// Configure CDN policy
cdnPolicy: {
cacheMode: "CACHE_ALL_STATIC",
defaultTtl: 3600,
clientTtl: 7200,
maxTtl: 86400,
negativeCaching: true,
},
// Enable logging for Cloud CDN
logConfig: {
enable: true,
sampleRate: 1.0, // Log 100% of requests (adjust based on traffic volume)
},
backends: [{
group: instanceGroup.id,
balancingMode: "UTILIZATION",
capacityScaler: 1.0,
}],
healthChecks: [healthCheck.id],
});
cloud-cdn-origin-tls
Severity: high · Enforcement: advisory
Require Cloud CDN to use secure TLS to origin
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Fix: Enable HTTPS Protocol for CDN Backend Service
const backendService = new gcp.compute.BackendService("cdn-backend", {
enableCdn: true,
protocol: "HTTPS", // Use HTTPS for secure communication to origin
// ... other config
});
cloud-functions-inside-vpc
Severity: high · Enforcement: advisory
Ensure Cloud Functions are inside VPC
- 1.3.2 — 1.3.2: Outbound traffic from the CDE is restricted
Remediation
Configure VPC connector for Cloud Functions:
// Cloud Functions v1
new gcp.cloudfunctions.Function("my-function", {
vpcConnector: "projects/my-project/locations/us-central1/connectors/my-connector",
vpcConnectorEgressSettings: "ALL_TRAFFIC", // or "PRIVATE_RANGES_ONLY"
});
// Cloud Functions v2
new gcp.cloudfunctionsv2.Function("my-function-v2", {
serviceConfig: {
vpcConnector: "projects/my-project/locations/us-central1/connectors/my-connector",
vpcConnectorEgressSettings: "ALL_TRAFFIC",
},
});
cloud-logging-encrypted
Severity: high · Enforcement: advisory
Ensure Cloud Logging is encrypted
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure Cloud Logging buckets with customer-managed encryption keys (CMEK) for enhanced security. On gcp.logging.LogBucket or gcp.logging.ProjectBucketConfig resources, set cmekSettings with a kmsKeyName pointing to a Cloud KMS key. While GCP encrypts all log data by default with Google-managed keys, CMEK provides additional control and compliance capabilities.
cloud-logging-retention-period-365
Severity: high · Enforcement: advisory
Ensure Cloud Logging log retention is 365 days or more
- 10.5.1 — 10.5.1: Retain audit log history for at least 12 months, with at least the most recent three months immediately available for analysis *
Remediation
Configure Cloud Logging buckets with retentionDays set to at least 365 days. Update gcp.logging.ProjectBucketConfig, gcp.logging.OrganizationBucketConfig, gcp.logging.FolderBucketConfig, or gcp.logging.BillingAccountBucketConfig resources to specify ‘retentionDays: 365’ or higher to maintain adequate audit trails for security investigations and compliance.
cloud-run-service-configuration-check
Severity: high · Enforcement: advisory
Ensure Cloud Run service configuration check
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove IAM bindings that grant allUsers the roles/run.invoker role. Set service ingress annotation run.googleapis.com/ingress to internal or internal-and-cloud-load-balancing in metadata.annotations. Grant invocation permissions only to specific service accounts or users.
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Service with restricted ingress
const compliantService = new gcp.cloudrun.Service("compliant-service", {
name: "secure-service",
location: "us-central1",
metadata: {
annotations: {
"run.googleapis.com/ingress": "internal-and-cloud-load-balancing",
},
},
template: {
spec: {
containers: [{
image: "gcr.io/my-project/my-image:latest",
}],
},
},
});
// COMPLIANT: Grant access to specific user
const specificAccess = new gcp.cloudrun.IamMember("specific-access", {
service: compliantService.name,
location: compliantService.location,
role: "roles/run.invoker",
member: "user:jane@example.com",
});
// NON-COMPLIANT: Public access via IAM
const publicAccess = new gcp.cloudrun.IamMember("public-access", {
service: compliantService.name,
location: compliantService.location,
role: "roles/run.invoker",
member: "allUsers", // Violates policy
});
// NON-COMPLIANT: Service without ingress restrictions
const badService = new gcp.cloudrun.Service("bad-service", {
name: "public-service",
location: "us-central1",
metadata: {
annotations: {
"run.googleapis.com/ingress": "all", // Violates policy
},
},
template: {
spec: {
containers: [{
image: "gcr.io/my-project/my-image:latest",
}],
},
},
});
cloud-security-scanner-enabled
Severity: high · Enforcement: advisory
Ensure Cloud Security Scanner is enabled for vulnerability management
- 11.3.1 — 11.3.1: Internal vulnerability scans are performed
Remediation
Enable the Web Security Scanner API service (websecurityscanner.googleapis.com) in your project. Configure scan configurations for your web applications using gcp.securityscanner.ScanConfig resources. Specify starting URLs, authentication details, and scan schedules to ensure comprehensive vulnerability coverage.
Example - Enable Web Security Scanner:
import * as gcp from "@pulumi/gcp";
// COMPLIANT: Enable Web Security Scanner API and configure scan
const scannerService = new gcp.projects.Service("scanner-api", {
service: "websecurityscanner.googleapis.com" // Compliant: API enabled
});
const scanConfig = new gcp.securityscanner.ScanConfig("web-scan", {
displayName: "Production Web App Scan",
startingUrls: ["https://myapp.example.com"], // Compliant: URLs configured
schedule: {
scheduleTime: "0 2 * * *", // Compliant: daily at 2 AM
intervalDurationDays: 1
},
maxQps: 5,
targetPlatforms: ["APP_ENGINE", "COMPUTE"]
}, { dependsOn: [scannerService] });
// NON-COMPLIANT: Web Security Scanner API not enabled
// (no gcp.projects.Service resource for websecurityscanner.googleapis.com)
// NON-COMPLIANT: Scanner API enabled but no scan configurations
const scannerServiceOnly = new gcp.projects.Service("scanner-only", {
service: "websecurityscanner.googleapis.com"
// Missing ScanConfig resources
});
// NON-COMPLIANT: Scan config without schedule
const scanConfigNoSchedule = new gcp.securityscanner.ScanConfig("no-schedule-scan", {
displayName: "Manual Scan Only",
startingUrls: ["https://app.example.com"]
// Missing schedule - won't run automatically
});
// NON-COMPLIANT: Scan config without starting URLs
const scanConfigNoUrls = new gcp.securityscanner.ScanConfig("no-urls-scan", {
displayName: "Incomplete Scan",
schedule: {
scheduleTime: "0 2 * * *"
}
// Missing startingUrls - scanner can't run
});
cloud-sql-iam-authentication
Severity: high · Enforcement: advisory
Require Cloud SQL instances to enable IAM authentication and Cloud SQL users to use IAM-based user types
- 8.2.1 — 8.2.1: All users are assigned a unique ID before access to system components or cardholder data is allowed *
Remediation
Fix: Use IAM Authentication
// For Cloud SQL instance: Enable IAM authentication
const sqlInstance = new gcp.sql.DatabaseInstance("my-sql-instance", {
databaseVersion: "POSTGRES_15",
settings: {
databaseFlags: [
{
name: "cloudsql_iam_authentication",
value: "on", // Enable IAM authentication
},
],
// ... other config
},
});
// For Cloud SQL users: Use IAM-based authentication
const sqlUser = new gcp.sql.User("my-sql-user", {
instance: sqlInstance.name,
name: "user@example.com",
type: "CLOUD_IAM_USER", // Use IAM user type instead of built-in
});
cloud-sql-instance-not-publicly-accessible
Severity: high · Enforcement: advisory
Restrict public access for Cloud SQL instances
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Disable public IP by setting ipv4Enabled to false and configure privateNetwork in ipConfiguration. Remove authorized networks that allow 0.0.0.0/0 access.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.sql.DatabaseInstance("my-db-instance", {
databaseVersion: "POSTGRES_15",
region: "us-central1",
settings: {
tier: "db-f1-micro",
ipConfiguration: {
ipv4Enabled: false,
privateNetwork: "projects/my-project/global/networks/my-vpc",
},
},
});
cloud-sql-logging-enabled
Severity: high · Enforcement: advisory
Enable Cloud SQL logging for monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Enable database logging flags via databaseFlags. For PostgreSQL: set log_connections, log_disconnections, log_checkpoints to ‘on’. For MySQL: enable general_log or slow_query_log.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.sql.DatabaseInstance("db-with-logging", {
databaseVersion: "POSTGRES_15",
region: "us-central1",
settings: {
tier: "db-f1-micro",
databaseFlags: [
{ name: "log_connections", value: "on" },
{ name: "log_disconnections", value: "on" },
{ name: "log_checkpoints", value: "on" },
],
},
});
cloud-sql-no-hardcoded-passwords
Severity: high · Enforcement: advisory
Prohibit hardcoded root passwords on Cloud SQL instances and hardcoded passwords on Cloud SQL users
- 8.6.3 — 8.6.3: 3 Passwords/passphrases for any application and system accounts are protected against misuse
Remediation
Fix: Remove Hardcoded Passwords
// For Cloud SQL instance: do not set rootPassword directly
const sqlInstance = new gcp.sql.DatabaseInstance("my-sql-instance", {
databaseVersion: "POSTGRES_15",
settings: {
// ... other config
},
// Do not set rootPassword - use auto-generated or Secret Manager
});
// For Cloud SQL users: avoid hardcoded passwords
const sqlUser = new gcp.sql.User("my-sql-user", {
instance: sqlInstance.name,
name: "user@example.com",
type: "CLOUD_IAM_USER", // IAM users need no password
// Do not set password field for IAM users
});
cloud-sql-password-policy-enabled
Severity: high · Enforcement: advisory
Require Cloud SQL instances that configure a password validation policy to have it enabled
- 8.3.6 — 8.3.6: 6 If passwords/passphrases are used as authentication factors to meet Requirement 8.3.6, they meet the minimum level of complexity *
Remediation
Fix: Enable Password Validation Policy
const sqlInstance = new gcp.sql.DatabaseInstance("my-sql-instance", {
databaseVersion: "POSTGRES_15",
settings: {
passwordValidationPolicy: {
enablePasswordPolicy: true, // Enable password validation
},
// ... other config
},
});
cloud-sql-restricted-usernames
Severity: high · Enforcement: advisory
Prohibit weak or commonly targeted Cloud SQL user names such as admin, root, test, user, guest, or anonymous
- 8.2.2 — 8.2.2: Group, shared, or generic IDs, or other shared authentication credentials are only used when necessary on an exception basis, and are managed
Remediation
Fix: Use Specific, Non-Obvious User Names
const sqlUser = new gcp.sql.User("my-sql-user", {
instance: sqlInstance.name,
name: "billing-api-service", // Specific, non-obvious name instead of "admin" or "root"
type: "CLOUD_IAM_USER",
});
cloud-sql-storage-encrypted
Severity: high · Enforcement: advisory
Ensure Cloud SQL storage is encrypted
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure customer-managed encryption keys (CMEK) by setting encryptionKeyName to a Cloud KMS key for enhanced security and key management control.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.sql.DatabaseInstance("my-db-instance", {
databaseVersion: "POSTGRES_15",
region: "us-central1",
encryptionKeyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key",
settings: {
tier: "db-f1-micro",
},
});
cloud-storage-bucket-iam-policy-grantee-check
Severity: high · Enforcement: advisory
Ensure Cloud Storage bucket IAM policy grantee check
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove IAM policy bindings that grant access to allUsers or allAuthenticatedUsers. Use Cloud IAM to grant access only to specific service accounts, users, or groups.
cloud-storage-bucket-public-write-prohibited
Severity: critical · Enforcement: advisory
Ensure Cloud Storage bucket public write is prohibited
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove IAM policy bindings that grant write permissions to allUsers or allAuthenticatedUsers. Ensure bucket ACLs do not grant WRITE or FULL_CONTROL to public principals.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const bucket = new gcp.storage.Bucket("secure-bucket", {
location: "US",
uniformBucketLevelAccess: true,
publicAccessPrevention: "enforced",
});
// Do NOT add IAM bindings like this:
// new gcp.storage.BucketIAMMember("bad-binding", {
// bucket: bucket.name,
// role: "roles/storage.objectCreator",
// member: "allUsers", // NEVER use allUsers/allAuthenticatedUsers
// });
cloudfunctions-iam-no-public
Severity: high · Enforcement: advisory
Require Cloud Functions IAM bindings to not grant public access
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Fix: Use Specific IAM Members
const iamMember = new gcp.cloudfunctions.FunctionIamMember("my-function-invoker", {
cloudFunction: myFunction.name,
role: "roles/cloudfunctions.invoker",
member: "serviceAccount:my-service@project.iam.gserviceaccount.com",
// ... other config
});
cloudfunctions-kms-env-vars
Severity: medium · Enforcement: advisory
Require Cloud Functions environment variables to be encrypted with Cloud KMS
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Encrypt Environment Variables with KMS
const func = new gcp.cloudfunctions.Function("my-function", {
environmentVariables: {
API_KEY: "sensitive-value",
DATABASE_URL: "postgres://...",
},
kmsKeyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Add KMS key for encryption
// ... other config
});
cloudfunctions-logging
Severity: low · Enforcement: advisory
Require Cloud Functions to have logging configuration enabled
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Fix: Enable Logging Configuration
const func = new gcp.cloudfunctions.Function("my-function", {
environmentVariables: {
LOG_FORMAT: "json", // Enable structured logging
LOG_LEVEL: "INFO", // Set appropriate log level
},
labels: {
"log-sink": "cloud-logging", // Configure log sink
},
// ... other config
});
cloudfunctions-runtime-versions
Severity: medium · Enforcement: advisory
Restrict Cloud Functions to approved runtime versions only
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Fix: Use Approved Runtime Version
const func = new gcp.cloudfunctions.Function("my-function", {
runtime: "nodejs20", // Use current supported runtime (e.g., nodejs20, python311, go121)
// ... other config
});
cloudsql-authorized-networks-restricted
Severity: high · Enforcement: advisory
Ensures Cloud SQL instances do not allow overly broad authorized networks
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Remove or replace overly broad authorized networks with specific IP ranges. Avoid 0.0.0.0/0, /8, and /16 CIDR blocks.
cloudsql-patching
Severity: medium · Enforcement: advisory
Require Cloud SQL instances to use managed service patching
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Fix: Configure Maintenance Window for Automatic Patching
const sqlInstance = new gcp.sql.DatabaseInstance("my-sql-instance", {
databaseVersion: "POSTGRES_15",
settings: {
maintenanceWindow: {
updateTrack: "stable", // Use stable update track for patching
day: 7, // Saturday (1=Sunday, 7=Saturday)
hour: 3, // 3 AM
},
// ... other config
},
});
cloudsql-ssl
Severity: high · Enforcement: advisory
Require Cloud SQL connections to use SSL/TLS encryption
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Fix: Require SSL/TLS for All Connections
const sqlInstance = new gcp.sql.DatabaseInstance("my-sql-instance", {
databaseVersion: "POSTGRES_15",
settings: {
ipConfiguration: {
sslMode: "ENCRYPTED_ONLY", // Require SSL/TLS for all connections
// ... other config
},
},
});
compute-engine-instance-detailed-monitoring-enabled
Severity: medium · Enforcement: advisory
Enable Compute Engine instance detailed monitoring
- 11.5.2 — 11.5.2: A change-detection mechanism (for example, file integrity monitoring tools) is deployed
Remediation
Enable shieldedInstanceConfig with enableIntegrityMonitoring set to true. Add metadata keys ’enable-guest-attributes: TRUE’ and ’enable-oslogin: TRUE’. Install the Ops Agent for comprehensive monitoring.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.compute.Instance("monitored-instance", {
machineType: "e2-medium",
zone: "us-central1-a",
shieldedInstanceConfig: {
enableIntegrityMonitoring: true,
enableVtpm: true,
},
metadata: {
"enable-guest-attributes": "TRUE",
"enable-oslogin": "TRUE",
},
bootDisk: { /* ... */ },
networkInterfaces: [{ /* ... */ }],
});
compute-engine-instance-in-vpc
Severity: high · Enforcement: advisory
Ensure Compute Engine instances are deployed in VPC networks
- 1.4.1 — 1.4.1: NSCs are implemented between trusted and untrusted networks *
Remediation
Deploy instances in custom VPC with explicit subnetwork:
new gcp.compute.Instance("my-instance", {
networkInterfaces: [{
network: customVpc.id,
subnetwork: customSubnet.id,
// Specify explicit VPC and subnet
}],
// ... other config
});
Avoid using legacy default networks.
compute-engine-instance-not-publicly-accessible
Severity: high · Enforcement: advisory
Ensure Compute Engine instances are not publicly accessible
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Remove accessConfigs from networkInterfaces to disable external IP assignment:
new gcp.compute.Instance("my-instance", {
networkInterfaces: [{
network: "my-vpc",
subnetwork: "my-subnet",
// accessConfigs: [], // Remove or set to empty array
}],
canIpForward: false,
});
Use Cloud NAT or load balancers for controlled external access.
compute-engine-instance-os-config-managed
Severity: high · Enforcement: advisory
Ensure Compute Engine instances are managed with OS Config
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Enable OS Config management by adding metadata key ’enable-osconfig: TRUE’ to enable centralized patch management, security updates, and configuration compliance.
import * as gcp from "@pulumi/gcp";
const instance = new gcp.compute.Instance("managed-instance", {
machineType: "e2-medium",
zone: "us-central1-a",
metadata: {
"enable-osconfig": "TRUE", // REQUIRED: Enable OS Config management
},
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
},
},
networkInterfaces: [{
network: "default",
accessConfigs: [{}],
}],
});
Benefits of OS Config:
- Automated security patching
- Vulnerability detection and reporting
- Configuration drift detection
- Centralized compliance monitoring
compute-engine-instance-service-account-attached
Severity: high · Enforcement: advisory
Ensure Compute Engine instances have service accounts attached
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
- 8.2.2 — 8.2.2: Group, shared, or generic IDs, or other shared authentication credentials are only used when necessary on an exception basis, and are managed
Remediation
Attach a custom service account with explicit scopes:
new gcp.compute.Instance("my-instance", {
serviceAccount: {
email: customServiceAccount.email,
scopes: [
"https://www.googleapis.com/auth/cloud-platform",
],
},
// ... other config
});
Avoid using the default compute service account.
compute-engine-instance-uses-os-login
Severity: high · Enforcement: advisory
Ensure Compute Engine instances enforce OS Login
- 8.2.1 — 8.2.1: All users are assigned a unique ID before access to system components or cardholder data is allowed *
Remediation
Add metadata keys to enable OS Login:
new gcp.compute.Instance("my-instance", {
metadata: {
"enable-oslogin": "TRUE",
"block-project-ssh-keys": "TRUE",
"enable-oslogin-2fa": "TRUE", // For production instances
},
// ... other config
});
This enables centralized SSH authentication via IAM.
compute-engine-os-config-compliance-association-compliant
Severity: high · Enforcement: advisory
Ensure OS Config managed instances have compliance association
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Add compliance labels such as ‘patch-group’ or ‘compliance-policy’ to instances with OS Config enabled. Enable OS Config by setting metadata ’enable-osconfig’ to ‘TRUE’ for proper vulnerability scanning and patch management tracking.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.compute.Instance("compliant-instance", {
machineType: "e2-medium",
zone: "us-central1-a",
metadata: {
"enable-osconfig": "TRUE",
},
labels: {
"patch-group": "production",
"compliance-policy": "cis-benchmark",
},
bootDisk: { /* ... */ },
networkInterfaces: [{ /* ... */ }],
});
compute-instance-encrypted-attached-disk
Severity: medium · Enforcement: advisory
Require Compute Engine instances to have encrypted attached disks
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Encrypt Attached Disks with CMEK
const instance = new compute.Instance("my-instance", {
attachedDisks: [{
source: myDisk.selfLink,
kmsKeySelfLink: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Use CMEK for attached disk encryption
}],
// ... other config
});
compute-instance-encrypted-boot-disk
Severity: medium · Enforcement: advisory
Require Compute Engine instances to have encrypted boot disks
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Enable Boot Disk Encryption with CMEK
const instance = new compute.Instance("my-instance", {
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
},
kmsKeySelfLink: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Use customer-managed encryption key
},
// ... other config
});
compute-osconfig-vulnerability
Severity: medium · Enforcement: advisory
Require OS Config agent for vulnerability management on compute instances
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
- 11.3.1 — 11.3.1: Internal vulnerability scans are performed
Remediation
Fix: Enable OS Config Agent
const instance = new compute.Instance("my-instance", {
metadata: {
"enable-osconfig": "TRUE", // Enable OS Config for vulnerability scanning
"enable-guest-attributes": "TRUE", // Enable guest attributes for older agent versions
},
// ... other config
});
database-migration-service-not-publicly-accessible
Severity: high · Enforcement: advisory
Prevent public accessibility of Database Migration Service instances
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Configure connection profiles to use private connectivity (VPC peering or Private Service Connect) instead of public IP addresses. Ensure SSL is configured with CA certificates.
Example - Secure Database Migration Service Connection Profiles:
import * as gcp from "@pulumi/gcp";
// COMPLIANT: MySQL connection profile with SSL and private IP
const mysqlCompliant = new gcp.databasemigrationservice.ConnectionProfile("mysql-private", {
location: "us-central1",
connectionProfileId: "mysql-private-profile",
displayName: "MySQL Private Connection",
mysql: {
host: "10.0.0.5", // Compliant: private IP address
port: 3306,
username: "migration-user",
password: sqldbUser.password,
ssl: { // Compliant: SSL configured
clientKey: sqlClientCert.privateKey,
clientCertificate: sqlClientCert.cert,
caCertificate: sqlClientCert.serverCaCert,
type: "SERVER_CLIENT"
}
}
});
// COMPLIANT: PostgreSQL with internal hostname and SSL
const postgresCompliant = new gcp.databasemigrationservice.ConnectionProfile("postgres-private", {
location: "us-central1",
connectionProfileId: "postgres-private-profile",
displayName: "PostgreSQL Private Connection",
postgresql: {
host: "db.internal.example.com", // Compliant: internal hostname
port: 5432,
username: "migration-user",
password: pgUser.password,
ssl: { // Compliant: SSL configured
clientCertificate: pgClientCert.cert,
caCertificate: pgClientCert.serverCaCert
}
}
});
// NON-COMPLIANT: MySQL connection without SSL
const mysqlNoSsl = new gcp.databasemigrationservice.ConnectionProfile("mysql-no-ssl", {
location: "us-central1",
connectionProfileId: "mysql-insecure",
mysql: {
host: "203.0.113.10", // Public IP
port: 3306,
username: "user",
password: "password"
// Missing ssl configuration
}
});
// NON-COMPLIANT: PostgreSQL with public IP and no SSL
const postgresPublic = new gcp.databasemigrationservice.ConnectionProfile("postgres-public", {
location: "us-central1",
connectionProfileId: "postgres-public",
postgresql: {
host: "198.51.100.20", // Non-compliant: public IP
port: 5432,
username: "user",
password: "password"
// Missing ssl configuration
}
});
dataflow-kms
Severity: high · Enforcement: advisory
Require Dataflow jobs and pipelines to use customer-managed Cloud KMS keys
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Configure KMS Encryption for Dataflow
const job = new dataflow.Job("my-job", {
kmsKeyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Use CMEK for data encryption
// ... other config
});
// For Dataflow Pipeline
const pipeline = new dataflow.Pipeline("my-pipeline", {
workload: {
dataflowFlexTemplateRequest: {
launchParameter: {
environment: {
kmsKeyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Use CMEK for data encryption
},
},
},
},
// ... other config
});
dataproc-cluster-master-nodes-no-public-ip
Severity: high · Enforcement: advisory
Restrict public access for Dataproc clusters by ensuring master nodes use internal IP addresses only
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Set clusterConfig.gceClusterConfig.internalIpOnly: true for the Dataproc cluster. Enable Private Google Access on the VPC subnet. Use Cloud NAT or VPN/Interconnect for required external connectivity. Configure firewall rules for internal communication.
import * as gcp from "@pulumi/gcp";
const privateDataprocCluster = new gcp.dataproc.Cluster("private-dataproc", {
name: "private-dataproc-cluster",
region: "us-central1",
clusterConfig: {
gceClusterConfig: {
// Disable external IPs for all nodes including master
internalIpOnly: true,
network: "projects/my-project/global/networks/my-vpc",
subnetwork: "projects/my-project/regions/us-central1/subnetworks/my-subnet",
zone: "us-central1-a",
// Optional: Configure service account with necessary permissions
serviceAccount: "dataproc-sa@my-project.iam.gserviceaccount.com",
serviceAccountScopes: [
"https://www.googleapis.com/auth/cloud-platform",
],
},
masterConfig: {
numInstances: 1,
machineType: "n2-standard-4",
diskConfig: {
bootDiskSizeGb: 100,
bootDiskType: "pd-standard",
},
},
workerConfig: {
numInstances: 2,
machineType: "n2-standard-4",
},
},
});
dataproc-kerberos-enabled
Severity: medium · Enforcement: advisory
Ensure Dataproc Kerberos is enabled
- 8.2.1 — 8.2.1: All users are assigned a unique ID before access to system components or cardholder data is allowed *
Remediation
Configure clusterConfig.securityConfig.kerberosConfig with enableKerberos: true. Store Kerberos credentials in Secret Manager and provide URIs for rootPrincipalPasswordUri, keyPasswordUri, keystorePasswordUri, and truststorePasswordUri. Configure realm and KDC settings as needed.
import * as gcp from "@pulumi/gcp";
// First, create secrets in Secret Manager for Kerberos passwords
const rootPassword = new gcp.secretmanager.Secret("kerberos-root-pwd", {
secretId: "dataproc-kerberos-root-password",
replication: { auto: {} },
});
const keystorePassword = new gcp.secretmanager.Secret("kerberos-keystore-pwd", {
secretId: "dataproc-kerberos-keystore-password",
replication: { auto: {} },
});
const truststorePassword = new gcp.secretmanager.Secret("kerberos-truststore-pwd", {
secretId: "dataproc-kerberos-truststore-password",
replication: { auto: {} },
});
const keyPassword = new gcp.secretmanager.Secret("kerberos-key-pwd", {
secretId: "dataproc-kerberos-key-password",
replication: { auto: {} },
});
// Create Dataproc cluster with Kerberos enabled
const kerberosCluster = new gcp.dataproc.Cluster("kerberos-dataproc", {
name: "secure-dataproc-cluster",
region: "us-central1",
clusterConfig: {
securityConfig: {
kerberosConfig: {
// Enable Kerberos authentication
enableKerberos: true,
// Reference secrets stored in Secret Manager
rootPrincipalPasswordUri: rootPassword.name.apply(name =>
`projects/my-project/secrets/${name}/versions/latest`
),
keyPasswordUri: keyPassword.name.apply(name =>
`projects/my-project/secrets/${name}/versions/latest`
),
keystorePasswordUri: keystorePassword.name.apply(name =>
`projects/my-project/secrets/${name}/versions/latest`
),
truststorePasswordUri: truststorePassword.name.apply(name =>
`projects/my-project/secrets/${name}/versions/latest`
),
// Optional: Configure realm and KDC
realm: "DATAPROC.EXAMPLE.COM",
// Optional: Cross-realm trust configuration
// crossRealmTrustRealm: "EXTERNAL.EXAMPLE.COM",
// crossRealmTrustKdc: "kdc.external.example.com",
},
},
gceClusterConfig: {
network: "projects/my-project/global/networks/my-vpc",
subnetwork: "projects/my-project/regions/us-central1/subnetworks/my-subnet",
},
},
});
elasticsearch-encrypted-at-rest
Severity: high · Enforcement: advisory
Ensure Elasticsearch is encrypted at rest
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure bootDisk.kmsKeySelfLink for boot disks and diskEncryptionKeyRaw or kmsKeySelfLink for attached disks. Create Cloud KMS keys in the same region as the instance and grant the Compute Engine service account encrypt/decrypt permissions. All disks are encrypted by default with Google-managed keys; use CMEK for enhanced control.
import * as gcp from "@pulumi/gcp";
// Create KMS key ring and key for disk encryption
const keyRing = new gcp.kms.KeyRing("elasticsearch-keyring", {
name: "elasticsearch-disk-encryption",
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("elasticsearch-key", {
name: "elasticsearch-disk-key",
keyRing: keyRing.id,
rotationPeriod: "7776000s", // 90 days
});
// Grant Compute Engine service account permission to use the key
const computeSA = gcp.compute.getDefaultServiceAccount({});
const keyIAM = new gcp.kms.CryptoKeyIAMMember("compute-encrypter-decrypter", {
cryptoKeyId: cryptoKey.id,
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
member: computeSA.then(sa => `serviceAccount:${sa.email}`),
});
// Elasticsearch instance with CMEK-encrypted disks
const encryptedElasticsearch = new gcp.compute.Instance("encrypted-elasticsearch", {
name: "elasticsearch-encrypted",
machineType: "n2-standard-8",
zone: "us-central1-a",
labels: {
app: "elasticsearch",
},
// Boot disk with customer-managed encryption key
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
size: 100,
},
kmsKeySelfLink: cryptoKey.id,
},
// Attached data disk with encryption
attachedDisks: [{
source: new gcp.compute.Disk("elasticsearch-data-disk", {
name: "elasticsearch-data",
zone: "us-central1-a",
size: 500,
type: "pd-ssd",
diskEncryptionKey: {
kmsKeySelfLink: cryptoKey.id,
},
}).selfLink,
kmsKeySelfLink: cryptoKey.id,
}],
networkInterfaces: [{
network: "default",
}],
});
// Alternative: Create separate persistent disk with encryption
const elasticsearchDataDisk = new gcp.compute.Disk("elasticsearch-persistent-disk", {
name: "elasticsearch-data-disk",
zone: "us-central1-a",
size: 1000, // 1TB for Elasticsearch data
type: "pd-ssd",
diskEncryptionKey: {
kmsKeySelfLink: cryptoKey.id,
},
});
elasticsearch-in-vpc-only
Severity: high · Enforcement: advisory
Ensure Elasticsearch is in VPC only
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Remove accessConfigs from networkInterfaces to disable external IP assignment for instances running Elasticsearch. Ensure instances are deployed within a VPC subnet. Access Elasticsearch through private networking or VPN/Interconnect only.
import * as gcp from "@pulumi/gcp";
// Elasticsearch instance with no external IP (VPC-only access)
const elasticsearchInstance = new gcp.compute.Instance("elasticsearch-node", {
name: "elasticsearch-node-1",
machineType: "n2-standard-8",
zone: "us-central1-a",
labels: {
environment: "production",
},
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
size: 100,
},
},
networkInterfaces: [{
network: "projects/my-project/global/networks/my-vpc",
subnetwork: "projects/my-project/regions/us-central1/subnetworks/private-subnet",
// IMPORTANT: No accessConfigs means no external IP
// Instance is only accessible within VPC or via VPN/Interconnect
}],
// Optional: Add startup script to install Elasticsearch
metadataStartupScript: `#!/bin/bash
# Install and configure Elasticsearch
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/8.x/apt stable main" | tee /etc/apt/sources.list.d/elastic-8.x.list
apt-get update && apt-get install -y elasticsearch
systemctl enable elasticsearch
systemctl start elasticsearch
`,
serviceAccount: {
email: "elasticsearch-sa@my-project.iam.gserviceaccount.com",
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
},
});
// Optional: Create firewall rule to allow Elasticsearch access from within VPC
const elasticsearchFirewall = new gcp.compute.Firewall("elasticsearch-internal", {
network: "projects/my-project/global/networks/my-vpc",
allows: [{
protocol: "tcp",
ports: ["9200", "9300"],
}],
sourceRanges: ["10.0.0.0/8"], // Only allow from private IP ranges
targetTags: ["elasticsearch"],
});
elasticsearch-logs-to-cloud-logging
Severity: medium · Enforcement: advisory
Ensure instances have proper Cloud Logging configuration
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
- 10.3.3 — 10.3.3: Audit log files, including those for externalfacing technologies, are promptly backed up to a secure, central, internal log server(s) or other media that is difficult to modify
Remediation
Configure Compute Engine instances with the Ops Agent for log forwarding. Ensure service accounts have proper scopes for Cloud Logging API access. Install logging agents via startup scripts or custom images.
import * as gcp from "@pulumi/gcp";
// Service account with logging permissions
const loggingSA = new gcp.serviceaccount.Account("logging-sa", {
accountId: "instance-logger",
displayName: "Instance Logging Service Account",
});
// Grant logging.logWriter role to service account
const loggingRole = new gcp.projects.IAMMember("logging-role", {
project: "my-project",
role: "roles/logging.logWriter",
member: loggingSA.email.apply(email => `serviceAccount:${email}`),
});
// Instance with proper logging configuration
const instanceWithLogging = new gcp.compute.Instance("instance-with-logging", {
name: "app-instance",
machineType: "n2-standard-4",
zone: "us-central1-a",
bootDisk: {
initializeParams: {
image: "debian-cloud/debian-11",
},
},
networkInterfaces: [{
network: "default",
subnetwork: "default",
}],
serviceAccount: {
email: loggingSA.email,
// IMPORTANT: Include cloud-platform scope for logging APIs
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
},
// Install Ops Agent for log forwarding
metadataStartupScript: `#!/bin/bash
# Install Google Cloud Ops Agent
curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
bash add-google-cloud-ops-agent-repo.sh --also-install
# Configure Ops Agent for application logs
cat > /etc/google-cloud-ops-agent/config.yaml <<EOF
logging:
receivers:
app_logs:
type: files
include_paths:
- /var/log/app/*.log
- /var/log/app/*.json
processors:
app_parser:
type: parse_json
service:
pipelines:
app_pipeline:
receivers: [app_logs]
processors: [app_parser]
EOF
# Restart Ops Agent to apply configuration
systemctl restart google-cloud-ops-agent
`,
// Alternative: Use metadata for logging configuration
metadata: {
"google-logging-enabled": "true",
"google-monitoring-enabled": "true",
},
});
filestore-cmek
Severity: high · Enforcement: advisory
Ensure Cloud Filestore is encrypted
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
firewall-explicit-direction
Severity: high · Enforcement: advisory
Require firewall rules to explicitly specify direction (INGRESS or EGRESS)
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
Remediation
Fix: Explicitly Specify Firewall Rule Direction
const firewallRule = new gcp.compute.Firewall("explicit-direction-rule", {
network: network.id,
direction: "INGRESS", // Explicitly specify direction
allows: [{
protocol: "tcp",
ports: ["443"],
}],
sourceRanges: ["10.0.0.0/24"],
// ... other config
});
firewall-explicit-target-scoping
Severity: high · Enforcement: advisory
Require firewall rules to specify explicit targets (targetTags, targetServiceAccounts, or destinationRanges)
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
Remediation
Fix: Specify Explicit Firewall Rule Targets
const firewallRule = new gcp.compute.Firewall("scoped-firewall-rule", {
network: network.id,
direction: "INGRESS",
allows: [{
protocol: "tcp",
ports: ["443"],
}],
sourceRanges: ["10.0.0.0/24"],
targetTags: ["web-servers"], // Specify explicit targets
// ... other config
});
firewall-no-http-ingress
Severity: critical · Enforcement: advisory
Require firewall rules to disallow inbound HTTP traffic from unauthorized sources
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Fix: Use HTTPS Instead of HTTP
const firewallRule = new gcp.compute.Firewall("allow-https", {
network: network.id,
direction: "INGRESS",
allows: [{
protocol: "tcp",
ports: ["443"], // Use HTTPS (port 443) instead of HTTP (port 80)
}],
sourceRanges: ["0.0.0.0/0"],
// ... other config
});
firewall-no-public-ingress
Severity: critical · Enforcement: advisory
Require firewall rules to disallow public internet ingress unless specifically authorized
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Fix: Use Private Network Ranges or Source Tags
const firewallRule = new gcp.compute.Firewall("allow-private-ingress", {
network: network.id,
direction: "INGRESS",
allows: [{
protocol: "tcp",
ports: ["443"],
}],
sourceRanges: ["10.0.0.0/8", "192.168.0.0/16"], // Use private ranges instead of 0.0.0.0/0
// Or use source tags for internal traffic control:
// sourceTags: ["web-tier"],
// ... other config
});
firewall-no-unrestricted-egress
Severity: high · Enforcement: advisory
Prohibit EGRESS firewall rules that allow all ports to 0.0.0.0/0
- 1.3.2 — 1.3.2: Outbound traffic from the CDE is restricted
Remediation
Fix: Restrict EGRESS Firewall Rules to Explicit Ports and Destinations
const egressRule = new gcp.compute.Firewall("restricted-egress-rule", {
network: network.id,
direction: "EGRESS",
allows: [{
protocol: "tcp",
ports: ["443"], // Specify explicit ports, not all ports
}],
destinationRanges: ["10.0.0.0/24"], // Prefer explicit destinations over 0.0.0.0/0
// ... other config
});
firewall-restrict-rdp-ingress
Severity: high · Enforcement: advisory
Ensures VPC firewall rules do not allow RDP (port 3389) from broad sources
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Restrict sourceRanges to specific CIDRs, or use IAP (35.235.240.0/20).
firewall-restrict-ssh-ingress
Severity: high · Enforcement: advisory
Ensures VPC firewall rules do not allow SSH (port 22) from broad sources
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Restrict sourceRanges to specific CIDRs, or use IAP (35.235.240.0/20).
gke-nodes-private
Severity: high · Enforcement: advisory
Ensures GKE clusters enable private nodes
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Set privateClusterConfig.enablePrivateNodes to true.
gke-private-endpoint
Severity: high · Enforcement: advisory
Restrict public access for GKE clusters by enabling private endpoint or configuring master authorized networks
- 1.3.1 — 1.3.1: Inbound traffic to the CDE is restricted as follows: To only traffic that is necessary, All other traffic is specifically denied
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Enable private endpoint by setting privateClusterConfig.enablePrivateEndpoint: true, or configure masterAuthorizedNetworksConfig.cidrBlocks with specific IP ranges (avoid 0.0.0.0/0).
gke-secrets-encryption
Severity: high · Enforcement: advisory
Require GKE clusters to have Application-layer Secrets Encryption enabled
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Enable Application-layer Secrets Encryption
const cluster = new gcp.container.Cluster("my-cluster", {
databaseEncryption: {
state: "ENCRYPTED", // Enable secrets encryption
keyName: "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key", // Specify KMS key
},
// ... other config
});
hardcoded-secrets
Severity: critical · Enforcement: advisory
Prohibit hardcoded secrets in code and configuration
- 8.6.3 — 8.6.3: 3 Passwords/passphrases for any application and system accounts are protected against misuse
Remediation
Fix: Remove Hardcoded Secrets
Remove hardcoded secrets from resource configuration.
Example Violations
Compute Instance with hardcoded secrets:
const instance = new gcp.compute.Instance("my-instance", {
machineType: "e2-medium",
bootDisk: { /* ... */ },
networkInterfaces: [{ /* ... */ }],
metadataStartupScript: `#!/bin/bash
export DATABASE_PASSWORD="mySecretPassword123"
export API_KEY="AIzaSyAbc123def456ghi789jkl012mno345pqr678"
mysql -u admin -p"hardcodedPassword" -h db.example.com
`,
});
Cloud Function with hardcoded environment variables:
const cloudFunction = new gcp.cloudfunctions.Function("my-function", {
runtime: "nodejs20",
environmentVariables: {
"DATABASE_PASSWORD": "mySecretPassword123",
"API_KEY": "AIzaSyAbc123def456ghi789jkl012mno345pqr678",
},
});
Cloud SQL with hardcoded root password:
const sqlInstance = new gcp.sql.DatabaseInstance("my-db", {
databaseVersion: "POSTGRES_15",
rootPassword: "myHardcodedPassword123",
});
iam-no-broad-roles
Severity: critical · Enforcement: advisory
Prohibit overly broad IAM roles on projects, organizations, folders, and service accounts
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Fix: Use Specific Least-Privilege Roles
const binding = new gcp.projects.IAMBinding("service-binding", {
project: "my-project",
role: "roles/storage.objectViewer",
members: ["serviceAccount:my-app@my-project.iam.gserviceaccount.com"],
});
Replace broad roles such as roles/owner or roles/editor with the most
specific role that grants only the permissions needed.
iam-no-custom-role-excessive-permissions
Severity: high · Enforcement: advisory
Prevent IAM custom roles with excessive permissions for enhanced data access control.
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Review and refine custom role permissions. Remove excessive administrative permissions like setIamPolicy, admin-level permissions, and owner permissions. Follow the principle of least privilege by granting only the minimum permissions required for the role’s intended purpose. Consider breaking up roles with too many permissions.
Example - Create custom role with limited permissions:
import * as gcp from "@pulumi/gcp";
const role = new gcp.projects.IAMCustomRole("limited-role", {
roleId: "limitedViewer",
title: "Limited Viewer",
permissions: [
"compute.instances.get",
"compute.instances.list",
],
});
iam-policy-no-statements-with-admin-access
Severity: critical · Enforcement: advisory
Prevent IAM policy bindings with administrative access permissions for secure configuration.
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Review and refine IAM policy bindings. Replace service-specific administrative roles like ‘roles/compute.admin’, ‘roles/storage.admin’, ‘roles/bigquery.admin’ with specific, granular roles. Use predefined roles with minimal permissions required for the task. Follow the principle of least privilege by granting only necessary permissions for operations, not administrative control over entire services.
Example - Replace admin role with specific permissions:
import * as gcp from "@pulumi/gcp";
// Instead of roles/compute.admin, use specific roles
const binding = new gcp.projects.IAMMember("compute-viewer", {
project: "my-project",
role: "roles/compute.viewer",
member: "serviceAccount:sa@project.iam.gserviceaccount.com",
});
iam-policy-no-wildcard-permissions
Severity: critical · Enforcement: advisory
Prevent IAM custom roles with wildcard permissions to enforce least privilege access control.
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Review and refine custom IAM roles. Remove wildcard () permissions and replace them with specific, granular permissions. Instead of using ‘compute.’ or ‘storage.*’, explicitly list the required permissions such as ‘compute.instances.get’, ‘compute.instances.list’, ‘storage.buckets.get’, etc. Follow the principle of least privilege by granting only the exact permissions needed for the role’s intended purpose.
Example - Create custom role with specific permissions:
import * as gcp from "@pulumi/gcp";
const customRole = new gcp.organizations.IAMCustomRole("custom-role", {
roleId: "computeViewer",
orgId: "123456789",
title: "Compute Viewer",
permissions: [
"compute.instances.get",
"compute.instances.list",
],
});
iam-user-no-direct-policies-check
Severity: high · Enforcement: advisory
Prevent IAM users from having direct policy attachments for better access management.
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
Remediation
Remove direct IAM policy bindings from individual user and service accounts. Instead, create IAM groups and assign users to groups, or use predefined roles. For service accounts, grant permissions through workload identity federation or service account impersonation where possible. This improves manageability and follows the principle of least privilege.
Example - Use groups instead of direct user bindings:
import * as gcp from "@pulumi/gcp";
// Grant permissions to a group instead of individual users
const groupBinding = new gcp.projects.IAMMember("group-viewer", {
project: "my-project",
role: "roles/viewer",
member: "group:dev-team@example.com",
});
instance-template-disk-cmek
Severity: high · Enforcement: advisory
Require instance template disks to have customer-managed (CMEK) or customer-supplied (CSEK) encryption keys
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Configure CMEK or CSEK Disk Encryption
const template = new gcp.compute.InstanceTemplate("my-template", {
disks: [{
boot: true,
sourceImage: "debian-cloud/debian-11",
diskEncryptionKey: {
kmsKeySelfLink: "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key",
},
}],
});
kms-key-configuration
Severity: low · Enforcement: advisory
Require proper Cloud KMS key creation and configuration
- 3.6.1 — 3.6.1: Procedures are defined and implemented to protect cryptographic keys used to protect stored account data against disclosure
- 3.7.1 — 3.7.1: Key-management policies and procedures are implemented to include generation of strong cryptographic keys used to protect stored account data
Remediation
Fix: Configure KMS Key with Version Template
const cryptoKey = new gcp.kms.CryptoKey("my-crypto-key", {
name: "my-key",
keyRing: keyRing.id,
versionTemplate: {
algorithm: "GOOGLE_SYMMETRIC_ENCRYPTION", // Specify cryptographic algorithm
protectionLevel: "HSM", // Set protection level (SOFTWARE, HSM, EXTERNAL, EXTERNAL_VPC)
},
rotationPeriod: "7776000s", // Optional: Configure 90-day rotation period
purpose: "ENCRYPT_DECRYPT", // Optional: Explicitly set key purpose
// ... other config
});
kms-key-iam
Severity: high · Enforcement: advisory
Require proper access controls for Cloud KMS key IAM policies
- 3.6.1.3 — 3.6.1.3: Access to cleartext cryptographic key components is restricted to the fewest number of custodians necessary
- 7.2.1 — 7.2.1: An access control model is defined and includes granting access
Remediation
Fix: Use Least-Privilege IAM Roles
// Use specific roles instead of overprivileged roles
const cryptoKeyBinding = new gcp.kms.CryptoKeyIAMBinding("key-access", {
cryptoKeyId: cryptoKey.id,
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter", // Use specific role
members: [
"serviceAccount:my-service@project.iam.gserviceaccount.com", // Use specific service accounts
"group:security-team@example.com", // Or specific groups
],
// Avoid: roles/owner, roles/editor, roles/cloudkms.admin
// Avoid: allUsers, allAuthenticatedUsers, domain-wide access
// ... other config
});
kms-key-lifecycle
Severity: low · Enforcement: advisory
Require proper Cloud KMS key deletion and lifecycle management
- 3.7.5 — 3.7.5: Key management policies procedures are implemented to include the retirement, replacement, or destruction of keys used to protect stored account data *
Remediation
Fix: Configure Key Lifecycle Management
const cryptoKey = new kms.CryptoKey("my-crypto-key", {
name: "my-key",
keyRing: keyRing.id,
destroyScheduledDuration: "2592000s", // Set 30-day scheduled destruction period
skipInitialVersionCreation: false, // Create initial key version
versionTemplate: {
algorithm: "GOOGLE_SYMMETRIC_ENCRYPTION",
protectionLevel: "SOFTWARE",
},
// ... other config
});
kms-key-rotation
Severity: high · Enforcement: advisory
Ensure Cloud KMS key rotation is enabled
- 3.7.4 — 3.7.4: Key management policies and procedures are implemented for cryptographic key changes for keys that have reached the end of their cryptoperiod, as defined by the associated application vendor or key owner
Remediation
Enable automatic key rotation for Cloud KMS crypto keys by setting ‘rotationPeriod’. Key rotation limits the lifetime of encryption keys and reduces cryptographic risk.
Example - Enable automatic key rotation:
import * as gcp from "@pulumi/gcp";
const key = new gcp.kms.CryptoKey("my-key", {
name: "crypto-key",
keyRing: keyring.id,
rotationPeriod: "7776000s", // 90 days (configurable)
});
Common rotation periods:
- 30 days: “2592000s”
- 60 days: “5184000s”
- 90 days: “7776000s” (recommended default)
- 365 days: “31536000s” (maximum recommended)
lb-cloud-armor-attached
Severity: high · Enforcement: advisory
Ensures external backend services have Cloud Armor security policy attached
- 6.4.2 — 6.4.2: For public-facing web applications, an automated technical solution is deployed that continually detects and prevents web-based attacks
Remediation
Attach a Cloud Armor security policy to external backend services using the securityPolicy property for DDoS protection and threat detection.
lb-managed-ssl-certificate
Severity: high · Enforcement: advisory
Ensure Application Load Balancer uses managed SSL certificates with proper configuration
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Attach SSL certificates to target HTTPS proxies via sslCertificates property. Create and configure SSL policy with minimum TLS 1.2 using minTlsVersion: TLS_1_2 and profile: MODERN or RESTRICTED for secure cipher suites. For managed SSL certificates, specify at least one domain in the managed.domains field.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
// Create managed SSL certificate
const managedCert = new gcp.compute.ManagedSslCertificate("ssl-cert", {
name: "my-managed-cert",
managed: {
domains: ["example.com", "www.example.com"]
}
});
// Create SSL policy with secure TLS settings
const sslPolicy = new gcp.compute.SSLPolicy("ssl-policy", {
name: "secure-ssl-policy",
profile: "MODERN",
minTlsVersion: "TLS_1_2"
});
// Create HTTPS target proxy with managed certificate and SSL policy
const httpsProxy = new gcp.compute.TargetHttpsProxy("https-proxy", {
name: "app-https-proxy",
urlMap: urlMap.id,
sslCertificates: [managedCert.id],
sslPolicy: sslPolicy.id
});
load-balancer-http-to-https-redirection
Severity: high · Enforcement: advisory
Ensure Load Balancer HTTP to HTTPS redirection is configured
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Configure URL map with HTTPS redirect by setting defaultUrlRedirect.httpsRedirect: true or configure path matchers with HTTPS redirects. Create separate HTTP and HTTPS target proxies, with HTTP proxy redirecting to HTTPS.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
// Create URL map with HTTP to HTTPS redirect
const redirectUrlMap = new gcp.compute.URLMap("redirect-map", {
name: "http-redirect",
defaultUrlRedirect: {
httpsRedirect: true,
redirectResponseCode: "MOVED_PERMANENTLY_DEFAULT"
}
});
// Create HTTP target proxy for redirection
const httpProxy = new gcp.compute.TargetHttpProxy("http-proxy", {
name: "http-redirect-proxy",
urlMap: redirectUrlMap.id
});
// Create HTTP forwarding rule (port 80)
const httpForwardingRule = new gcp.compute.GlobalForwardingRule("http-rule", {
name: "http-forwarding-rule",
target: httpProxy.id,
portRange: "80",
ipProtocol: "TCP",
loadBalancingScheme: "EXTERNAL"
});
// Create HTTPS URL map with backend service
const httpsUrlMap = new gcp.compute.URLMap("https-map", {
name: "https-url-map",
defaultService: backendService.id
});
// Create HTTPS target proxy
const httpsProxy = new gcp.compute.TargetHttpsProxy("https-proxy", {
name: "https-proxy",
urlMap: httpsUrlMap.id,
sslCertificates: [sslCert.id]
});
// Create HTTPS forwarding rule (port 443)
const httpsForwardingRule = new gcp.compute.GlobalForwardingRule("https-rule", {
name: "https-forwarding-rule",
target: httpsProxy.id,
portRange: "443",
ipProtocol: "TCP",
loadBalancingScheme: "EXTERNAL"
});
load-balancer-logging-enabled
Severity: high · Enforcement: advisory
Enable Load Balancer logging for monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Enable logging on backend services by configuring logConfig.enable: true and set logConfig.sampleRate (1.0 for full logging, lower values for sampling). Ensure logs are retained in Cloud Logging for security analysis and troubleshooting.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
// For global backend service
const backendService = new gcp.compute.BackendService("my-backend", {
logConfig: {
enable: true,
sampleRate: 1.0 // 1.0 = 100% logging, 0.5 = 50% sampling
},
// ... other configuration
});
// For regional backend service
const regionBackendService = new gcp.compute.RegionBackendService("my-region-backend", {
logConfig: {
enable: true,
sampleRate: 1.0
},
// ... other configuration
});
load-balancer-tls-https-listeners-only
Severity: high · Enforcement: advisory
Ensure Load Balancer uses TLS/HTTPS listeners only
- 4.2.1 — 4.2.1: Strong cryptography and security protocols are implemented as follows to safeguard PAN during transmission over open, public networks *
Remediation
Configure forwarding rules to use port 443 for HTTPS traffic. Use TargetHttpsProxy instead of TargetHttpProxy. For HTTP listeners, ensure they redirect to HTTPS via URL map configuration. Remove standalone HTTP listeners without HTTPS redirection.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
// Create SSL certificate (or use existing)
const sslCert = new gcp.compute.ManagedSslCertificate("my-cert", {
managed: {
domains: ["example.com"]
}
});
// Create HTTPS target proxy
const httpsProxy = new gcp.compute.TargetHttpsProxy("https-proxy", {
urlMap: urlMap.id,
sslCertificates: [sslCert.id]
});
// Create forwarding rule on port 443
const httpsForwardingRule = new gcp.compute.GlobalForwardingRule("https-rule", {
target: httpsProxy.id,
portRange: "443",
ipProtocol: "TCP",
loadBalancingScheme: "EXTERNAL"
});
// Optional: HTTP to HTTPS redirect
const httpProxy = new gcp.compute.TargetHttpProxy("http-proxy", {
urlMap: redirectUrlMap.id // URL map configured for redirect
});
const httpForwardingRule = new gcp.compute.GlobalForwardingRule("http-rule", {
target: httpProxy.id,
portRange: "80",
ipProtocol: "TCP",
loadBalancingScheme: "EXTERNAL"
});
managed-instance-group-launch-template-public-ip-disabled
Severity: high · Enforcement: advisory
Ensure Managed Instance Group launch templates have public IP addresses disabled
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
Remediation
Disable public IP in instance template:
new gcp.compute.InstanceTemplate("my-template", {
networkInterfaces: [{
network: "my-vpc",
subnetwork: "my-subnet",
accessConfigs: [], // Empty array disables public IP
}],
// ... other config
});
This prevents unintended public accessibility.
no-unrestricted-route-to-internet-gateway
Severity: high · Enforcement: advisory
Ensure no unrestricted route to Internet Gateway for monitoring
- 1.3.2 — 1.3.2: Outbound traffic from the CDE is restricted
Remediation
Route internet-bound traffic through Cloud NAT with logging enabled instead of direct internet gateway routes. Enable VPC Flow Logs for traffic monitoring. Use specific routes for known destinations. Configure firewall rules to control outbound traffic.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
// Bad: Direct route to default internet gateway (triggers violation)
const directInternetRoute = new gcp.compute.Route("direct-internet-route", {
network: network.id,
destRange: "0.0.0.0/0", // Default route
nextHopGateway: "default-internet-gateway", // Direct internet access
priority: 1000,
});
// Good: Route through Cloud NAT for monitored egress
const natRouter = new gcp.compute.Router("nat-router", {
network: network.id,
region: "us-central1",
});
const cloudNat = new gcp.compute.RouterNat("cloud-nat", {
router: natRouter.name,
region: natRouter.region,
natIpAllocateOption: "AUTO_ONLY",
sourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_IP_RANGES",
logConfig: {
enable: true,
filter: "ERRORS_ONLY", // or "TRANSLATIONS_ONLY", "ALL"
},
});
// Good: Specific route for known destination
const specificRoute = new gcp.compute.Route("specific-route", {
network: network.id,
destRange: "10.128.0.0/20", // Specific destination
nextHopGateway: "default-internet-gateway",
priority: 1000,
});
// Good: Route through internal load balancer for centralized egress
const ilbRoute = new gcp.compute.Route("ilb-egress-route", {
network: network.id,
destRange: "0.0.0.0/0",
nextHopIlb: ilb.selfLink, // Centralized egress control
priority: 1000,
});
Recommended patterns:
- Use Cloud NAT with logging enabled for internet-bound traffic
- Enable VPC Flow Logs on subnets for comprehensive monitoring
- Use specific routes (not 0.0.0.0/0) when possible
- Route through ILB for centralized egress control and monitoring
os-config-patch-coverage
Severity: high · Enforcement: advisory
Ensure Compute Engine instances are covered by OS Config patch management
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Enable OS Config on instances by setting metadata ’enable-osconfig: TRUE’. Create patch deployment policies so instances receive automated patch management.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const instance = new gcp.compute.Instance("managed-instance", {
// ... other config
metadata: {
"enable-osconfig": "TRUE",
},
});
const patchDeployment = new gcp.osconfig.PatchDeployment("weekly-patch", {
patchDeploymentId: "weekly-patches",
instanceFilter: {
all: true,
},
recurringSchedule: {
timeZone: { id: "UTC" },
weekly: { dayOfWeek: "SUNDAY" },
},
patchConfig: {
rebootConfig: "DEFAULT",
},
});
os-config-patch-deployment-configuration
Severity: high · Enforcement: advisory
Ensure OS Config patch deployments define a schedule, instance filter, patch configuration, and rollout mode
- 6.3.3 — 6.3.3: All system components are protected from known vulnerabilities by installing applicable security patches/updates *
Remediation
Configure patch deployments with a schedule, instance filter, and patchConfig including reboot settings. When using rollout, specify the rollout mode.
Example Pulumi TypeScript:
import * as gcp from "@pulumi/gcp";
const patchDeployment = new gcp.osconfig.PatchDeployment("weekly-patch", {
patchDeploymentId: "weekly-patches",
instanceFilter: {
all: true,
},
recurringSchedule: {
timeZone: { id: "UTC" },
weekly: { dayOfWeek: "SUNDAY" },
},
patchConfig: {
rebootConfig: "DEFAULT",
},
rollout: {
mode: "ZONE_BY_ZONE",
disruptionBudget: { fixed: 1 },
},
});
persistent-disk-cmek
Severity: high · Enforcement: advisory
Ensures persistent disks have customer-managed (CMEK) or customer-supplied (CSEK) encryption keys
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure diskEncryptionKey with either kmsKeySelfLink (CMEK), rawKey, or rsaEncryptedKey (CSEK).
persistent-disk-snapshot-not-publicly-restorable
Severity: high · Enforcement: advisory
Restrict public access to Persistent Disk snapshots
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Remove IAM bindings granting allUsers or allAuthenticatedUsers permissions to snapshots. Use specific service accounts or groups for snapshot access.
private-service-connect
Severity: high · Enforcement: advisory
Require Private Service Connect endpoints to have restrictive security policies
- 1.4.2 — 1.4.2: Inbound traffic from untrusted networks to trusted networks is restricted
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Fix: Configure Secure Private Service Connect
// Create Service Attachment with manual connection approval
const serviceAttachment = new gcp.compute.ServiceAttachment("psc-service", {
connectionPreference: "ACCEPT_MANUAL", // Require manual approval for connections
consumerAcceptLists: [{
projectIdOrNum: "consumer-project-123",
connectionLimit: 10, // Limit connections to minimize attack surface
}],
targetService: backendService.id,
// ... other config
});
// Create Forwarding Rule for PSC with internal load balancing
const forwardingRule = new gcp.compute.ForwardingRule("psc-endpoint", {
network: network.id, // Specify network for proper isolation
loadBalancingScheme: "INTERNAL", // Use internal scheme for private connectivity
target: serviceAttachment.id,
// ... other config
});
pubsub-encrypted-kms
Severity: medium · Enforcement: advisory
Ensure Pub/Sub is encrypted with Cloud KMS
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Configure kmsKeyName property when creating Pub/Sub topics to use customer-managed encryption keys (CMEK) for enhanced security and key management control.
Example - Pub/Sub Topic with Customer-Managed Encryption:
import * as gcp from "@pulumi/gcp";
// Create KMS KeyRing and CryptoKey for encryption
const keyRing = new gcp.kms.KeyRing("pubsub-keyring", {
name: "pubsub-keyring",
location: "us-central1"
});
const cryptoKey = new gcp.kms.CryptoKey("pubsub-key", {
name: "pubsub-encryption-key",
keyRing: keyRing.id,
rotationPeriod: "7776000s" // 90 days
});
// Grant Pub/Sub service account access to the key
const binding = new gcp.kms.CryptoKeyIAMBinding("pubsub-key-binding", {
cryptoKeyId: cryptoKey.id,
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
members: ["serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com"]
});
// COMPLIANT: Topic with customer-managed encryption key
const topicCompliant = new gcp.pubsub.Topic("encrypted-topic", {
name: "secure-topic",
kmsKeyName: cryptoKey.id, // Compliant: CMEK configured
messageRetentionDuration: "86400s"
});
// NON-COMPLIANT: Topic without CMEK (uses Google-managed keys)
const topicNonCompliant = new gcp.pubsub.Topic("default-topic", {
name: "insecure-topic"
// Missing kmsKeyName - uses default Google-managed encryption
});
pubsub-message-retention
Severity: medium · Enforcement: advisory
Require Pub/Sub subscriptions to have appropriate message retention policies
- 3.2.1 — 3.2.1: Account data storage is kept to a minimum through implementation of data retention and disposal policies, procedures, and processes
Remediation
Fix: Configure Message Retention Duration
const subscription = new gcp.pubsub.Subscription("my-subscription", {
topic: topic.name,
messageRetentionDuration: "604800s", // Set retention to 7 days (in seconds)
// ... other config
});
pubsub-subscription-iam-least-privilege
Severity: high · Enforcement: advisory
Enforce least privilege IAM policies for Pub/Sub subscriptions
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Fix: Use Least Privilege IAM Roles
const subscriptionIamBinding = new gcp.pubsub.SubscriptionIAMBinding("subscription-subscriber-binding", {
subscription: subscription.id,
role: "roles/pubsub.subscriber", // Use specific role instead of admin/owner/editor
members: [
"serviceAccount:app-service@project.iam.gserviceaccount.com", // Use specific service accounts instead of allUsers
],
});
pubsub-topic-iam-least-privilege
Severity: high · Enforcement: advisory
Enforce least privilege IAM policies for Pub/Sub topics
- 7.2.2 — 7.2.2: Access is assigned to users, including privileged users
- 7.3.1 — 7.3.1: An access control system(s) is in place that restricts access based on a user’s need to know and covers all system components
Remediation
Fix: Use Least Privilege IAM Roles
const topicIamBinding = new gcp.pubsub.TopicIAMBinding("topic-publisher-binding", {
topic: topic.id,
role: "roles/pubsub.publisher", // Use specific role instead of admin/owner/editor
members: [
"serviceAccount:app-service@project.iam.gserviceaccount.com", // Use specific service accounts instead of allUsers
],
});
secretmanager-secret-cmek
Severity: high · Enforcement: advisory
Require Secret Manager secrets to use customer-managed Cloud KMS keys
- 3.5.1 — 3.5.1 PAN is rendered unreadable anywhere it is stored
Remediation
Fix: Configure Customer-Managed KMS Encryption
import * as gcp from "@pulumi/gcp";
// Create a KMS KeyRing and CryptoKey
const keyRing = new gcp.kms.KeyRing("my-keyring", {
name: "secret-keyring",
location: "us-central1",
});
const cryptoKey = new gcp.kms.CryptoKey("my-key", {
name: "secret-encryption-key",
keyRing: keyRing.id,
rotationPeriod: "7776000s", // 90 days
});
// Option 1: Auto-replication with customer-managed encryption
const secretAuto = new gcp.secretmanager.Secret("my-secret-auto", {
secretId: "my-cmek-secret",
replication: {
auto: {
customerManagedEncryption: {
kmsKeyName: cryptoKey.id, // Use customer-managed KMS key
},
},
},
});
// Option 2: User-managed replication with customer-managed encryption
const secretUserManaged = new gcp.secretmanager.Secret("my-secret-user", {
secretId: "my-cmek-secret-regional",
replication: {
userManaged: {
replicas: [
{
location: "us-central1",
customerManagedEncryption: {
kmsKeyName: cryptoKey.id, // Use customer-managed KMS key
},
},
{
location: "us-east1",
customerManagedEncryption: {
kmsKeyName: cryptoKey.id, // Specify for each replica
},
},
],
},
},
});
security-command-center-enabled
Severity: high · Enforcement: advisory
Ensure Security Command Center is enabled
- 10.4.1 — 10.4.1: Potentially suspicious or anomalous activities are quickly identified to minimize impact *
- 11.5.1 — 11.5.1: Intrusion-detection and/or intrusionprevention techniques are used to detect and/or prevent intrusions into the network *
Remediation
Enable the Security Command Center API service (securitycenter.googleapis.com) in your project or organization. Configure notification channels using gcp.securitycenter.NotificationConfig resources to receive security alerts. Consider enabling Security Command Center’s built-in detectors and custom modules for comprehensive security monitoring.
Example - Enable Security Command Center:
import * as gcp from "@pulumi/gcp";
const sccService = new gcp.projects.Service("scc-api", {
service: "securitycenter.googleapis.com",
});
const notifConfig = new gcp.securitycenter.NotificationConfig("scc-notify", {
configId: "scc-notifications",
organization: "123456789",
pubsubTopic: topic.id,
streamingConfig: { filter: 'state = "ACTIVE"' },
});
securitypolicy-has-custom-rules
Severity: high · Enforcement: advisory
Ensures Cloud Armor security policies have custom protection rules
- 6.4.2 — 6.4.2: For public-facing web applications, an automated technical solution is deployed that continually detects and prevents web-based attacks
Remediation
Add custom protection rules to the Cloud Armor security policy for DDoS protection, SQL injection prevention, XSS protection, or other security policies.
service-account-key
Severity: critical · Enforcement: advisory
Ensure proper service account key usage and prohibit insecure authentication methods
- 8.2.2 — 8.2.2: Group, shared, or generic IDs, or other shared authentication credentials are only used when necessary on an exception basis, and are managed
- 8.3.2 — 8.3.2: Strong cryptography is used to render all authentication factors unreadable during transmission and storage on all system components
Remediation
Fix: Use Strong Key Algorithm and Avoid Default Service Accounts
const serviceAccount = new gcp.serviceaccount.Account("app-service-account", {
accountId: "webapp-backend", // Use application-specific service account
displayName: "Web App Backend Service Account",
});
const serviceAccountKey = new gcp.serviceaccount.Key("app-key", {
serviceAccountId: serviceAccount.name,
keyAlgorithm: "KEY_ALG_RSA_2048", // Use RSA_2048 or RSA_4096 for strong encryption
// Avoid creating keys for default service accounts like:
// - {project-number}-compute@developer.gserviceaccount.com
// - {project-id}@appspot.gserviceaccount.com
});
service-account-restricted-names
Severity: medium · Enforcement: advisory
Restrict default service account creation with prohibited names
- 8.2.2 — 8.2.2: Group, shared, or generic IDs, or other shared authentication credentials are only used when necessary on an exception basis, and are managed
Remediation
Fix: Use Descriptive Service Account Names
const serviceAccount = new gcp.serviceaccount.Account("backend-service-account", {
accountId: "webapp-backend", // Use specific, descriptive names (6-30 chars)
displayName: "Web Application Backend Service Account",
// Avoid generic/prohibited names like: default, admin, root, system, compute
// Use names that describe the service purpose: webapp-backend, data-processor, etc.
});
vpc-firewall-rule-associated-to-network
Severity: high · Enforcement: advisory
Ensure VPC firewall rules are associated to networks
- 1.2.8 — 1.2.8: Configuration files for NSCs are secured from unauthorized access and are kept consistent with active network configurations
Remediation
Associate firewall rules with VPC networks by specifying the network property with a valid VPC network reference (name, partial URL, or full URL). Ensure the network exists before creating the firewall rule.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
const firewallRule = new gcp.compute.Firewall("my-firewall-rule", {
network: network.id, // Reference to VPC network
allows: [{
protocol: "tcp",
ports: ["80", "443"],
}],
sourceRanges: ["0.0.0.0/0"],
targetTags: ["web-server"],
});
The network property can be specified as:
- A resource reference:
network.idornetwork.selfLink - A full URL:
https://www.googleapis.com/compute/v1/projects/PROJECT/global/networks/NETWORK - A partial URL:
projects/PROJECT/global/networks/NETWORK - A short name:
network-name
vpc-flow-logs-enabled
Severity: high · Enforcement: advisory
Ensure VPC subnets have Flow Logs enabled for audit and monitoring
- 10.2.1 — 10.2.1: Audit logs are enabled and active for all system components and cardholder data
Remediation
Enable VPC Flow Logs on subnets by configuring logConfig with aggregationInterval (e.g., INTERVAL_5_SEC) and flowSampling (0.5 for 50% or 1.0 for 100%). Ensure flowSampling is greater than 0 to collect traffic logs.
Pulumi TypeScript Example:
import * as gcp from "@pulumi/gcp";
const subnet = new gcp.compute.Subnetwork("my-subnet", {
ipCidrRange: "10.0.0.0/24",
network: network.id,
region: "us-central1",
logConfig: {
aggregationInterval: "INTERVAL_5_SEC", // or INTERVAL_30_SEC, INTERVAL_1_MIN, etc.
flowSampling: 1.0, // 1.0 = 100% sampling, 0.5 = 50%
metadata: "INCLUDE_ALL_METADATA"
},
privateIpGoogleAccess: true
});
Valid aggregationInterval values: INTERVAL_5_SEC, INTERVAL_30_SEC, INTERVAL_1_MIN, INTERVAL_5_MIN, INTERVAL_10_MIN, INTERVAL_15_MIN