Manage GCP Bigtable IAM Policies

The gcp:bigtable/tableIamPolicy:TableIamPolicy resource, part of the Pulumi GCP provider, controls IAM access to Bigtable tables through three distinct resources: TableIamPolicy (replaces entire policy), TableIamBinding (manages one role), and TableIamMember (adds individual members). This guide focuses on three capabilities: authoritative policy replacement, role-level binding management, and incremental member addition.

These resources reference existing Bigtable tables and instances. TableIamPolicy cannot be used with TableIamBinding or TableIamMember, as they will conflict over policy state. TableIamBinding and TableIamMember can coexist if they manage different roles. The examples are intentionally small. Combine them with your own table infrastructure and access requirements.

Replace the entire IAM policy for a table

When you need complete control over table access, you can set the entire IAM policy at once. This is useful for initial setup or when migrating from external access control systems.

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

const admin = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/bigtable.user",
        members: ["user:jane@example.com"],
    }],
});
const editor = new gcp.bigtable.TableIamPolicy("editor", {
    project: "your-project",
    instanceName: "your-bigtable-instance",
    table: "your-bigtable-table",
    policyData: admin.then(admin => admin.policyData),
});
import pulumi
import pulumi_gcp as gcp

admin = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/bigtable.user",
    "members": ["user:jane@example.com"],
}])
editor = gcp.bigtable.TableIamPolicy("editor",
    project="your-project",
    instance_name="your-bigtable-instance",
    table="your-bigtable-table",
    policy_data=admin.policy_data)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/bigtable"
	"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/bigtable.user",
					Members: []string{
						"user:jane@example.com",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = bigtable.NewTableIamPolicy(ctx, "editor", &bigtable.TableIamPolicyArgs{
			Project:      pulumi.String("your-project"),
			InstanceName: pulumi.String("your-bigtable-instance"),
			Table:        pulumi.String("your-bigtable-table"),
			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/bigtable.user",
                Members = new[]
                {
                    "user:jane@example.com",
                },
            },
        },
    });

    var editor = new Gcp.BigTable.TableIamPolicy("editor", new()
    {
        Project = "your-project",
        InstanceName = "your-bigtable-instance",
        Table = "your-bigtable-table",
        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.bigtable.TableIamPolicy;
import com.pulumi.gcp.bigtable.TableIamPolicyArgs;
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/bigtable.user")
                .members("user:jane@example.com")
                .build())
            .build());

        var editor = new TableIamPolicy("editor", TableIamPolicyArgs.builder()
            .project("your-project")
            .instanceName("your-bigtable-instance")
            .table("your-bigtable-table")
            .policyData(admin.policyData())
            .build());

    }
}
resources:
  editor:
    type: gcp:bigtable:TableIamPolicy
    properties:
      project: your-project
      instanceName: your-bigtable-instance
      table: your-bigtable-table
      policyData: ${admin.policyData}
variables:
  admin:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/bigtable.user
            members:
              - user:jane@example.com

The policyData property accepts output from getIAMPolicy, which defines all role bindings in a single structure. TableIamPolicy replaces the existing policy entirely, so any roles not included in policyData are removed. This makes it authoritative: what you declare is exactly what exists.

Grant a role to multiple members at once

Teams often need to grant the same role to several users or service accounts without affecting other role assignments.

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

const editor = new gcp.bigtable.TableIamBinding("editor", {
    table: "your-bigtable-table",
    instanceName: "your-bigtable-instance",
    role: "roles/bigtable.user",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

editor = gcp.bigtable.TableIamBinding("editor",
    table="your-bigtable-table",
    instance_name="your-bigtable-instance",
    role="roles/bigtable.user",
    members=["user:jane@example.com"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := bigtable.NewTableIamBinding(ctx, "editor", &bigtable.TableIamBindingArgs{
			Table:        pulumi.String("your-bigtable-table"),
			InstanceName: pulumi.String("your-bigtable-instance"),
			Role:         pulumi.String("roles/bigtable.user"),
			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 editor = new Gcp.BigTable.TableIamBinding("editor", new()
    {
        Table = "your-bigtable-table",
        InstanceName = "your-bigtable-instance",
        Role = "roles/bigtable.user",
        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.bigtable.TableIamBinding;
import com.pulumi.gcp.bigtable.TableIamBindingArgs;
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 editor = new TableIamBinding("editor", TableIamBindingArgs.builder()
            .table("your-bigtable-table")
            .instanceName("your-bigtable-instance")
            .role("roles/bigtable.user")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  editor:
    type: gcp:bigtable:TableIamBinding
    properties:
      table: your-bigtable-table
      instanceName: your-bigtable-instance
      role: roles/bigtable.user
      members:
        - user:jane@example.com

TableIamBinding manages one role at a time. The members array lists everyone who should have that role. Other roles on the table remain unchanged, making this safer than full policy replacement when you only need to manage specific roles.

Add a single member to a role incrementally

When onboarding individual users or service accounts, you can add them to a role without listing all existing members.

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

const editor = new gcp.bigtable.TableIamMember("editor", {
    table: "your-bigtable-table",
    instanceName: "your-bigtable-instance",
    role: "roles/bigtable.user",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

editor = gcp.bigtable.TableIamMember("editor",
    table="your-bigtable-table",
    instance_name="your-bigtable-instance",
    role="roles/bigtable.user",
    member="user:jane@example.com")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := bigtable.NewTableIamMember(ctx, "editor", &bigtable.TableIamMemberArgs{
			Table:        pulumi.String("your-bigtable-table"),
			InstanceName: pulumi.String("your-bigtable-instance"),
			Role:         pulumi.String("roles/bigtable.user"),
			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 editor = new Gcp.BigTable.TableIamMember("editor", new()
    {
        Table = "your-bigtable-table",
        InstanceName = "your-bigtable-instance",
        Role = "roles/bigtable.user",
        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.bigtable.TableIamMember;
import com.pulumi.gcp.bigtable.TableIamMemberArgs;
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 editor = new TableIamMember("editor", TableIamMemberArgs.builder()
            .table("your-bigtable-table")
            .instanceName("your-bigtable-instance")
            .role("roles/bigtable.user")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  editor:
    type: gcp:bigtable:TableIamMember
    properties:
      table: your-bigtable-table
      instanceName: your-bigtable-instance
      role: roles/bigtable.user
      member: user:jane@example.com

TableIamMember adds one member to one role. This is the most granular approach and the safest for concurrent modifications, since multiple TableIamMember resources can add different members to the same role without conflict.

Beyond these examples

These snippets focus on specific IAM management approaches: authoritative policy replacement, role-level binding management, and incremental member addition. They’re intentionally minimal rather than full access control configurations.

The examples reference pre-existing infrastructure such as Bigtable tables and instances. They focus on IAM policy configuration rather than provisioning the tables themselves.

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

  • Conditional IAM bindings (condition blocks)
  • Service account impersonation
  • Cross-project access grants
  • Audit logging configuration

These omissions are intentional: the goal is to illustrate how each IAM resource type modifies policies, not provide drop-in access control modules. See the Bigtable TableIamPolicy resource reference for all available configuration options.

Let's manage GCP Bigtable 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 Conflicts & Compatibility
Can I use TableIamPolicy with TableIamBinding or TableIamMember?
No, TableIamPolicy cannot be used with TableIamBinding or TableIamMember because they will conflict over the policy configuration.
What's the risk of using TableIamPolicy incorrectly?
TableIamPolicy replaces the entire IAM policy, so you can accidentally remove existing permissions, including table ownership. Ensure all necessary permissions are included in your policy data.
Can I use TableIamBinding and TableIamMember together?
Yes, but only if they don’t grant privileges to the same role. If both resources target the same role, they will conflict.
Resource Selection & Usage
Which IAM resource should I use for my Bigtable table?

Choose based on your needs:

  • TableIamPolicy: Authoritative, replaces the entire policy
  • TableIamBinding: Authoritative for a specific role, preserves other roles
  • TableIamMember: Non-authoritative, adds a single member to a role while preserving other members
How do I set IAM policies using TableIamPolicy?
Use gcp.organizations.getIAMPolicy to generate policy data with role bindings, then pass the policyData to TableIamPolicy.
How do I grant a role to multiple members?
Use TableIamBinding with the role property and a members array containing the member identifiers.
How do I add a single member to a role without affecting others?
Use TableIamMember with the role and member properties. This preserves existing members for that role.

Using a different cloud?

Explore security guides for other cloud providers: