Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions .claude/skills/upgrade-v1-to-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Upgrade postgresql module from v1 to v2

Upgrade Terraform configurations that use the `entur/terraform-google-sql-db` postgresql module
from v1.x to v2.x.

## What changed in v2

### Breaking changes

| Variable | v1 behaviour | v2 behaviour |
|----------|-------------|--------------|
| `database_version` | Default: `"POSTGRES_14"` | **Required** - no default |
| `enable_basic_auth` | Did not exist | **Required** - no default |
| `enable_iam_auth` | Did not exist | **Required** - no default |
| `create_kubernetes_resources` | Optional (`true` by default) | **Removed** |
| `additional_users[*].create_kubernetes_secret` | Optional field | **Removed** |
| Non-prod default machine size | `db-f1-micro` | `db-custom-1-3840` |

### Removed resources

The module no longer creates these resources. Terraform will plan to destroy them on upgrade:

- `kubernetes_config_map.<name>-psql-connection`
- `kubernetes_secret.<name>-psql-credentials`
- `kubernetes_secret.<name>-<user>-psql-credentials` (per additional user)
- `google_secret_manager_secret.db_secret["HOST"]`
- `google_secret_manager_secret.db_secret["PORT"]`
- `google_secret_manager_secret_version.db_secret_version_main_database_credentials["HOST"]`
- `google_secret_manager_secret_version.db_secret_version_main_database_credentials["PORT"]`

### New optional variables

| Variable | Description | Default |
|----------|-------------|---------|
| `enable_pgaudit` | Enable pgaudit extension (restarts instance) | `false` |
| `iam_auth_default_application_user` | IAM auth for the app service account | enabled when `enable_iam_auth = true` |
| `iam_auth_additional_service_account_users` | Additional service account users | `{}` |
| `iam_auth_users` | Individual IAM users | `{}` |
| `iam_auth_groups` | IAM groups | `{}` |

### Resource moves

The upgrade includes `moved` blocks so these resources are renamed rather than recreated:

- `random_password.password` → `random_password.password[0]`
- `google_sql_user.main` → `google_sql_user.main[0]`

## Steps

### 1. Find module configurations

Search the working directory for files that reference this module:

```
grep -rl 'terraform-google-sql-db//modules/postgresql' .
```

Read each file found.

### 2. Update each file

For each Terraform file that references the postgresql module, apply these changes:

**a. Update the source ref**

Change the `?ref=` tag from `v1.x.x` to the latest v2 release. Check
https://github.com/entur/terraform-google-sql-db/releases for the current latest v2.x.x tag.

**b. Add `enable_basic_auth`**

Add this argument to preserve v1 behaviour (v1 always created a basic auth user):

```hcl
enable_basic_auth = true
```

If the user wants to disable basic auth and use only IAM auth, set `enable_basic_auth = false`
instead, but warn them that this will destroy the SQL user, its password, and the corresponding
Secret Manager secrets.

**c. Add `enable_iam_auth` and disable the default application user**

Enable IAM auth, but keep the default application user opt-in. Without the override the module
would automatically add the application service account as a database user, which teams should
choose to do deliberately.

```hcl
enable_iam_auth = true
iam_auth_default_application_user = { enabled = false }
```

`enable_iam_auth = true` adds the `cloudsql.iam_authentication` database flag and makes the
instance ready to accept IAM users and groups.

When a team is ready to grant the application service account database access, they can remove
the override (or set `enabled = true`) to add it.

> **Note:** Any new IAM user added via `iam_auth_default_application_user`,
> `iam_auth_additional_service_account_users`, `iam_auth_users`, or `iam_auth_groups` can connect
> to the instance, but will not have access to existing schemas, tables, sequences, or other
> objects in the database. A database administrator must run `GRANT` statements for each new user
> before the application can read or write data.

**d. Ensure `database_version` is explicit**

If `database_version` is not set, add it. v1 defaulted to `"POSTGRES_14"`. Ask the user which
PostgreSQL version their instance is running if they are unsure, or confirm `POSTGRES_14` is
correct before using it as a safe default.

```hcl
database_version = "POSTGRES_14" # confirm this matches the running instance
```

**e. Remove `create_kubernetes_resources`**

If present, remove the `create_kubernetes_resources` argument. The module no longer supports
Kubernetes resource creation.

**f. Remove `create_kubernetes_secret` from `additional_users`**

In v1, each entry in `additional_users` could have a `create_kubernetes_secret` field:

```hcl
# v1
additional_users = {
reporting = {
username = "reporting"
create_kubernetes_secret = true
}
}
```

Remove the `create_kubernetes_secret` field from each entry. Only `username` is accepted in v2:

```hcl
# v2
additional_users = {
reporting = {
username = "reporting"
}
}
```

**g. Warn about machine size changes in dev/tst**

Check whether `machine_size` is set. If it is not set on a `dev` or `tst` instance, the module is
currently using the v1 default of `db-f1-micro`. v2 changes that default to `db-custom-1-3840`.
Warn the user that changing an instance's machine type causes Cloud SQL to **restart the
instance**, so applying this upgrade will cause brief downtime on that instance. This applies
whether they accept the new default or pin `machine_size` to a different tier - any machine size
change triggers a restart.

### 3. Warn about removed resources

After making the code changes, tell the user what Terraform will destroy on next apply and what
they need to do before applying:

> **Before applying, check the following:**
>
> - **Keep the Kubernetes provider during the upgrade**: Terraform must destroy the old
> Kubernetes resources as part of the upgrade, but v2 of the module no longer declares the
> Kubernetes provider. Without it in the root module, `terraform plan` will error. Add the
> provider to the root module's `required_providers` (or `provider` block) before running
> plan, and remove it again after the apply succeeds.
>
> ```hcl
> terraform {
> required_providers {
> kubernetes = {
> source = "hashicorp/kubernetes"
> version = "~> 2.0"
> }
> }
> }
> ```
>
> - **Kubernetes ConfigMap / Secrets**: The Kubernetes ConfigMap and Secrets previously created
> by the module will be destroyed. If anything still reads them, migrate to the common Helm
> chart v2 (see step 4) or read from Secret Manager directly.
>
> - **`PGHOST` and `PGPORT` secrets**: The module no longer writes host or port to Secret
> Manager. The common Helm chart v2 provides these as environment variables automatically
> (`localhost` and `5432`). If the application reads `PGHOST` or `PGPORT` from Secret Manager
> directly (outside of the Helm chart), update it to use hardcoded values instead.
>
> - **Machine size restart (dev/tst)**: If `machine_size` was not set on a `dev` or `tst`
> instance, it will move from the old default (`db-f1-micro`) to the new one
> (`db-custom-1-3840`). Cloud SQL restarts the instance to apply any machine type change, so
> expect brief downtime on `terraform apply`.
>
> Run `terraform plan` to see the full list of resources that will be created, moved, or
> destroyed. The `google_sql_user.main` and `random_password.password` resources will appear
> as "moved", not recreated - the database user and password are preserved.

### 4. Recommend the common Helm chart v2

The [common Helm chart v2](https://github.com/entur/helm-charts) is designed to work with this
Terraform module version. It reads `{prefix}INSTANCES`, `{prefix}USER`, and `{prefix}PASSWORD`
directly from Secret Manager via External Secrets - no Terraform-created Kubernetes secrets
needed. It also generates `{prefix}HOST=localhost` and `{prefix}PORT=5432` as environment
variables itself, and supports IAM authentication via Cloud SQL Auth Proxy v2 with Workload
Identity.

Tell the user to upgrade the Helm chart alongside the Terraform module, and to follow the
upgrade skill from the `entur/helm-charts` repository:

```
Read and follow the upgrade skill at:
https://raw.githubusercontent.com/entur/helm-charts/main/.claude/skills/upgrade-common-chart/SKILL.md
```

### 5. Summary

After making all changes, report:

- Which Terraform files were updated and which variables were added or removed
- Whether Kubernetes resources were in use (and whether the Helm chart v2 was recommended)
- Whether any applications read `PGHOST`/`PGPORT` from Secret Manager directly (and need updating)
- The next steps: `terraform plan`, then `helm dependency update` and `helm lint`
4 changes: 0 additions & 4 deletions .github/workflows/pr-int-test-terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ jobs:
environment: dev
gcp_workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
gcp_service_account: ${{ vars.SERVICE_ACCOUNT }}
- name: Authenticate with GKE
uses: entur/gha-meta/.github/actions/k8s-auth@v1
with:
environment: dev
- name: Run recursive Go integration tests
working-directory: test/integration
run: go test -tags=integration ./... -timeout 30m
67 changes: 62 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ A PostgreSQL module that uses the [init module](https://github.com/entur/terrafo
```terraform
module "postgresql" {
source = "github.com/entur/terraform-google-sql-db//modules/postgresql?ref=v1.7.4"
database_version = "POSTGRES_18" # Use the latest postgres version
...
}
```
Expand All @@ -39,16 +40,15 @@ If a desired machine size and/or availability type is not explicitly set, defaul

| Environment | Type | CPU | Memory | Highly available |
| -------------- | -------------- | --- | ------- | ---------------- |
| non-production | Shared vCPU | <1 | 600 MB | No |
| non-production | Dedicated vCPU | 1 | 3840 MB | No |
| production | Dedicated vCPU | 1 | 3840 MB | Yes |


### Edition

Changing this will cause a database restart on existing instances. Choosing **Enterprise Plus** (`ENTERPRISE_PLUS`) over **Enterprise** (`ENTERPRISE`) can also increase costs. Carefully evaluate your requirements before choosing this edition.

Ensure you select the appropriate tier for your use case. For more details about instance editions, refer to the [official documentation](https://cloud.google.com/sql/docs/postgres/instance-settings).


### Sizing

To specify the size of a database instance, supply the `cpu` and `memory` attributes in `var.machine_size` (recommended):
Expand All @@ -74,6 +74,65 @@ module "postgresql" {
}
```

## IAM authentication

IAM authentication lets Google identities (service accounts, users, and groups) log in to Cloud SQL
using their Google credentials instead of a password.

Prefer `iam_auth_groups` over `iam_auth_users` for human access. Group membership is managed in
the identity provider, so access is revoked automatically when someone leaves the team - no
Terraform change required.

### Adding a team group

To allow all members of a team to connect to the database, add the team's Google group using
`iam_auth_groups`. The group email follows the pattern `sg-dig-team-<teamname>@entur.no`.

```terraform
module "postgresql" {
...
enable_iam_auth = true

iam_auth_groups = {
team = {
email = "sg-dig-team-<teamname>@entur.no"
}
}
}
```

The default `roles` value is `["cloudsqlsuperuser"]`, which allows the group to connect to the
instance. It does not grant access to existing schemas, tables, or sequences.

### Granting access to database objects

After applying the Terraform configuration, a existing database administrator user must run `GRANT` statements
to give the group access to existing objects. Connect to the database as a superuser and run:

```sql
-- Allow the group to use the schema
GRANT USAGE ON SCHEMA public TO "sg-dig-team-<teamname>@entur.no";

-- Grant access to existing tables and sequences
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "sg-dig-team-<teamname>@entur.no";
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO "sg-dig-team-<teamname>@entur.no";

-- Grant access to tables and sequences created in the future
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "sg-dig-team-<teamname>@entur.no";
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO "sg-dig-team-<teamname>@entur.no";
```

Adjust the privileges (`SELECT`, `INSERT`, `UPDATE`, `DELETE`) to match the level of access the
team should have. A team that only needs read access should receive `SELECT` only.

### Connecting from a local machine

Use Cloud SQL Auth Proxy with Application Default Credentials to connect from a local machine.
See the official guide:
[Connect using Cloud SQL Auth Proxy with IAM authentication](https://docs.cloud.google.com/sql/docs/postgres/iam-logins#cloud-sql-auth-proxy)

### Integration Tests

Run local integration tests in test/integration folder.
Expand All @@ -82,8 +141,6 @@ Run local integration tests in test/integration folder.
> Only Team-Plattform has rights to do this locally.
> Contributors can create a PR which will run the tests as well.

Make sure you are connected to the dev kubernetes cluster in GKE (kub-ent-dev-001)

```bash
cd test/integration
go test -v -tags=integration -timeout 30m ./...
Expand Down
Loading
Loading