> For the complete documentation index, see [llms.txt](https://docs.ionos.com/cloud/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ionos.com/cloud/terraform/guides/migrating_pgsql_v1_to_v2.md).

# Migrating PostgreSQL clusters from ionoscloud\_pg\_cluster to ionoscloud\_pg\_cluster\_v2

`ionoscloud_pg_cluster_v2` is a re-implementation of the PostgreSQL cluster resource on the newer plugin framework, backed by the v2 DBaaS PostgreSQL API. It is a **different resource type** from the legacy `ionoscloud_pg_cluster`, with a different schema (single nested blocks instead of lists, a required `location`, write-only credentials, etc.).

Because the two are different resource types served by different plugin frameworks, a plain `moved {}` block **does not** work between them. Instead, the recommended migration adopts each existing cluster into the new resource and drops the old one from state — **without touching the running cluster**. No data is moved or recreated; only Terraform state changes.

> **Important:** This migration assumes `ionoscloud_pg_cluster` and `ionoscloud_pg_cluster_v2` manage the *same* underlying cluster (same UUID). It changes Terraform state only and never mutates, recreates, or deletes the live database.

## Prerequisites

* Terraform **1.14+** (for the `terraform query` list/config-generation workflow used below). Terraform **1.7+** is enough if you choose to write the `import` blocks by hand.
* A backup of your state file (`terraform state pull > state.backup.json`).

## Recommended approach: query-generate + `removed`

The `ionoscloud_pg_cluster_v2` **list resource** can enumerate your existing clusters and generate complete `v2` configuration for each one, so you don't have to hand-write the new resource blocks or look up cluster UUIDs.

### 1. Discover clusters and generate v2 configuration

Create a query file, for example `migrate.tfquery.hcl`:

```hcl
list "ionoscloud_pg_cluster_v2" "all" {
  provider         = ionoscloud
  include_resource = true
  # Optional: scope discovery to a single region.
  # config {
  #   filters = [
  #     { field_name = "location", field_value = "de/txl" },
  #   ]
  # }
}
```

Then generate configuration and import blocks for the discovered clusters:

```sh
terraform query -generate-config-out=generated_pg_v2.tf
```

`generated_pg_v2.tf` will contain a fully-populated `ionoscloud_pg_cluster_v2` block for each cluster (name, version, instances, connections, maintenance window, backup, logs/metrics, …), together with the `import` wiring keyed by the v2 identity (`<location>:<cluster_id>`).

### 2. Drop the legacy resources from state

For each legacy `ionoscloud_pg_cluster` you are migrating, add a `removed` block so Terraform forgets it **without destroying** the live cluster:

```hcl
removed {
  from = ionoscloud_pg_cluster.example
  lifecycle {
    destroy = false
  }
}
```

Delete the corresponding `resource "ionoscloud_pg_cluster" "example" { … }` configuration.

### 3. Fill in the values that cannot be generated

A few attributes are not part of the cluster's readable state and must be added by hand to the generated `ionoscloud_pg_cluster_v2` blocks. Fill these in yourself before applying:

| Attribute                      | Why it's missing                            | What to do                                                               |
| ------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------ |
| `credentials.password`         | Write-only; the API never returns it.       | Set it (reuse the same value, e.g. via a variable or `random_password`). |
| `credentials.password_version` | Provider-side marker with no v1 equivalent. | Set an initial value such as `"v1"`.                                     |
| `credentials.database`         | Write-only; the API never returns it.       | Set it to any existing database on the cluster (see below).              |
| `credentials.username`         | Write-only; the API never returns it.       | Set it to an existing or new user name on the cluster (see below).       |
| `timeouts`                     | Optional; not generated.                    | Add only if you previously customized timeouts.                          |

`restore_from_backup` is create-time-only and not part of a cluster's readable state, so config generation never emits it and you do not need it when adopting an existing cluster. Leave it out (it is only relevant when initializing a brand-new cluster from a backup).

You can set `credentials.database` to any database you are already using on the cluster, not just the one originally created with it. Applying the `credentials` block never destroys existing data; it only creates what's missing and, if needed, resets the password:

* If the database doesn't exist yet: it gets created on the existing cluster.
* If the user doesn't exist yet: the user gets created with the password you provided.
* If the user already exists: the user's password gets set to the one you provided (and if it's already the same password, nothing changes).

### 4. Apply

```sh
terraform plan    # confirm: imports the v2 resources, removes v1 from state, NO destroys/replaces
terraform apply
```

The plan should show the clusters being **imported** into `ionoscloud_pg_cluster_v2`, the `ionoscloud_pg_cluster` resources being **removed from state**, and **no** create/destroy/replace actions against the live clusters. If you see a forced replacement, stop and reconcile the offending attribute before applying.

## Manual alternative (no config generation)

If you prefer not to use the query workflow, you can do the same thing with explicit blocks. For each cluster, write the `ionoscloud_pg_cluster_v2` configuration yourself and pair it with:

```hcl
import {
  to = ionoscloud_pg_cluster_v2.example
  id = "de/txl:00000000-0000-0000-0000-000000000000" # "<location>:<cluster_id>"
}

removed {
  from = ionoscloud_pg_cluster.example
  lifecycle {
    destroy = false
  }
}
```

Then `terraform plan` / `terraform apply` as above. You can find the cluster UUID in the legacy resource's state (`terraform state show ionoscloud_pg_cluster.example`).

## Rollback

Because the live cluster is never touched, rollback is just reverting your configuration changes and restoring the pre-migration state file if you already applied:

```sh
terraform state push state.backup.json
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ionos.com/cloud/terraform/guides/migrating_pgsql_v1_to_v2.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
