Manage GCP Folder IAM Policies

The gcp:folder/iAMPolicy:IAMPolicy resource, part of the Pulumi GCP provider, manages IAM policies for GCP folders, controlling who can access folder resources and what actions they can perform. This guide focuses on four capabilities: authoritative policy replacement, role-based member management, time-bound access with IAM Conditions, and audit logging.

IAM resources reference existing folders by ID and assume user or service account identities already exist. The examples are intentionally small. Combine them with your own folder hierarchy and identity management.

Replace all folder permissions with a complete policy

Organizations managing folders through infrastructure-as-code sometimes need to define the complete IAM policy in one place, ensuring no permissions exist outside the declared state.

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

const admin = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/editor",
        members: ["user:jane@example.com"],
    }],
});
const folder = new gcp.folder.IAMPolicy("folder", {
    folder: "folders/1234567",
    policyData: admin.then(admin => admin.policyData),
});
import pulumi
import pulumi_gcp as gcp

admin = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/editor",
    "members": ["user:jane@example.com"],
}])
folder = gcp.folder.IAMPolicy("folder",
    folder="folders/1234567",
    policy_data=admin.policy_data)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		admin, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
			Bindings: []organizations.GetIAMPolicyBinding{
				{
					Role: "roles/editor",
					Members: []string{
						"user:jane@example.com",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = folder.NewIAMPolicy(ctx, "folder", &folder.IAMPolicyArgs{
			Folder:     pulumi.String("folders/1234567"),
			PolicyData: pulumi.String(admin.PolicyData),
		})
		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 admin = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/editor",
                Members = new[]
                {
                    "user:jane@example.com",
                },
            },
        },
    });

    var folder = new Gcp.Folder.IAMPolicy("folder", new()
    {
        Folder = "folders/1234567",
        PolicyData = admin.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.folder.IAMPolicy;
import com.pulumi.gcp.folder.IAMPolicyArgs;
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) {
        final var admin = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/editor")
                .members("user:jane@example.com")
                .build())
            .build());

        var folder = new IAMPolicy("folder", IAMPolicyArgs.builder()
            .folder("folders/1234567")
            .policyData(admin.policyData())
            .build());

    }
}
resources:
  folder:
    type: gcp:folder:IAMPolicy
    properties:
      folder: folders/1234567
      policyData: ${admin.policyData}
variables:
  admin:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/editor
            members:
              - user:jane@example.com

The IAMPolicy resource is authoritative: it replaces the entire IAM policy for the folder. The policyData property comes from the getIAMPolicy data source, which constructs a policy document from bindings. Each binding maps a role to a list of members. Deleting this resource removes all access, so it’s critical to import existing policies before applying changes.

Apply time-bound permissions with IAM Conditions

Temporary access grants often require expiration dates to limit the window of elevated privileges.

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

const admin = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/compute.admin",
        members: ["user:jane@example.com"],
        condition: {
            title: "expires_after_2019_12_31",
            description: "Expiring at midnight of 2019-12-31",
            expression: "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
        },
    }],
});
const folder = new gcp.folder.IAMPolicy("folder", {
    folder: "folders/1234567",
    policyData: admin.then(admin => admin.policyData),
});
import pulumi
import pulumi_gcp as gcp

admin = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/compute.admin",
    "members": ["user:jane@example.com"],
    "condition": {
        "title": "expires_after_2019_12_31",
        "description": "Expiring at midnight of 2019-12-31",
        "expression": "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
    },
}])
folder = gcp.folder.IAMPolicy("folder",
    folder="folders/1234567",
    policy_data=admin.policy_data)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		admin, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
			Bindings: []organizations.GetIAMPolicyBinding{
				{
					Role: "roles/compute.admin",
					Members: []string{
						"user:jane@example.com",
					},
					Condition: {
						Title:       "expires_after_2019_12_31",
						Description: pulumi.StringRef("Expiring at midnight of 2019-12-31"),
						Expression:  "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = folder.NewIAMPolicy(ctx, "folder", &folder.IAMPolicyArgs{
			Folder:     pulumi.String("folders/1234567"),
			PolicyData: pulumi.String(admin.PolicyData),
		})
		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 admin = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/compute.admin",
                Members = new[]
                {
                    "user:jane@example.com",
                },
                Condition = new Gcp.Organizations.Inputs.GetIAMPolicyBindingConditionInputArgs
                {
                    Title = "expires_after_2019_12_31",
                    Description = "Expiring at midnight of 2019-12-31",
                    Expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
                },
            },
        },
    });

    var folder = new Gcp.Folder.IAMPolicy("folder", new()
    {
        Folder = "folders/1234567",
        PolicyData = admin.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.folder.IAMPolicy;
import com.pulumi.gcp.folder.IAMPolicyArgs;
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) {
        final var admin = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/compute.admin")
                .members("user:jane@example.com")
                .condition(GetIAMPolicyBindingConditionArgs.builder()
                    .title("expires_after_2019_12_31")
                    .description("Expiring at midnight of 2019-12-31")
                    .expression("request.time < timestamp(\"2020-01-01T00:00:00Z\")")
                    .build())
                .build())
            .build());

        var folder = new IAMPolicy("folder", IAMPolicyArgs.builder()
            .folder("folders/1234567")
            .policyData(admin.policyData())
            .build());

    }
}
resources:
  folder:
    type: gcp:folder:IAMPolicy
    properties:
      folder: folders/1234567
      policyData: ${admin.policyData}
variables:
  admin:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/compute.admin
            members:
              - user:jane@example.com
            condition:
              title: expires_after_2019_12_31
              description: Expiring at midnight of 2019-12-31
              expression: request.time < timestamp("2020-01-01T00:00:00Z")

IAM Conditions add temporal or attribute-based constraints to role bindings. The condition block requires a title, an expression using Common Expression Language (CEL), and an optional description. The expression request.time < timestamp("2020-01-01T00:00:00Z") grants access only until the specified timestamp. Conditions cannot be used with Basic Roles like Owner.

Grant a role to multiple members

Teams often need to assign the same role to several users or service accounts without managing each binding individually.

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

const folder = new gcp.folder.IAMBinding("folder", {
    folder: "folders/1234567",
    role: "roles/editor",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

folder = gcp.folder.IAMBinding("folder",
    folder="folders/1234567",
    role="roles/editor",
    members=["user:jane@example.com"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := folder.NewIAMBinding(ctx, "folder", &folder.IAMBindingArgs{
			Folder: pulumi.String("folders/1234567"),
			Role:   pulumi.String("roles/editor"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
		})
		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 folder = new Gcp.Folder.IAMBinding("folder", new()
    {
        Folder = "folders/1234567",
        Role = "roles/editor",
        Members = new[]
        {
            "user:jane@example.com",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.folder.IAMBinding;
import com.pulumi.gcp.folder.IAMBindingArgs;
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 folder = new IAMBinding("folder", IAMBindingArgs.builder()
            .folder("folders/1234567")
            .role("roles/editor")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  folder:
    type: gcp:folder:IAMBinding
    properties:
      folder: folders/1234567
      role: roles/editor
      members:
        - user:jane@example.com

The IAMBinding resource is authoritative for a single role: it replaces all members for that role but preserves other roles in the folder’s policy. The members property accepts a list of identities in the format user:email, serviceAccount:email, group:email, or domain:example.com. You can use IAMBinding alongside IAMMember as long as they manage different roles.

Add a single member to a role

When onboarding individual users or service accounts, you often want to grant access without affecting other members who already have the same role.

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

const folder = new gcp.folder.IAMMember("folder", {
    folder: "folders/1234567",
    role: "roles/editor",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

folder = gcp.folder.IAMMember("folder",
    folder="folders/1234567",
    role="roles/editor",
    member="user:jane@example.com")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := folder.NewIAMMember(ctx, "folder", &folder.IAMMemberArgs{
			Folder: pulumi.String("folders/1234567"),
			Role:   pulumi.String("roles/editor"),
			Member: pulumi.String("user:jane@example.com"),
		})
		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 folder = new Gcp.Folder.IAMMember("folder", new()
    {
        Folder = "folders/1234567",
        Role = "roles/editor",
        Member = "user:jane@example.com",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.folder.IAMMember;
import com.pulumi.gcp.folder.IAMMemberArgs;
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 folder = new IAMMember("folder", IAMMemberArgs.builder()
            .folder("folders/1234567")
            .role("roles/editor")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  folder:
    type: gcp:folder:IAMMember
    properties:
      folder: folders/1234567
      role: roles/editor
      member: user:jane@example.com

The IAMMember resource is non-authoritative: it adds one member to a role without removing existing members. The member property accepts a single identity. Use this when you need to grant access incrementally without coordinating with other IAM resources managing the same role.

Enable audit logging for folder operations

Compliance requirements often mandate logging of administrative actions and data access across all services in a folder.

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

const folder = new gcp.folder.IamAuditConfig("folder", {
    folder: "folders/1234567",
    service: "allServices",
    auditLogConfigs: [
        {
            logType: "ADMIN_READ",
        },
        {
            logType: "DATA_READ",
            exemptedMembers: ["user:joebloggs@example.com"],
        },
    ],
});
import pulumi
import pulumi_gcp as gcp

folder = gcp.folder.IamAuditConfig("folder",
    folder="folders/1234567",
    service="allServices",
    audit_log_configs=[
        {
            "log_type": "ADMIN_READ",
        },
        {
            "log_type": "DATA_READ",
            "exempted_members": ["user:joebloggs@example.com"],
        },
    ])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := folder.NewIamAuditConfig(ctx, "folder", &folder.IamAuditConfigArgs{
			Folder:  pulumi.String("folders/1234567"),
			Service: pulumi.String("allServices"),
			AuditLogConfigs: folder.IamAuditConfigAuditLogConfigArray{
				&folder.IamAuditConfigAuditLogConfigArgs{
					LogType: pulumi.String("ADMIN_READ"),
				},
				&folder.IamAuditConfigAuditLogConfigArgs{
					LogType: pulumi.String("DATA_READ"),
					ExemptedMembers: pulumi.StringArray{
						pulumi.String("user:joebloggs@example.com"),
					},
				},
			},
		})
		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 folder = new Gcp.Folder.IamAuditConfig("folder", new()
    {
        Folder = "folders/1234567",
        Service = "allServices",
        AuditLogConfigs = new[]
        {
            new Gcp.Folder.Inputs.IamAuditConfigAuditLogConfigArgs
            {
                LogType = "ADMIN_READ",
            },
            new Gcp.Folder.Inputs.IamAuditConfigAuditLogConfigArgs
            {
                LogType = "DATA_READ",
                ExemptedMembers = new[]
                {
                    "user:joebloggs@example.com",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.folder.IamAuditConfig;
import com.pulumi.gcp.folder.IamAuditConfigArgs;
import com.pulumi.gcp.folder.inputs.IamAuditConfigAuditLogConfigArgs;
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 folder = new IamAuditConfig("folder", IamAuditConfigArgs.builder()
            .folder("folders/1234567")
            .service("allServices")
            .auditLogConfigs(            
                IamAuditConfigAuditLogConfigArgs.builder()
                    .logType("ADMIN_READ")
                    .build(),
                IamAuditConfigAuditLogConfigArgs.builder()
                    .logType("DATA_READ")
                    .exemptedMembers("user:joebloggs@example.com")
                    .build())
            .build());

    }
}
resources:
  folder:
    type: gcp:folder:IamAuditConfig
    properties:
      folder: folders/1234567
      service: allServices
      auditLogConfigs:
        - logType: ADMIN_READ
        - logType: DATA_READ
          exemptedMembers:
            - user:joebloggs@example.com

The IamAuditConfig resource enables Cloud Audit Logs for a folder. The service property specifies which GCP service to audit (use “allServices” for folder-wide logging). Each auditLogConfig entry defines a logType (ADMIN_READ, DATA_READ, DATA_WRITE) and optional exemptedMembers who won’t generate logs for that type.

Beyond these examples

These snippets focus on specific IAM management features: authoritative and non-authoritative IAM management, time-bound access with IAM Conditions, and audit logging configuration. They’re intentionally minimal rather than full access control systems.

The examples reference pre-existing infrastructure such as GCP folders (folders/{folder_id}) and user accounts or service accounts referenced in bindings. They focus on configuring IAM policies rather than provisioning the folder hierarchy or identity infrastructure.

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

  • IAM policy import to avoid lockouts
  • Combining IAMBinding with IAMMember for different roles
  • Custom roles and organization-level policies
  • Condition expressions beyond time-based expiration

These omissions are intentional: the goal is to illustrate how each IAM resource is wired, not provide drop-in access control modules. See the Folder IAMPolicy resource reference for all available configuration options.

Let's manage GCP Folder IAM 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 Selection & Compatibility
What's the difference between IAMPolicy, IAMBinding, and IAMMember?
gcp.folder.IAMPolicy is authoritative and replaces the entire policy. gcp.folder.IAMBinding is authoritative for a single role, preserving other roles. gcp.folder.IAMMember is non-authoritative, adding individual members without affecting others.
Can I use IAMPolicy with other folder IAM resources?
No, gcp.folder.IAMPolicy cannot be used with gcp.folder.IAMBinding, gcp.folder.IAMMember, or gcp.folder.IamAuditConfig as they will conflict over policy management.
Can I use IAMBinding and IAMMember together?
Yes, but only if they don’t grant privileges to the same role. Using both for the same role will cause conflicts.
Security & Access Risks
What's the risk of using IAMPolicy on my folder?
You can accidentally lock yourself out. Deleting gcp.folder.IAMPolicy removes all access from anyone without parent folder/organization permissions. It’s not recommended for your provider folder and should only be used with fully managed folders.
Should I import the existing policy before applying IAMPolicy?
Yes, it’s recommended to import the policy before applying changes to avoid accidentally removing existing access.
IAM Conditions & Constraints
Can I use IAM Conditions with any role?
No, IAM Conditions cannot be used with Basic Roles like Owner. This violates API constraints and returns a 400 error. Use Conditions only with predefined or custom roles.
How do I add time-based access control to folder IAM policies?
Use the condition block with title, description, and expression fields. For example, set expression to request.time < timestamp("2020-01-01T00:00:00Z") for expiring access.
Configuration
What format does the folder parameter use?
The folder parameter uses the format folders/{folder_id} (e.g., folders/1234567) and is immutable after creation.

Using a different cloud?

Explore security guides for other cloud providers: