Configure GCP Spanner Database IAM Permissions

The gcp:spanner/databaseIAMBinding:DatabaseIAMBinding resource, part of the Pulumi GCP provider, manages IAM role bindings for Spanner databases by granting roles to lists of members. This guide focuses on three capabilities: authoritative role binding, conditional access with IAM Conditions, and non-authoritative member grants.

DatabaseIAMBinding is one of three resources for managing Spanner database IAM. It authoritatively controls all members for a specific role, replacing any existing grants for that role while preserving other roles. The examples are intentionally small. Combine them with your own Spanner infrastructure and identity management.

Grant a role to multiple members with DatabaseIAMBinding

Teams managing Spanner access often grant the same role to multiple users or service accounts at once.

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

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

database = gcp.spanner.DatabaseIAMBinding("database",
    instance="your-instance-name",
    database="your-database-name",
    role="roles/compute.networkUser",
    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.NewDatabaseIAMBinding(ctx, "database", &spanner.DatabaseIAMBindingArgs{
			Instance: pulumi.String("your-instance-name"),
			Database: pulumi.String("your-database-name"),
			Role:     pulumi.String("roles/compute.networkUser"),
			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 database = new Gcp.Spanner.DatabaseIAMBinding("database", new()
    {
        Instance = "your-instance-name",
        Database = "your-database-name",
        Role = "roles/compute.networkUser",
        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.DatabaseIAMBinding;
import com.pulumi.gcp.spanner.DatabaseIAMBindingArgs;
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 database = new DatabaseIAMBinding("database", DatabaseIAMBindingArgs.builder()
            .instance("your-instance-name")
            .database("your-database-name")
            .role("roles/compute.networkUser")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  database:
    type: gcp:spanner:DatabaseIAMBinding
    properties:
      instance: your-instance-name
      database: your-database-name
      role: roles/compute.networkUser
      members:
        - user:jane@example.com

DatabaseIAMBinding replaces all members for the specified role. The members array accepts user accounts, service accounts, groups, and special identifiers like allUsers. The role property specifies which IAM role to grant; only one DatabaseIAMBinding can exist per role per database.

Apply conditional access with IAM Conditions

Fine-grained access control requires conditions that limit when permissions apply, such as restricting access to specific database roles.

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

const database = new gcp.spanner.DatabaseIAMBinding("database", {
    instance: "your-instance-name",
    database: "your-database-name",
    role: "roles/compute.networkUser",
    members: ["user:jane@example.com"],
    condition: {
        title: "My Role",
        description: "Grant permissions on my_role",
        expression: "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))",
    },
});
import pulumi
import pulumi_gcp as gcp

database = gcp.spanner.DatabaseIAMBinding("database",
    instance="your-instance-name",
    database="your-database-name",
    role="roles/compute.networkUser",
    members=["user:jane@example.com"],
    condition={
        "title": "My Role",
        "description": "Grant permissions on my_role",
        "expression": "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))",
    })
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.NewDatabaseIAMBinding(ctx, "database", &spanner.DatabaseIAMBindingArgs{
			Instance: pulumi.String("your-instance-name"),
			Database: pulumi.String("your-database-name"),
			Role:     pulumi.String("roles/compute.networkUser"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
			Condition: &spanner.DatabaseIAMBindingConditionArgs{
				Title:       pulumi.String("My Role"),
				Description: pulumi.String("Grant permissions on my_role"),
				Expression:  pulumi.String("(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))"),
			},
		})
		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 database = new Gcp.Spanner.DatabaseIAMBinding("database", new()
    {
        Instance = "your-instance-name",
        Database = "your-database-name",
        Role = "roles/compute.networkUser",
        Members = new[]
        {
            "user:jane@example.com",
        },
        Condition = new Gcp.Spanner.Inputs.DatabaseIAMBindingConditionArgs
        {
            Title = "My Role",
            Description = "Grant permissions on my_role",
            Expression = "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.spanner.DatabaseIAMBinding;
import com.pulumi.gcp.spanner.DatabaseIAMBindingArgs;
import com.pulumi.gcp.spanner.inputs.DatabaseIAMBindingConditionArgs;
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 database = new DatabaseIAMBinding("database", DatabaseIAMBindingArgs.builder()
            .instance("your-instance-name")
            .database("your-database-name")
            .role("roles/compute.networkUser")
            .members("user:jane@example.com")
            .condition(DatabaseIAMBindingConditionArgs.builder()
                .title("My Role")
                .description("Grant permissions on my_role")
                .expression("(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))")
                .build())
            .build());

    }
}
resources:
  database:
    type: gcp:spanner:DatabaseIAMBinding
    properties:
      instance: your-instance-name
      database: your-database-name
      role: roles/compute.networkUser
      members:
        - user:jane@example.com
      condition:
        title: My Role
        description: Grant permissions on my_role
        expression: (resource.type == "spanner.googleapis.com/DatabaseRole" && (resource.name.endsWith("/myrole")))

IAM Conditions use CEL expressions to evaluate resource attributes at access time. The condition block requires a title, optional description, and an expression that returns true or false. In this configuration, the expression restricts access to resources ending with “/myrole”, limiting the role grant to specific database roles rather than the entire database.

Add a single member to a role with DatabaseIAMMember

When multiple teams manage access independently, DatabaseIAMMember adds individual members without affecting other grants.

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

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

database = gcp.spanner.DatabaseIAMMember("database",
    instance="your-instance-name",
    database="your-database-name",
    role="roles/compute.networkUser",
    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.NewDatabaseIAMMember(ctx, "database", &spanner.DatabaseIAMMemberArgs{
			Instance: pulumi.String("your-instance-name"),
			Database: pulumi.String("your-database-name"),
			Role:     pulumi.String("roles/compute.networkUser"),
			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 database = new Gcp.Spanner.DatabaseIAMMember("database", new()
    {
        Instance = "your-instance-name",
        Database = "your-database-name",
        Role = "roles/compute.networkUser",
        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.DatabaseIAMMember;
import com.pulumi.gcp.spanner.DatabaseIAMMemberArgs;
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 database = new DatabaseIAMMember("database", DatabaseIAMMemberArgs.builder()
            .instance("your-instance-name")
            .database("your-database-name")
            .role("roles/compute.networkUser")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  database:
    type: gcp:spanner:DatabaseIAMMember
    properties:
      instance: your-instance-name
      database: your-database-name
      role: roles/compute.networkUser
      member: user:jane@example.com

Unlike DatabaseIAMBinding, DatabaseIAMMember is non-authoritative: it adds one member to a role without replacing existing members. Use this when different parts of your infrastructure need to grant the same role independently. The member property accepts a single identity in the same formats as the members array.

Beyond these examples

These snippets focus on specific IAM binding features: role binding and member management, conditional access with IAM Conditions, and authoritative vs non-authoritative grants. They’re intentionally minimal rather than full access control configurations.

The examples reference pre-existing infrastructure such as Spanner instances and databases. They focus on configuring IAM bindings rather than provisioning the database infrastructure.

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

  • Full IAM policy replacement (DatabaseIAMPolicy)
  • Project-level configuration (project property)
  • Custom role definitions and formats
  • Combining Binding and Member resources safely

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

Let's configure GCP Spanner Database 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 & Compatibility
What's the difference between DatabaseIAMPolicy, DatabaseIAMBinding, and DatabaseIAMMember?
gcp.spanner.DatabaseIAMPolicy is fully authoritative and replaces the entire IAM policy. gcp.spanner.DatabaseIAMBinding is authoritative for a specific role but preserves other roles. gcp.spanner.DatabaseIAMMember is non-authoritative and adds individual members without affecting other members for that role.
Can I use DatabaseIAMPolicy with DatabaseIAMBinding or DatabaseIAMMember?
No, gcp.spanner.DatabaseIAMPolicy cannot be used with gcp.spanner.DatabaseIAMBinding or gcp.spanner.DatabaseIAMMember because they will conflict over policy management. However, DatabaseIAMBinding and DatabaseIAMMember can be used together as long as they don’t grant privileges to the same role.
Common Pitfalls & Errors
How can I avoid locking myself out when using DatabaseIAMPolicy?
gcp.spanner.DatabaseIAMPolicy removes any permissions not explicitly included in your configuration, including default permissions. Always include all necessary permissions in your policy to maintain access.
Configuration & Syntax
What member identity formats are supported?

The members array supports six formats:

  • allUsers (anyone on the internet)
  • allAuthenticatedUsers (anyone with a Google account)
  • user:{emailid} (specific Google account)
  • serviceAccount:{emailid} (service account)
  • group:{emailid} (Google group)
  • domain:{domain} (all users in a G Suite domain)
What's the required format for custom roles?
Custom roles must follow the format [projects|organizations]/{parent-name}/roles/{role-name}. Only one gcp.spanner.DatabaseIAMBinding can be used per role.
Can I use IAM Conditions to restrict access?
Yes, use the condition property with title, description, and expression fields to apply conditional access policies.
Immutability & Updates
What properties can't be changed after creation?
The database, instance, project, role, and condition properties are all immutable and require resource replacement if changed.

Using a different cloud?

Explore database guides for other cloud providers: