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, non-authoritative member grants, time-limited access with IAM Conditions, and audit logging.
IAM resources reference existing folders by ID. IAMPolicy is authoritative and replaces the entire policy; IAMBinding and IAMMember are safer for incremental changes. The examples are intentionally small. Combine them with your own folder hierarchy and organizational policies.
Replace all folder permissions with a complete policy
Organizations managing folder access entirely through code define the complete IAM policy in one place, replacing any manually-added permissions.
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 getIAMPolicy data source constructs a policy document from bindings. The IAMPolicy resource applies this policy to the folder, replacing any existing permissions. This is authoritative: deleting the resource removes ALL access, potentially locking you out. Import existing policies before applying changes, and avoid using this on your provider folder.
Set time-limited permissions with IAM Conditions
Temporary access grants, such as contractor permissions or time-boxed experiments, need expiration dates to revoke access automatically.
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")
The condition block adds time-based constraints to role bindings. The expression uses CEL (Common Expression Language) to compare request.time against a timestamp. When the condition evaluates to false, access is denied. IAM Conditions cannot be used with Basic Roles like Owner; use predefined or custom roles instead.
Grant a single member access without affecting others
When adding one person or service account, you often want to preserve existing permissions rather than managing the complete member list.
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
IAMMember is non-authoritative: it adds one member to a role without affecting other members. The member property uses the format “user:email”, “serviceAccount:email”, or “group:email”. You can safely use multiple IAMMember resources for the same folder as long as they grant different roles.
Enable audit logging for folder operations
Compliance requirements often mandate logging of administrative actions and data access for security monitoring.
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
IamAuditConfig enables Cloud Audit Logs for a service. The service property accepts “allServices” or specific service names like “storage.googleapis.com”. Each auditLogConfig specifies a logType (ADMIN_READ, DATA_READ, DATA_WRITE) and optional exemptedMembers who bypass logging. Logs flow to Cloud Logging; configure log sinks separately for long-term storage.
Beyond these examples
These snippets focus on specific folder IAM features: authoritative vs non-authoritative access control, time-based access with IAM Conditions, and audit logging configuration. They’re intentionally minimal rather than complete access management solutions.
The examples reference pre-existing infrastructure such as GCP folders (folders/{folder_id}) and assume parent folder or organization permissions for recovery. They focus on configuring IAM policies rather than provisioning the folder hierarchy.
To keep things focused, common IAM patterns are omitted, including:
- Custom role definitions (organizations/{{org_id}}/roles/{{role_id}})
- Service account and group member types
- Multiple condition expressions (AND/OR logic)
- Audit log exemptions for specific operations
These omissions are intentional: the goal is to illustrate how each IAM resource type 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 FREEFrequently Asked Questions
Resource Selection & Compatibility
Choose based on your management needs:
gcp.folder.IAMPolicy: Authoritative, replaces the entire IAM policygcp.folder.IAMBinding: Authoritative for a specific role, preserves other rolesgcp.folder.IAMMember: Non-authoritative, adds a single member to a role while preserving other membersgcp.folder.IamAuditConfig: Authoritative for audit logging configuration
gcp.folder.IAMPolicy cannot be used with any other folder IAM resource (they will conflict). However, gcp.folder.IAMBinding and gcp.folder.IAMMember can be used together only if they don’t manage the same role.Common Pitfalls & Errors
gcp.folder.IAMPolicy removes all access for anyone without parent folder/organization permissions. To avoid lockout: import the existing policy before applying changes, avoid using IAMPolicy with your provider folder, and only use it with folders fully managed by Pulumi.IAM Conditions
condition block with title, description, and expression properties. For example, to expire access at a specific time: expression: "request.time < timestamp(\"2020-01-01T00:00:00Z\")".Configuration & Import
folders/{folder_id}, for example: folders/1234567. This property is immutable after creation.gcp.folder.IAMMember uses singular member for a single identity, while gcp.folder.IAMBinding uses plural members for a list of identities.organizations/{{org_id}}/roles/{{role_id}} instead of just the role ID.pulumi import gcp:folder/iAMBinding:IAMBinding my_folder "folder roles/{{role_id}} condition-title".