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. This guide focuses on three capabilities: enforcement at project, folder, and organization levels; conditional rules with value restrictions; and dry-run testing with parameterized constraints.

Policies attach to existing projects, folders, or organizations and reference predefined or custom constraints. The examples are intentionally small. Combine them with your own resource hierarchy and constraint definitions.

Enforce a policy at the project level

Teams often disable specific security features at the project level to accommodate development workflows.

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 defines enforcement behavior; setting enforce to “FALSE” disables the constraint. This policy prevents service account key upload restrictions for the specified project.

Apply folder-level policies with inheritance

Organizations structure resources into folders to apply governance at scale, with policies inheriting from parent levels.

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 pulls rules from higher levels in the hierarchy before applying folder-specific rules. Setting denyAll to “TRUE” blocks all values for the gcp.resourceLocations constraint, restricting where resources can be created across all projects in the folder.

Reset organization-wide policy to defaults

When policies need to return to their default state, the reset option clears custom rules without deleting the resource.

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

Setting spec.reset to true removes all custom rules and restores the constraint’s default behavior. This is useful when unwinding temporary policy changes or returning to baseline governance.

Define conditional rules with allowed and denied values

Complex policies need different rules for different resources based on 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 property 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, dry-run mode audits how the policy would affect resources without blocking operations.

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

const constraint = new gcp.orgpolicy.CustomConstraint("constraint", {
    name: "custom.disableGkeAutoUpgrade_32270",
    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_32270",
    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_32270"),
			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_32270",
        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_32270")
            .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_32270
      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 enforcement. This lets you preview impact on existing resources and future operations. The spec property defines the actual enforcement rules that will apply once testing is complete.

Pass structured parameters to custom constraints

Custom constraints can accept parameters to make policies more flexible without modifying constraint definitions.

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 a JSON object to the custom constraint. Here, it configures disk creation limits with isSizeLimitCheck and allowedDiskTypes. The constraint evaluates these parameters when checking resource compliance.

Beyond these examples

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

The examples reference pre-existing infrastructure such as projects, folders, or organizations to attach policies to; custom constraints for parameterized policies; and resource tags for conditional rules. They focus on policy configuration rather than provisioning the resource hierarchy.

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

  • Policy deletion and lifecycle management
  • Etag-based concurrency control
  • Multiple rule combinations and precedence
  • Integration with custom constraint creation 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

Resource Naming & Hierarchy
What name format should I use for organization policies?
The name must follow the format {parent_type}/{parent_id}/policies/{constraint_name}, where parent_type is projects, folders, or organizations. For example: projects/123/policies/compute.disableSerialPortAccess. Note that while the API accepts project_id, responses return the equivalent project_number.
What properties are immutable after creation?
Both name and parent are immutable. Changing either property will force resource recreation, so plan these values carefully during initial setup.
What's the parent property used for?
The parent specifies the resource hierarchy level (project, folder, or organization) where the policy applies. It must match the parent specified in the name property.
Policy Rules & Enforcement
What rule types can I configure in a policy?

You can use several rule types:

  1. enforce - Set to "TRUE" or "FALSE" to enable/disable enforcement
  2. denyAll - Set to "TRUE" to deny all values
  3. allowAll - Set to "TRUE" to allow all values
  4. values - Specify allowedValues and deniedValues lists
  5. reset - Set to true to reset policy to default state
Can I add conditional logic to policy rules?
Yes, use the condition field within a rule to specify an expression, description, location, and title. For example: resource.matchTagId('tagKeys/123', 'tagValues/345').
How do I pass custom parameters to a constraint?
Use the parameters field with a JSON string (not an object). For example: JSON.stringify({isSizeLimitCheck: true, allowedDiskTypes: ["pd-ssd"]}).
Policy Inheritance & Reset
How do I inherit policy settings from a parent resource?
Set inheritFromParent to true in the spec. This allows the policy to inherit rules from higher levels in the resource hierarchy.
How do I reset a policy to its default state?
Set reset to true in the spec. This removes all custom rules and restores the policy to its default configuration.
Dry Run & Testing
How do I test a policy without enforcing it?
Use dryRunSpec to create an audit-only policy that monitors how the policy would impact existing and future resources without actually enforcing it. The dryRunSpec has the same structure as spec but operates in monitoring mode.
Advanced Configuration
What's the etag field used for?
The etag is computed by the server based on the policy’s current state and is used for concurrency control. Send it on update and delete requests to ensure you have an up-to-date value before making changes.

Using a different cloud?

Explore security guides for other cloud providers: