Manage GCP Spanner Instance IAM Permissions

The gcp:spanner/instanceIAMMember:InstanceIAMMember resource, part of the Pulumi GCP provider, grants IAM permissions on Spanner instances using three different authoritativeness levels. This guide focuses on three approaches: non-authoritative single-member grants (InstanceIAMMember), authoritative role-level bindings (InstanceIAMBinding), and complete policy replacement (InstanceIAMPolicy).

These resources reference existing Spanner instances and grant access to users, service accounts, or groups. The examples are intentionally small. Combine them with your own instance infrastructure and identity management.

Grant a single user access to an instance

Most teams add individual users or service accounts to roles without affecting existing permissions.

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

const instance = new gcp.spanner.InstanceIAMMember("instance", {
    instance: "your-instance-name",
    role: "roles/spanner.databaseAdmin",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.spanner.InstanceIAMMember("instance",
    instance="your-instance-name",
    role="roles/spanner.databaseAdmin",
    member="user:jane@example.com")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := spanner.NewInstanceIAMMember(ctx, "instance", &spanner.InstanceIAMMemberArgs{
			Instance: pulumi.String("your-instance-name"),
			Role:     pulumi.String("roles/spanner.databaseAdmin"),
			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 instance = new Gcp.Spanner.InstanceIAMMember("instance", new()
    {
        Instance = "your-instance-name",
        Role = "roles/spanner.databaseAdmin",
        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.spanner.InstanceIAMMember;
import com.pulumi.gcp.spanner.InstanceIAMMemberArgs;
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 instance = new InstanceIAMMember("instance", InstanceIAMMemberArgs.builder()
            .instance("your-instance-name")
            .role("roles/spanner.databaseAdmin")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  instance:
    type: gcp:spanner:InstanceIAMMember
    properties:
      instance: your-instance-name
      role: roles/spanner.databaseAdmin
      member: user:jane@example.com

The InstanceIAMMember resource is non-authoritative: it adds one member to a role while preserving other members already assigned to that role. The member property accepts user emails, service accounts, groups, or special identifiers like allUsers. The role property specifies a predefined Spanner role like roles/spanner.databaseAdmin.

Manage all members for a single role

When you need to control the complete list of who has a specific role, InstanceIAMBinding defines all members at once.

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

const instance = new gcp.spanner.InstanceIAMBinding("instance", {
    instance: "your-instance-name",
    role: "roles/spanner.databaseAdmin",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

instance = gcp.spanner.InstanceIAMBinding("instance",
    instance="your-instance-name",
    role="roles/spanner.databaseAdmin",
    members=["user:jane@example.com"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := spanner.NewInstanceIAMBinding(ctx, "instance", &spanner.InstanceIAMBindingArgs{
			Instance: pulumi.String("your-instance-name"),
			Role:     pulumi.String("roles/spanner.databaseAdmin"),
			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 instance = new Gcp.Spanner.InstanceIAMBinding("instance", new()
    {
        Instance = "your-instance-name",
        Role = "roles/spanner.databaseAdmin",
        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.spanner.InstanceIAMBinding;
import com.pulumi.gcp.spanner.InstanceIAMBindingArgs;
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 instance = new InstanceIAMBinding("instance", InstanceIAMBindingArgs.builder()
            .instance("your-instance-name")
            .role("roles/spanner.databaseAdmin")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  instance:
    type: gcp:spanner:InstanceIAMBinding
    properties:
      instance: your-instance-name
      role: roles/spanner.databaseAdmin
      members:
        - user:jane@example.com

The InstanceIAMBinding resource is authoritative for the specified role: it sets the complete member list and removes anyone not included. The members property takes an array of identities. Other roles on the instance remain unchanged. You can use InstanceIAMBinding alongside InstanceIAMMember as long as they don’t target the same role.

Replace the entire IAM policy for an instance

In rare cases, you need complete control over all roles and members, replacing any existing policy entirely.

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 instance = new gcp.spanner.InstanceIAMPolicy("instance", {
    instance: "your-instance-name",
    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"],
}])
instance = gcp.spanner.InstanceIAMPolicy("instance",
    instance="your-instance-name",
    policy_data=admin.policy_data)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/spanner"
	"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 = spanner.NewInstanceIAMPolicy(ctx, "instance", &spanner.InstanceIAMPolicyArgs{
			Instance:   pulumi.String("your-instance-name"),
			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 instance = new Gcp.Spanner.InstanceIAMPolicy("instance", new()
    {
        Instance = "your-instance-name",
        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.spanner.InstanceIAMPolicy;
import com.pulumi.gcp.spanner.InstanceIAMPolicyArgs;
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 instance = new InstanceIAMPolicy("instance", InstanceIAMPolicyArgs.builder()
            .instance("your-instance-name")
            .policyData(admin.policyData())
            .build());

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

The InstanceIAMPolicy resource is fully authoritative: it replaces the entire IAM policy for the instance. The policyData property comes from getIAMPolicy, which defines all role bindings. This approach can lock you out if you omit default permissions. InstanceIAMPolicy cannot be used with InstanceIAMBinding or InstanceIAMMember; they will conflict.

Beyond these examples

These snippets focus on specific IAM management features: non-authoritative member grants, authoritative role bindings, and complete policy replacement. They’re intentionally minimal rather than full access control configurations.

The examples reference pre-existing infrastructure such as Spanner instances. They focus on granting permissions rather than provisioning instances or identities.

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

  • Conditional IAM bindings (condition property)
  • Cross-project instance references (project property)
  • Custom role definitions
  • Service account creation and management

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 Spanner InstanceIAMMember resource reference for all available configuration options.

Let's manage GCP Spanner Instance IAM Permissions

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 & Conflicts
What's the difference between InstanceIAMPolicy, InstanceIAMBinding, and InstanceIAMMember?
gcp.spanner.InstanceIAMPolicy is authoritative and replaces the entire IAM policy. gcp.spanner.InstanceIAMBinding is authoritative for a specific role, managing all members for that role while preserving other roles. gcp.spanner.InstanceIAMMember is non-authoritative, adding individual members without affecting existing members for the role.
Can I use InstanceIAMPolicy with other IAM resources?
No, gcp.spanner.InstanceIAMPolicy cannot be used with gcp.spanner.InstanceIAMBinding or gcp.spanner.InstanceIAMMember because they will conflict over the policy state.
Can I use InstanceIAMBinding and InstanceIAMMember together?
Yes, but only if they don’t grant privileges to the same role. Each role must be managed by only one resource type.
How can I avoid locking myself out when using InstanceIAMPolicy?
gcp.spanner.InstanceIAMPolicy removes all default permissions not explicitly included in your configuration. Always include necessary permissions in your policy, or use gcp.spanner.InstanceIAMBinding or gcp.spanner.InstanceIAMMember for safer, incremental management.
Configuration & Identity Formats
What identity formats can I use in the member property?
You can use: allUsers (anyone on the internet), allAuthenticatedUsers (anyone with a Google account), user:{email} (specific Google account), serviceAccount:{email} (service account), principal:{principal} (federated identity), principalSet:{principalSet} (federated group), group:{email} (Google group), or domain:{domain} (G Suite domain).
What format should I use for custom roles?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}. For example, projects/my-project/roles/my-custom-role.
Immutability & Limitations
What properties can I change after creating the resource?
None. All properties (instance, member, project, role, and condition) are immutable and require resource replacement if changed.

Using a different cloud?

Explore iam guides for other cloud providers: