This provider plugin allows configuring fine-grained access control (FGAC) for Google Cloud
Spanner through Terraform, including database roles, privilege grants, and role membership. It
is modeled after the sibling project,
terraform-provider-scylladb, which
manages the same kind of ACL concepts for ScyllaDB.
Status: early development. The spanner_role resource is implemented and unit-tested; grants
and role membership are not yet built.
It contains, or will contain, the following:
spanneracl/: client for Cloud Spanner and abstracted methods to manage ACL objects (roles, grants)internal/provider/: Terraform provider and resources (spanner_role; more to come)internal/testutil/: shared test helpers, including a real Cloud Spanner emulator spun up via testcontainersexamples/: example Terraform configurations, embedded into generated docstemplates/: hand-written doc templates (tfplugindocssource of truth) — edit these, notdocs/docs/: generated reference docs and guides — runmake generateafter editingtemplates/orexamples/; do not edit files underdocs/directly, they get overwrittentools/: pinned dev-tool versions (tfplugindocs,copywrite) formake generate, isolated into their own Go module so they don't pollute the provider's owngo.mod
- Terraform >= 1.4
- Go >= 1.26
- Docker (required to run the test suite against the Cloud Spanner emulator)
Whatever identity/credentials the provider authenticates as must be granted the following roles on the target Spanner instance or database (grant at the narrowest scope — a single database — rather than at the instance or project level where possible):
roles/spanner.viewerroles/spanner.databaseUser
The provider manages ACL objects on a single Spanner database, configured via:
| Attribute | Env var | Required | Notes |
|---|---|---|---|
project |
SPANNER_PROJECT |
yes | GCP project ID |
instance |
SPANNER_INSTANCE |
yes | Spanner instance ID |
database |
SPANNER_DATABASE |
yes | Spanner database ID |
credentials |
SPANNER_CREDENTIALS |
no | Inline service account JSON key. Mutually exclusive with below. |
credentials_file |
— | no | Path to a service account JSON key file. |
If neither credentials nor credentials_file is set, the provider falls back to
Application Default Credentials —
see the generated authentication guide (source:
templates/guides/authentication.md.tmpl) for details.
provider "spanner" {
project = "my-gcp-project"
instance = "my-spanner-instance"
database = "my-database"
}
resource "spanner_role" "app_reader" {
name = "app_reader"
}- Clone the repository
- Enter the repository directory
- Build the provider using the Go
installcommand:
go installIf you wish to work on the provider, you'll first need Go and Docker installed on your machine (see Requirements above). Docker is required because the test suite exercises a real Cloud Spanner emulator container instead of mocks.
go build ./... # compile
go vet ./... # static analysis
gofmt -l . # formatting check
go test -v -cover ./... # unit tests + provider-config unit tests + emulator-backed client testsNote: the emulator-backed tests in spanneracl/ start real Docker containers via
testcontainers-go, so they run slower than typical unit
tests.
spanner_role acceptance tests (TF_ACC=1) do not currently pass against the
emulator. Read depends on spanneracl.GetRole, which queries
INFORMATION_SCHEMA.ROLES — a view the Cloud Spanner emulator doesn't implement. Every
resource.Test step runs a post-apply Read internally, so this currently blocks emulator-based
acceptance testing of the resource end-to-end; it doesn't block real usage against actual Spanner.
Exercise the resource against a real Spanner instance/database until the emulator gap is fixed upstream.
To regenerate documentation after editing templates/ or examples/, run:
make generateThis runs (via the isolated tools/ Go module, so tfplugindocs/copywrite versions stay
pinned independently of the provider's own dependencies):
copywrite headers— adds/updates copyright headers (respects.copywrite.hcl'sheader_ignore, which excludesexamples/**andtest_cmds/**)terraform fmt -recursive examples/— formats the example.tffilestfplugindocs generate— renderstemplates/*.md.tmpl(interpolating live schema via{{ .SchemaMarkdown }}and example files via{{ tffile ... }}/{{ codefile ... }}) intodocs/
docs/ is fully regenerated on each run — edit templates/ (or examples/), not docs/
directly.
The following is general guidance on how to use the local provider you are developing in Terraform code before it is published.
-
Using
dev_overridespathFollow the official direction here. This allows you to use the provider by running
go install .. Your~/.terraformrcwould look like:provider_installation { dev_overrides { "registry.terraform.io/retailnext/spanner" = "/Users/myusername/go/bin" } # For all other providers, install them directly from their origin provider # registries as normal. If you omit this, Terraform will _only_ use # the dev_overrides block, and so no other providers will be available. direct {} }Note that once
dev_overridesis added, you cannot runterraform initifregistry.terraform.io/retailnext/spannerappears in the Terraform code — see the "release" binary method below if you need other, non-local providers alongside it.If you are using
tofu, update~/.tofurcand useregistry.opentofu.orgas the provider registry. -
Using the local "release" binary
By manually doing what
terraform initwould have done, you can use the local code. The following example shows the steps in a Linux environment with an amd64 processor.CGO_ENABLED=0 go build -trimpath -o terraform-provider-spanner_v1.0.0 -ldflags "-s -w -X main.version=1.0.0" . mkdir -p ~/.terraform.d/plugins/local.providers/local/spanner/1.0.0/linux_amd64 mv terraform-provider-spanner_v1.0.0 ~/.terraform.d/plugins/local.providers/local/spanner/1.0.0/linux_amd64 cat <<EOF > $HOME/.terraformrc provider_installation { filesystem_mirror { path = "/home/runner/.terraform.d/plugins" include = ["local.providers/*/*"] } direct { exclude = ["local.providers/*/*"] } } EOF
If you are using
tofu, use$HOME/.tofurcinstead of$HOME/.terraformrcin the example.