• Task
  • Version · 6.0
  • Create

Connect to Cloud SQL for PostgreSQL using GCP IAM authentication

Last updated: September 29, 2026

Liquibase Secure can authenticate to Cloud SQL for PostgreSQL with a Google Cloud IAM identity instead of a static password. The Cloud SQL Java Connector runs inside Liquibase, opening the secure tunnel and handling TLS for you, so you do not need to run the Cloud SQL Auth Proxy. This capability is included in the Liquibase GCP extension, which ships with Liquibase Secure.

Before you begin

Be sure you have the following:

  • Liquibase Secure 6.0 or later. The Liquibase GCP extension ships with it, so there is no separate extension to install

  • IAM database authentication enabled on your Cloud SQL instance. It is disabled by default and is an instance setting, not something Liquibase can turn on

  • Your instance connection name, in project:region:instance format. Read it with gcloud sql instances describe your_instance_id --format="value(connectionName)"

  • The Google Cloud CLI, if you authenticate from a developer machine

  • A Google Cloud project, and permission to create a service account in it and to grant that service account project-level roles. You can also connect as your own Google account instead, which needs the same roles

Procedure

1

Choose the identity Liquibase will authenticate as

Liquibase connects as whichever Google Cloud identity it finds in Application Default Credentials. That can be your own Google account, which suits local work, or a service account, which is what a pipeline should use. The rest of this article calls it your service account, so substitute your own address throughout if you are connecting as yourself.

You need your project ID for the commands that follow. Read the one your CLI is already set to, or list the projects you can see.

gcloud config get-value project

gcloud projects list

To connect as a service account, create one. Its email address is always the name you choose, followed by @your_project_id.iam.gserviceaccount.com.

Be sure to:

  • Replace your_service_account_name with a name for the service account. For example, my-service-account

  • Replace your_project_id with your Google Cloud project ID. For example, my-project

loading

Confirm the address, which is the value every later step calls your_service_account_email.

gcloud iam service-accounts list --format="value(email)"

Note: Creating a service account does not let you act as it. If you plan to run Liquibase locally under this service account, you also need roles/iam.serviceAccountTokenCreator on it, which the impersonation step below shows how to grant.

2

Grant the Cloud SQL IAM roles

The identity that connects needs two project-level roles. roles/cloudsql.client lets it open a connection through the connector, and roles/cloudsql.instanceUser lets it log in with IAM.

Be sure to:

  • Replace your_project_id with your Google Cloud project ID. For example, my-project

  • Replace your_service_account_email with the service account Liquibase authenticates as. For example, my-service-account@my-project.iam.gserviceaccount.com

Note: To connect as yourself rather than as a service account, use --member=user:your_email instead.

loading
3

Create the IAM database user

Cloud SQL maps a Google Cloud identity to a database user. On PostgreSQL the database user name is the service account email with the .gserviceaccount.com suffix removed.

Note: Shorten the name yourself. The Cloud SQL Java Connector, which Liquibase uses, does not shorten a service account email for you, so the full .gserviceaccount.com address fails to log in. Other Cloud SQL connectors do shorten it, so a connection that works elsewhere is not evidence that the full address works here.

To connect as a person instead of a service account, create the user with --type=CLOUD_IAM_USER and the full email address, such as you@example.com. A person's address is not shortened, so that same address is also the user name you log in with.

Be sure to:

  • Replace your_database_user with your service account email without the .gserviceaccount.com suffix. For example, my-service-account@my-project.iam

  • Replace your_instance_id with the name of your Cloud SQL instance. For example, my-instance

loading

Confirm the user was created before granting it privileges.

4

Grant database privileges

An IAM database user can authenticate but starts with no privileges on your database. Connect as an administrative user and grant the privileges Liquibase needs, including the right to create the tracking tables.

Note: Quote the user name. It contains an @, so PostgreSQL rejects it unquoted.

Be sure to:

  • Replace your_database with the name of your database. For example, lbcat

  • Replace your_database_user with the database user you created. For example, my-service-account@my-project.iam

loading
5

Provide Google Cloud credentials

Liquibase authenticates with Application Default Credentials (ADC). Credentials come from the environment Liquibase runs in, never from Liquibase configuration files, so there is no credential to set in your properties. Use whichever method fits where Liquibase runs.

Google Cloud CLI (local development): Sign in to create local ADC. Liquibase reuses the resulting credentials automatically, so you do not need a service account key.

gcloud auth application-default login

Workload identity (Google Cloud-hosted workloads such as Cloud Run, GKE, and Compute Engine): Attach a service account to the resource and grant it the roles you granted earlier. Authentication happens automatically with no further configuration.

Service account key file (CI/CD pipelines, containers): Set GOOGLE_APPLICATION_CREDENTIALS to the path of a service account key file.

Be sure to:

  • Replace your_key_file with the path to your service account key file. For example, /etc/liquibase/gcp-key.json

Note: ADC signs in as whoever your browser is signed into, so it is easy to end up authenticated as the wrong Google account. A later cloudsql.instances.get permission error usually means the wrong identity rather than a missing role. Confirm which identity ADC holds before you change any role.

loading
6

Configure the connection

Set the instance connection name and a password value of gcp-cloudsql-iam. The JDBC URL needs no host, because the connector dials the instance named by liquibase.cloudsql.instance rather than a host name.

Be sure to:

  • Replace your_database with the name of your database. For example, lbcat

  • Replace your_database_user with the database user you created. For example, my-service-account@my-project.iam

  • Replace your_instance_connection_name with your instance connection name, in project:region:instance format. For example, my-project:us-east1:my-instance

Note: The password value is matched exactly. Letter case and surrounding spaces are ignored, but gcp-cloudsql-iam takes no arguments after a comma, and a value such as gcp-cloudsql-iam,my-project:us-east1:my-instance does not activate IAM authentication. The instance always goes in liquibase.cloudsql.instance.

Liquibase configures TLS on the connection itself, so you do not add an SSL parameter to the JDBC URL. The connector validates the instance's server certificate on your behalf.

7

(Optional) Impersonate a service account

Complete this step only if Liquibase should connect as a service account other than the identity in your ADC. This is the usual arrangement for cross-project access, and for pipelines whose own identity holds no database permissions.

The target service account is the one that reaches the database, so it needs the Cloud SQL roles, and the database user and privileges, from the earlier steps. Your own identity needs permission to act as it.

Be sure to:

  • Replace your_target_service_account with the service account to impersonate. For example, my-target-account@target-project.iam.gserviceaccount.com

  • Replace your_email with the identity your ADC holds. For example, you@example.com

loading

Then name the target in your Liquibase configuration.

liquibase.cloudsql.targetServiceAccount: your_target_service_account

Note: An IAM change can take a few minutes to take effect. Impersonation denied immediately after you grant it is usually propagation rather than a missing role, so wait and try again before changing anything.

8

(Optional) Chain impersonation through delegates

Complete this step only if your identity cannot impersonate the target directly and has to go through one or more intermediate service accounts. Each account in the chain needs roles/iam.serviceAccountTokenCreator on the next one.

Be sure to:

  • Replace your_delegate_service_account with the intermediate service account. For example, my-delegate-account@my-project.iam.gserviceaccount.com

  • Replace your_target_service_account with the service account that reaches the database. For example, my-target-account@target-project.iam.gserviceaccount.com

liquibase.cloudsql.delegates: your_delegate_service_account
liquibase.cloudsql.targetServiceAccount: your_target_service_account

Separate multiple delegates with commas, in the order the chain is traversed.

Note: Delegates apply only alongside a target service account. When liquibase.cloudsql.delegates is set and liquibase.cloudsql.targetServiceAccount is not, Liquibase logs a warning and ignores the delegates.

9

Create a changelog file

Create a changelog file in your project directory and add a changeset. Liquibase accepts XML, SQL, YAML, and JSON.

loading

loading

loading

loading
10

Test your connection

Run status to confirm Liquibase authenticates and reaches your database. It reports which changesets are still pending, so you can verify the connection without applying your changelog.

liquibase --changelog-file=changelog.xml status

Then run update to apply the changeset.

liquibase --changelog-file=changelog.xml update

Liquibase starts the Cloud SQL connector, authenticates with your IAM identity, and creates the DATABASECHANGELOG and DATABASECHANGELOGLOCK tracking tables. The password value is masked in the log.

Alternative: use an IAM token as the password

If you cannot use the Cloud SQL Java Connector, Liquibase can instead mint a short-lived Google Cloud access token through ADC and send it as the database password. Set the password to gcp-cloudsql-iam-token and leave liquibase.cloudsql.instance unset. The connector method above stays the recommended one.

Two things the connector would otherwise handle become yours. You provide the network path to the instance, and you configure TLS yourself on the JDBC URL. Liquibase checks the URL's SSL settings before it sends the token and refuses to send one over a connection that permits an unencrypted downgrade.

Set sslmode to require, verify-ca, or verify-full. The parameter name is case-sensitive, so SSLMODE counts as absent and is rejected. Declaring the same SSL parameter twice is also rejected, even when the last value is the secure one.

loading

The Cloud SQL Auth Proxy is the usual way to provide the network path. When Liquibase connects to a loopback address it skips the TLS check, because the token does not leave the machine.

Note: Run the proxy without --auto-iam-authn. With that flag the proxy performs IAM authentication itself, which makes the token redundant.

The earlier steps still apply. The roles, the database user, and the privileges are the same, and credentials still come from ADC.

Note: This method requires a Liquibase Secure license. Without one the command fails rather than falling back to another form of authentication.

Troubleshooting

liquibase.cloudsql.instance is required when password is gcp-cloudsql-iam

The password activated IAM authentication but no instance was configured. Set liquibase.cloudsql.instance to your instance connection name, in project:region:instance format.

An ordinary connection error, as though the password were never recognized

IAM authentication engages only for a jdbc:postgresql: or jdbc:mysql: URL. With any other scheme Liquibase does not activate it at all and simply tries an ordinary connection, so the error you see comes from the driver rather than from IAM authentication. This capability covers Cloud SQL for PostgreSQL and Cloud SQL for MySQL. A jdbc:mariadb URL does not work as a substitute for jdbc:mysql.

JDBC URL scheme does not match Cloud SQL instance

Before connecting, Liquibase asks the Cloud SQL Admin API what engine the instance runs, and the answer disagrees with your URL. The message names both. Point liquibase.cloudsql.instance at the right instance, or correct the URL scheme. The same check reports a Cloud SQL for SQL Server instance by name, because SQL Server is not supported.

Cloud SQL connection failed

The message names what to check: that liquibase.cloudsql.instance is correct, and that the authenticating identity holds roles/cloudsql.client and roles/cloudsql.instanceUser. Liquibase deliberately does not repeat the underlying error, so that credentials cannot reach the log.

When the roles are right, the usual cause is that ADC belongs to a different Google account than you expect. Confirm the identity before changing roles.

Service account impersonation failed

The identity in your ADC cannot act as the target service account. Grant it roles/iam.serviceAccountTokenCreator on that account, and allow a few minutes for the change to take effect. The target service account is masked in this message.

permission denied for schema public

Authentication succeeded and authorization did not. The database user exists but has no privileges, or the grants went to a different name. Confirm the grants from granting the database privileges were applied to the shortened user name, quoted.

password authentication failed

The user name is probably the full service account email. On PostgreSQL, remove the .gserviceaccount.com suffix.

The password value is ignored

Liquibase reads the trigger from liquibase.command.password. A command-specific key such as liquibase.command.update.password resolves first and never reaches IAM authentication. Set the general key instead.

Connection times out

Do not point the JDBC URL at the instance's IP address. The connector reaches the instance through the connection name, so the URL needs no host at all.