Configure GCP Organization Policies

The gcp:orgpolicy/policy:Policy resource, part of the Pulumi GCP provider, defines organization policies that enforce constraints on Google Cloud resource configurations across projects, folders, and organizations. This guide focuses on four capabilities: enforcement at different hierarchy levels, conditional rules with allowed/denied values, dry-run testing, and parameter passing to custom constraints.

Policies reference existing projects, folders, or organizations and may depend on custom constraints or resource tags. The examples are intentionally small. Combine them with your own resource hierarchy and constraint definitions.

Enforce a constraint at the project level

Teams often disable specific security features for development projects while keeping them enforced in production.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const basic = new gcp.organizations.Project("basic", {
    projectId: "id",
    name: "id",
    orgId: "123456789",
    deletionPolicy: "DELETE",
});
const primary = new gcp.orgpolicy.Policy("primary", {
    name: pulumi.interpolate`projects/${basic.projectId}/policies/iam.disableServiceAccountKeyUpload`,
    parent: pulumi.interpolate`projects/${basic.projectId}`,
    spec: {
        rules: [{
            enforce: "FALSE",
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

basic = gcp.organizations.Project("basic",
    project_id="id",
    name="id",
    org_id="123456789",
    deletion_policy="DELETE")
primary = gcp.orgpolicy.Policy("primary",
    name=basic.project_id.apply(lambda project_id: f"projects/{project_id}/policies/iam.disableServiceAccountKeyUpload"),
    parent=basic.project_id.apply(lambda project_id: f"projects/{project_id}"),
    spec={
        "rules": [{
            "enforce": "FALSE",
        }],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		basic, err := organizations.NewProject(ctx, "basic", &organizations.ProjectArgs{
			ProjectId:      pulumi.String("id"),
			Name:           pulumi.String("id"),
			OrgId:          pulumi.String("123456789"),
			DeletionPolicy: pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name: basic.ProjectId.ApplyT(func(projectId string) (string, error) {
				return fmt.Sprintf("projects/%v/policies/iam.disableServiceAccountKeyUpload", projectId), nil
			}).(pulumi.StringOutput),
			Parent: basic.ProjectId.ApplyT(func(projectId string) (string, error) {
				return fmt.Sprintf("projects/%v", projectId), nil
			}).(pulumi.StringOutput),
			Spec: &orgpolicy.PolicySpecArgs{
				Rules: orgpolicy.PolicySpecRuleArray{
					&orgpolicy.PolicySpecRuleArgs{
						Enforce: pulumi.String("FALSE"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var basic = new Gcp.Organizations.Project("basic", new()
    {
        ProjectId = "id",
        Name = "id",
        OrgId = "123456789",
        DeletionPolicy = "DELETE",
    });

    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = basic.ProjectId.Apply(projectId => $"projects/{projectId}/policies/iam.disableServiceAccountKeyUpload"),
        Parent = basic.ProjectId.Apply(projectId => $"projects/{projectId}"),
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    Enforce = "FALSE",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var basic = new Project("basic", ProjectArgs.builder()
            .projectId("id")
            .name("id")
            .orgId("123456789")
            .deletionPolicy("DELETE")
            .build());

        var primary = new Policy("primary", PolicyArgs.builder()
            .name(basic.projectId().applyValue(_projectId -> String.format("projects/%s/policies/iam.disableServiceAccountKeyUpload", _projectId)))
            .parent(basic.projectId().applyValue(_projectId -> String.format("projects/%s", _projectId)))
            .spec(PolicySpecArgs.builder()
                .rules(PolicySpecRuleArgs.builder()
                    .enforce("FALSE")
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: projects/${basic.projectId}/policies/iam.disableServiceAccountKeyUpload
      parent: projects/${basic.projectId}
      spec:
        rules:
          - enforce: FALSE
  basic:
    type: gcp:organizations:Project
    properties:
      projectId: id
      name: id
      orgId: '123456789'
      deletionPolicy: DELETE

The name property identifies both the parent resource and the constraint being configured. The spec.rules array contains enforcement directives; setting enforce to “FALSE” disables the constraint for this project. The parent property establishes where the policy applies in your resource hierarchy.

Apply policies to folder hierarchies with inheritance

Organizations with multiple teams group projects into folders and apply policies that cascade down the hierarchy.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const basic = new gcp.organizations.Folder("basic", {
    parent: "organizations/123456789",
    displayName: "folder",
    deletionProtection: false,
});
const primary = new gcp.orgpolicy.Policy("primary", {
    name: pulumi.interpolate`${basic.name}/policies/gcp.resourceLocations`,
    parent: basic.name,
    spec: {
        inheritFromParent: true,
        rules: [{
            denyAll: "TRUE",
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

basic = gcp.organizations.Folder("basic",
    parent="organizations/123456789",
    display_name="folder",
    deletion_protection=False)
primary = gcp.orgpolicy.Policy("primary",
    name=basic.name.apply(lambda name: f"{name}/policies/gcp.resourceLocations"),
    parent=basic.name,
    spec={
        "inherit_from_parent": True,
        "rules": [{
            "deny_all": "TRUE",
        }],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		basic, err := organizations.NewFolder(ctx, "basic", &organizations.FolderArgs{
			Parent:             pulumi.String("organizations/123456789"),
			DisplayName:        pulumi.String("folder"),
			DeletionProtection: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name: basic.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("%v/policies/gcp.resourceLocations", name), nil
			}).(pulumi.StringOutput),
			Parent: basic.Name,
			Spec: &orgpolicy.PolicySpecArgs{
				InheritFromParent: pulumi.Bool(true),
				Rules: orgpolicy.PolicySpecRuleArray{
					&orgpolicy.PolicySpecRuleArgs{
						DenyAll: pulumi.String("TRUE"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var basic = new Gcp.Organizations.Folder("basic", new()
    {
        Parent = "organizations/123456789",
        DisplayName = "folder",
        DeletionProtection = false,
    });

    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = basic.Name.Apply(name => $"{name}/policies/gcp.resourceLocations"),
        Parent = basic.Name,
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            InheritFromParent = true,
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    DenyAll = "TRUE",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Folder;
import com.pulumi.gcp.organizations.FolderArgs;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var basic = new Folder("basic", FolderArgs.builder()
            .parent("organizations/123456789")
            .displayName("folder")
            .deletionProtection(false)
            .build());

        var primary = new Policy("primary", PolicyArgs.builder()
            .name(basic.name().applyValue(_name -> String.format("%s/policies/gcp.resourceLocations", _name)))
            .parent(basic.name())
            .spec(PolicySpecArgs.builder()
                .inheritFromParent(true)
                .rules(PolicySpecRuleArgs.builder()
                    .denyAll("TRUE")
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: ${basic.name}/policies/gcp.resourceLocations
      parent: ${basic.name}
      spec:
        inheritFromParent: true
        rules:
          - denyAll: TRUE
  basic:
    type: gcp:organizations:Folder
    properties:
      parent: organizations/123456789
      displayName: folder
      deletionProtection: false

The inheritFromParent property allows this policy to merge with parent policies rather than replacing them. The denyAll rule blocks all values for the gcp.resourceLocations constraint, restricting where resources can be created. Folder-level policies affect all projects within the folder unless overridden.

Reset a policy to organization defaults

When policies have been customized at lower levels, you may need to restore the organization’s baseline configuration.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const primary = new gcp.orgpolicy.Policy("primary", {
    name: "organizations/123456789/policies/gcp.detailedAuditLoggingMode",
    parent: "organizations/123456789",
    spec: {
        reset: true,
    },
});
import pulumi
import pulumi_gcp as gcp

primary = gcp.orgpolicy.Policy("primary",
    name="organizations/123456789/policies/gcp.detailedAuditLoggingMode",
    parent="organizations/123456789",
    spec={
        "reset": True,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name:   pulumi.String("organizations/123456789/policies/gcp.detailedAuditLoggingMode"),
			Parent: pulumi.String("organizations/123456789"),
			Spec: &orgpolicy.PolicySpecArgs{
				Reset: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = "organizations/123456789/policies/gcp.detailedAuditLoggingMode",
        Parent = "organizations/123456789",
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            Reset = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var primary = new Policy("primary", PolicyArgs.builder()
            .name("organizations/123456789/policies/gcp.detailedAuditLoggingMode")
            .parent("organizations/123456789")
            .spec(PolicySpecArgs.builder()
                .reset(true)
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: organizations/123456789/policies/gcp.detailedAuditLoggingMode
      parent: organizations/123456789
      spec:
        reset: true

The reset property clears all custom rules and restores the policy to its organization-level defaults. This is useful when removing project-specific overrides or simplifying policy management.

Define conditional rules with allowed and denied values

Complex policies need different rules for different resources, using conditions to match tags or other attributes.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const basic = new gcp.organizations.Project("basic", {
    projectId: "id",
    name: "id",
    orgId: "123456789",
    deletionPolicy: "DELETE",
});
const primary = new gcp.orgpolicy.Policy("primary", {
    name: pulumi.interpolate`projects/${basic.projectId}/policies/gcp.resourceLocations`,
    parent: pulumi.interpolate`projects/${basic.projectId}`,
    spec: {
        rules: [
            {
                condition: {
                    description: "A sample condition for the policy",
                    expression: "resource.matchTagId('tagKeys/123', 'tagValues/345')",
                    location: "sample-location.log",
                    title: "sample-condition",
                },
                values: {
                    allowedValues: ["projects/allowed-project"],
                    deniedValues: ["projects/denied-project"],
                },
            },
            {
                allowAll: "TRUE",
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

basic = gcp.organizations.Project("basic",
    project_id="id",
    name="id",
    org_id="123456789",
    deletion_policy="DELETE")
primary = gcp.orgpolicy.Policy("primary",
    name=basic.project_id.apply(lambda project_id: f"projects/{project_id}/policies/gcp.resourceLocations"),
    parent=basic.project_id.apply(lambda project_id: f"projects/{project_id}"),
    spec={
        "rules": [
            {
                "condition": {
                    "description": "A sample condition for the policy",
                    "expression": "resource.matchTagId('tagKeys/123', 'tagValues/345')",
                    "location": "sample-location.log",
                    "title": "sample-condition",
                },
                "values": {
                    "allowed_values": ["projects/allowed-project"],
                    "denied_values": ["projects/denied-project"],
                },
            },
            {
                "allow_all": "TRUE",
            },
        ],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		basic, err := organizations.NewProject(ctx, "basic", &organizations.ProjectArgs{
			ProjectId:      pulumi.String("id"),
			Name:           pulumi.String("id"),
			OrgId:          pulumi.String("123456789"),
			DeletionPolicy: pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		_, err = orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name: basic.ProjectId.ApplyT(func(projectId string) (string, error) {
				return fmt.Sprintf("projects/%v/policies/gcp.resourceLocations", projectId), nil
			}).(pulumi.StringOutput),
			Parent: basic.ProjectId.ApplyT(func(projectId string) (string, error) {
				return fmt.Sprintf("projects/%v", projectId), nil
			}).(pulumi.StringOutput),
			Spec: &orgpolicy.PolicySpecArgs{
				Rules: orgpolicy.PolicySpecRuleArray{
					&orgpolicy.PolicySpecRuleArgs{
						Condition: &orgpolicy.PolicySpecRuleConditionArgs{
							Description: pulumi.String("A sample condition for the policy"),
							Expression:  pulumi.String("resource.matchTagId('tagKeys/123', 'tagValues/345')"),
							Location:    pulumi.String("sample-location.log"),
							Title:       pulumi.String("sample-condition"),
						},
						Values: &orgpolicy.PolicySpecRuleValuesArgs{
							AllowedValues: pulumi.StringArray{
								pulumi.String("projects/allowed-project"),
							},
							DeniedValues: pulumi.StringArray{
								pulumi.String("projects/denied-project"),
							},
						},
					},
					&orgpolicy.PolicySpecRuleArgs{
						AllowAll: pulumi.String("TRUE"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var basic = new Gcp.Organizations.Project("basic", new()
    {
        ProjectId = "id",
        Name = "id",
        OrgId = "123456789",
        DeletionPolicy = "DELETE",
    });

    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = basic.ProjectId.Apply(projectId => $"projects/{projectId}/policies/gcp.resourceLocations"),
        Parent = basic.ProjectId.Apply(projectId => $"projects/{projectId}"),
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    Condition = new Gcp.OrgPolicy.Inputs.PolicySpecRuleConditionArgs
                    {
                        Description = "A sample condition for the policy",
                        Expression = "resource.matchTagId('tagKeys/123', 'tagValues/345')",
                        Location = "sample-location.log",
                        Title = "sample-condition",
                    },
                    Values = new Gcp.OrgPolicy.Inputs.PolicySpecRuleValuesArgs
                    {
                        AllowedValues = new[]
                        {
                            "projects/allowed-project",
                        },
                        DeniedValues = new[]
                        {
                            "projects/denied-project",
                        },
                    },
                },
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    AllowAll = "TRUE",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var basic = new Project("basic", ProjectArgs.builder()
            .projectId("id")
            .name("id")
            .orgId("123456789")
            .deletionPolicy("DELETE")
            .build());

        var primary = new Policy("primary", PolicyArgs.builder()
            .name(basic.projectId().applyValue(_projectId -> String.format("projects/%s/policies/gcp.resourceLocations", _projectId)))
            .parent(basic.projectId().applyValue(_projectId -> String.format("projects/%s", _projectId)))
            .spec(PolicySpecArgs.builder()
                .rules(                
                    PolicySpecRuleArgs.builder()
                        .condition(PolicySpecRuleConditionArgs.builder()
                            .description("A sample condition for the policy")
                            .expression("resource.matchTagId('tagKeys/123', 'tagValues/345')")
                            .location("sample-location.log")
                            .title("sample-condition")
                            .build())
                        .values(PolicySpecRuleValuesArgs.builder()
                            .allowedValues("projects/allowed-project")
                            .deniedValues("projects/denied-project")
                            .build())
                        .build(),
                    PolicySpecRuleArgs.builder()
                        .allowAll("TRUE")
                        .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: projects/${basic.projectId}/policies/gcp.resourceLocations
      parent: projects/${basic.projectId}
      spec:
        rules:
          - condition:
              description: A sample condition for the policy
              expression: resource.matchTagId('tagKeys/123', 'tagValues/345')
              location: sample-location.log
              title: sample-condition
            values:
              allowedValues:
                - projects/allowed-project
              deniedValues:
                - projects/denied-project
          - allowAll: TRUE
  basic:
    type: gcp:organizations:Project
    properties:
      projectId: id
      name: id
      orgId: '123456789'
      deletionPolicy: DELETE

The condition property uses CEL expressions to match resources by tags or other attributes. The values block specifies allowedValues and deniedValues lists that apply when the condition matches. The second rule with allowAll provides a fallback for resources that don’t match the condition.

Test policy impact with dry-run mode

Before enforcing a new policy, teams use dry-run mode to audit how it would affect existing and future resources.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const constraint = new gcp.orgpolicy.CustomConstraint("constraint", {
    name: "custom.disableGkeAutoUpgrade_2234",
    parent: "organizations/123456789",
    displayName: "Disable GKE auto upgrade",
    description: "Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced.",
    actionType: "ALLOW",
    condition: "resource.management.autoUpgrade == false",
    methodTypes: ["CREATE"],
    resourceTypes: ["container.googleapis.com/NodePool"],
});
const primary = new gcp.orgpolicy.Policy("primary", {
    name: pulumi.interpolate`organizations/123456789/policies/${constraint.name}`,
    parent: "organizations/123456789",
    spec: {
        rules: [{
            enforce: "FALSE",
        }],
    },
    dryRunSpec: {
        inheritFromParent: false,
        reset: false,
        rules: [{
            enforce: "FALSE",
        }],
    },
});
import pulumi
import pulumi_gcp as gcp

constraint = gcp.orgpolicy.CustomConstraint("constraint",
    name="custom.disableGkeAutoUpgrade_2234",
    parent="organizations/123456789",
    display_name="Disable GKE auto upgrade",
    description="Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced.",
    action_type="ALLOW",
    condition="resource.management.autoUpgrade == false",
    method_types=["CREATE"],
    resource_types=["container.googleapis.com/NodePool"])
primary = gcp.orgpolicy.Policy("primary",
    name=constraint.name.apply(lambda name: f"organizations/123456789/policies/{name}"),
    parent="organizations/123456789",
    spec={
        "rules": [{
            "enforce": "FALSE",
        }],
    },
    dry_run_spec={
        "inherit_from_parent": False,
        "reset": False,
        "rules": [{
            "enforce": "FALSE",
        }],
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		constraint, err := orgpolicy.NewCustomConstraint(ctx, "constraint", &orgpolicy.CustomConstraintArgs{
			Name:        pulumi.String("custom.disableGkeAutoUpgrade_2234"),
			Parent:      pulumi.String("organizations/123456789"),
			DisplayName: pulumi.String("Disable GKE auto upgrade"),
			Description: pulumi.String("Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced."),
			ActionType:  pulumi.String("ALLOW"),
			Condition:   pulumi.String("resource.management.autoUpgrade == false"),
			MethodTypes: pulumi.StringArray{
				pulumi.String("CREATE"),
			},
			ResourceTypes: pulumi.StringArray{
				pulumi.String("container.googleapis.com/NodePool"),
			},
		})
		if err != nil {
			return err
		}
		_, err = orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name: constraint.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("organizations/123456789/policies/%v", name), nil
			}).(pulumi.StringOutput),
			Parent: pulumi.String("organizations/123456789"),
			Spec: &orgpolicy.PolicySpecArgs{
				Rules: orgpolicy.PolicySpecRuleArray{
					&orgpolicy.PolicySpecRuleArgs{
						Enforce: pulumi.String("FALSE"),
					},
				},
			},
			DryRunSpec: &orgpolicy.PolicyDryRunSpecArgs{
				InheritFromParent: pulumi.Bool(false),
				Reset:             pulumi.Bool(false),
				Rules: orgpolicy.PolicyDryRunSpecRuleArray{
					&orgpolicy.PolicyDryRunSpecRuleArgs{
						Enforce: pulumi.String("FALSE"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var constraint = new Gcp.OrgPolicy.CustomConstraint("constraint", new()
    {
        Name = "custom.disableGkeAutoUpgrade_2234",
        Parent = "organizations/123456789",
        DisplayName = "Disable GKE auto upgrade",
        Description = "Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced.",
        ActionType = "ALLOW",
        Condition = "resource.management.autoUpgrade == false",
        MethodTypes = new[]
        {
            "CREATE",
        },
        ResourceTypes = new[]
        {
            "container.googleapis.com/NodePool",
        },
    });

    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = constraint.Name.Apply(name => $"organizations/123456789/policies/{name}"),
        Parent = "organizations/123456789",
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    Enforce = "FALSE",
                },
            },
        },
        DryRunSpec = new Gcp.OrgPolicy.Inputs.PolicyDryRunSpecArgs
        {
            InheritFromParent = false,
            Reset = false,
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicyDryRunSpecRuleArgs
                {
                    Enforce = "FALSE",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.orgpolicy.CustomConstraint;
import com.pulumi.gcp.orgpolicy.CustomConstraintArgs;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicyDryRunSpecArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var constraint = new CustomConstraint("constraint", CustomConstraintArgs.builder()
            .name("custom.disableGkeAutoUpgrade_2234")
            .parent("organizations/123456789")
            .displayName("Disable GKE auto upgrade")
            .description("Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced.")
            .actionType("ALLOW")
            .condition("resource.management.autoUpgrade == false")
            .methodTypes("CREATE")
            .resourceTypes("container.googleapis.com/NodePool")
            .build());

        var primary = new Policy("primary", PolicyArgs.builder()
            .name(constraint.name().applyValue(_name -> String.format("organizations/123456789/policies/%s", _name)))
            .parent("organizations/123456789")
            .spec(PolicySpecArgs.builder()
                .rules(PolicySpecRuleArgs.builder()
                    .enforce("FALSE")
                    .build())
                .build())
            .dryRunSpec(PolicyDryRunSpecArgs.builder()
                .inheritFromParent(false)
                .reset(false)
                .rules(PolicyDryRunSpecRuleArgs.builder()
                    .enforce("FALSE")
                    .build())
                .build())
            .build());

    }
}
resources:
  constraint:
    type: gcp:orgpolicy:CustomConstraint
    properties:
      name: custom.disableGkeAutoUpgrade_2234
      parent: organizations/123456789
      displayName: Disable GKE auto upgrade
      description: Only allow GKE NodePool resource to be created or updated if AutoUpgrade is not enabled where this custom constraint is enforced.
      actionType: ALLOW
      condition: resource.management.autoUpgrade == false
      methodTypes:
        - CREATE
      resourceTypes:
        - container.googleapis.com/NodePool
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: organizations/123456789/policies/${constraint.name}
      parent: organizations/123456789
      spec:
        rules:
          - enforce: FALSE
      dryRunSpec:
        inheritFromParent: false
        reset: false
        rules:
          - enforce: FALSE

The dryRunSpec property defines an audit-only version of the policy that logs violations without blocking operations. This lets you test policy impact before enforcement. The spec property contains the actual enforced rules, while dryRunSpec runs in parallel for monitoring.

Pass structured parameters to custom constraints

Custom constraints can accept parameters to make policies more flexible, such as size limits or allowed resource types.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const basic = new gcp.organizations.Project("basic", {
    projectId: "id",
    name: "id",
    orgId: "123456789",
    deletionPolicy: "DELETE",
});
const primary = new gcp.orgpolicy.Policy("primary", {
    name: pulumi.interpolate`projects/${basic.name}/policies/compute.managed.restrictDiskCreation`,
    parent: pulumi.interpolate`projects/${basic.name}`,
    spec: {
        rules: [{
            enforce: "TRUE",
            parameters: JSON.stringify({
                isSizeLimitCheck: true,
                allowedDiskTypes: [
                    "pd-ssd",
                    "pd-standard",
                ],
            }),
        }],
    },
});
import pulumi
import json
import pulumi_gcp as gcp

basic = gcp.organizations.Project("basic",
    project_id="id",
    name="id",
    org_id="123456789",
    deletion_policy="DELETE")
primary = gcp.orgpolicy.Policy("primary",
    name=basic.name.apply(lambda name: f"projects/{name}/policies/compute.managed.restrictDiskCreation"),
    parent=basic.name.apply(lambda name: f"projects/{name}"),
    spec={
        "rules": [{
            "enforce": "TRUE",
            "parameters": json.dumps({
                "isSizeLimitCheck": True,
                "allowedDiskTypes": [
                    "pd-ssd",
                    "pd-standard",
                ],
            }),
        }],
    })
package main

import (
	"encoding/json"
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/orgpolicy"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		basic, err := organizations.NewProject(ctx, "basic", &organizations.ProjectArgs{
			ProjectId:      pulumi.String("id"),
			Name:           pulumi.String("id"),
			OrgId:          pulumi.String("123456789"),
			DeletionPolicy: pulumi.String("DELETE"),
		})
		if err != nil {
			return err
		}
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"isSizeLimitCheck": true,
			"allowedDiskTypes": []string{
				"pd-ssd",
				"pd-standard",
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = orgpolicy.NewPolicy(ctx, "primary", &orgpolicy.PolicyArgs{
			Name: basic.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v/policies/compute.managed.restrictDiskCreation", name), nil
			}).(pulumi.StringOutput),
			Parent: basic.Name.ApplyT(func(name string) (string, error) {
				return fmt.Sprintf("projects/%v", name), nil
			}).(pulumi.StringOutput),
			Spec: &orgpolicy.PolicySpecArgs{
				Rules: orgpolicy.PolicySpecRuleArray{
					&orgpolicy.PolicySpecRuleArgs{
						Enforce:    pulumi.String("TRUE"),
						Parameters: pulumi.String(json0),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var basic = new Gcp.Organizations.Project("basic", new()
    {
        ProjectId = "id",
        Name = "id",
        OrgId = "123456789",
        DeletionPolicy = "DELETE",
    });

    var primary = new Gcp.OrgPolicy.Policy("primary", new()
    {
        Name = basic.Name.Apply(name => $"projects/{name}/policies/compute.managed.restrictDiskCreation"),
        Parent = basic.Name.Apply(name => $"projects/{name}"),
        Spec = new Gcp.OrgPolicy.Inputs.PolicySpecArgs
        {
            Rules = new[]
            {
                new Gcp.OrgPolicy.Inputs.PolicySpecRuleArgs
                {
                    Enforce = "TRUE",
                    Parameters = JsonSerializer.Serialize(new Dictionary<string, object?>
                    {
                        ["isSizeLimitCheck"] = true,
                        ["allowedDiskTypes"] = new[]
                        {
                            "pd-ssd",
                            "pd-standard",
                        },
                    }),
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.Project;
import com.pulumi.gcp.organizations.ProjectArgs;
import com.pulumi.gcp.orgpolicy.Policy;
import com.pulumi.gcp.orgpolicy.PolicyArgs;
import com.pulumi.gcp.orgpolicy.inputs.PolicySpecArgs;
import static com.pulumi.codegen.internal.Serialization.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var basic = new Project("basic", ProjectArgs.builder()
            .projectId("id")
            .name("id")
            .orgId("123456789")
            .deletionPolicy("DELETE")
            .build());

        var primary = new Policy("primary", PolicyArgs.builder()
            .name(basic.name().applyValue(_name -> String.format("projects/%s/policies/compute.managed.restrictDiskCreation", _name)))
            .parent(basic.name().applyValue(_name -> String.format("projects/%s", _name)))
            .spec(PolicySpecArgs.builder()
                .rules(PolicySpecRuleArgs.builder()
                    .enforce("TRUE")
                    .parameters(serializeJson(
                        jsonObject(
                            jsonProperty("isSizeLimitCheck", true),
                            jsonProperty("allowedDiskTypes", jsonArray(
                                "pd-ssd", 
                                "pd-standard"
                            ))
                        )))
                    .build())
                .build())
            .build());

    }
}
resources:
  primary:
    type: gcp:orgpolicy:Policy
    properties:
      name: projects/${basic.name}/policies/compute.managed.restrictDiskCreation
      parent: projects/${basic.name}
      spec:
        rules:
          - enforce: TRUE
            parameters:
              fn::toJSON:
                isSizeLimitCheck: true
                allowedDiskTypes:
                  - pd-ssd
                  - pd-standard
  basic:
    type: gcp:organizations:Project
    properties:
      projectId: id
      name: id
      orgId: '123456789'
      deletionPolicy: DELETE

The parameters property passes JSON-encoded configuration to custom constraints. This example sends isSizeLimitCheck and allowedDiskTypes to the compute.managed.restrictDiskCreation constraint. Parameters let you reuse constraints across environments with different settings.

Beyond these examples

These snippets focus on specific policy-level features: enforcement at project, folder, and organization levels, conditional rules and value constraints, and dry-run testing and parameter passing. They’re intentionally minimal rather than full governance frameworks.

The examples may reference pre-existing infrastructure such as GCP projects, folders, or organizations, custom constraints for parameter-based policies, and resource tags for conditional matching. They focus on configuring the policy rather than provisioning the entire resource hierarchy.

To keep things focused, common policy patterns are omitted, including:

  • Policy versioning and etag-based concurrency control
  • Multiple rule combinations within a single policy
  • Cross-service constraint coordination
  • Policy deletion and cleanup workflows

These omissions are intentional: the goal is to illustrate how each policy feature is wired, not provide drop-in governance modules. See the Organization Policy resource reference for all available configuration options.

Let's configure GCP Organization Policies

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Policy Configuration & Structure
What's the correct format for the policy name?
The name must follow one of these patterns: projects/{project_number}/policies/{constraint_name}, folders/{folder_id}/policies/{constraint_name}, or organizations/{organization_id}/policies/{constraint_name}. For example, projects/123/policies/compute.disableSerialPortAccess.
What properties are immutable after creation?
Both name and parent are immutable and cannot be changed after the policy is created.
Why does my policy name show a project number instead of the project ID I provided?
The API accepts projects/{project_id}/policies/{constraint_name} in requests, but responses always return the name using the equivalent project number.
Rules & Enforcement
What rule types can I use in a policy?
Rules support four types: enforce (TRUE/FALSE), denyAll (TRUE), allowAll (TRUE), and values with allowedValues or deniedValues lists. You can also add condition expressions to rules.
How do I pass custom parameters to a policy?
Use the parameters field with a JSON string containing your custom parameters. For example, parameters: JSON.stringify({isSizeLimitCheck: true, allowedDiskTypes: ["pd-ssd"]}).
Testing & Dry Runs
What's the difference between spec and dryRunSpec?
spec defines the enforced policy that actively restricts resources, while dryRunSpec is audit-only and monitors how the policy would impact resources without actually enforcing it.
How do I test a policy without enforcing it?
Configure dryRunSpec with your desired rules. This creates an audit-only policy that monitors impact without enforcement.
Advanced Features
Can I inherit policies from parent resources?
Yes, set inheritFromParent: true in the spec to inherit policy rules from the parent resource.
How do I reset a policy to its default state?
Set reset: true in the spec to reset the policy to default.
What's the etag field used for?
The etag is computed by the server based on the policy state and used for concurrency control. Send it on update and delete requests to ensure you have an up-to-date value before proceeding.

Using a different cloud?

Explore security guides for other cloud providers: