6
votes

I have a module which contains resources for:

  • azure postgres server
  • azure postgres database
  • postgres role (user)
  • postgres provider (for the server and used to create the role)

In one of my env directories I can have 0-N .tf files which is an instance of that module and each specify database name etc. So if I add another .tf file with a new name then a new database server with a database will be provisioned. All this works fine.

However, if I now delete an existing database module (one of the .tf files in my env directory) I run into issues. Terraform will now try to get the state of all the previously existing resources and since that specific provider (for that postgres server) now is gone terraform cannot get the state of the created postgres role, with the output a provider configuration block is required for all operations.

I understand why this happens but I cannot figure out how to solve this. I want to "dynamically" create (and remove) postgres servers with a database on them but this requires "dynamic" providers which then makes me get stuck on this.

Example of how it looks

resource "azurerm_postgresql_server" "postgresserver" {
  name                = "${var.db_name}-server"
  location            = "${var.location}"
  resource_group_name = "${var.resource_group}"

  sku = ["${var.vmSize}"]

  storage_profile = ["${var.storage}"]

  administrator_login = "psqladminun"
  administrator_login_password = "${random_string.db-password.result}"
  version = "${var.postgres_version}"
  ssl_enforcement = "Disabled"
}

provider "postgresql" {
  version         = "0.1.0"
  host            = "${azurerm_postgresql_server.postgresserver.fqdn}"
  port            = 5432
  database        = "postgres"
  username        = "${azurerm_postgresql_server.postgresserver.administrator_login}@${azurerm_postgresql_server.postgresserver.name}". 
  password        = "${azurerm_postgresql_server.postgresserver.administrator_login_password}"
 }

resource "azurerm_postgresql_database" "db" {
  name                = "${var.db_name}"
  resource_group_name = "${var.resource_group}"
  server_name         = "${azurerm_postgresql_server.postgresserver.name}"
  charset             = "UTF8"
  collation           = "English_United States.1252"
}

resource "postgresql_role" "role" {
  name             = "${random_string.user.result}"
  login            = true
  connection_limit = 100
  password         = "${random_string.pass.result}"
  create_role      = true
  create_database     = true

  depends_on = ["azurerm_postgresql_database.db"]
}

Above you see how we, in the module create a postgres server, postgres db and also a postgres role (where only the role utilizes the postgres provider). So if I now define an instance datadb.tf such as:

module "datadb" {
  source = "../../modules/postgres"
  db_name   = "datadb"
  resource_group = "${azurerm_resource_group.resource-group.name}"
  location = "${azurerm_resource_group.resource-group.location}"
}

then it will be provisioned successfully. The issue is if I later on delete that same file (datadb.tf) then the planning fails because it will try to get the state of the postgres role without having the postgres provider present. The postgres provider is only needed for the postgres role which will be destroyed as soon as the azure provider destroys the postgres db and postgres server, so the actual removal of that role is not necessary. Is there a way to tell terraform that "if this resource should be removed, you don't have to do anything because it will be removed dependent on being removed"? Or does anyone see any other solutions?

I hope my goal and issue is clear, thanks!

1
You might need to give more information here (and maybe a minimal reproducible example) because this doesn't match the behaviour that I see in that destroys should be fine because Terraform will work back through the dependency chain, removing the Postgres parts before removing the database server (assuming you are setting the postgres provider connection details to the interpolated output from the database server resource). On the other hand there is a known issue where the current Postgres provider chokes on the first plan/apply if the database instance doesn't already exist because it can't feature detect.ydaetskcoR
Hey! Thanks for that, I added example of how the module looks (only the necessary resources for this issue) and also how a file where we define an instance of that module looks. So the issue is adding that file, the applying it and provisioning it. And then later on, removing the file (with the desire for everything to be removed) causes issues because of terraform not able to getting the state of the postgres role because of no provider present.poppe
What version of the Postgres provider are you using?ydaetskcoR
So I think the issue is that the provider is not a part of the dependancy chain so it does just like you say, starts by removing the postgres role but at that point there's no provider present.poppe
"0.1.0", like it says in the snippetpoppe

1 Answers

1
votes

I think the only solution is a two-step solution, but I think it's still clean enough. What I would do is have two files per database (name them how you want).

db-1-infra.tf
db-1-pgsql.tf

Put everything except your postgres resources in db-1-infra.tf

resource "azurerm_postgresql_server" "postgresserver" {
  name                = "${var.db_name}-server"
  location            = "${var.location}"
  resource_group_name = "${var.resource_group}"

  sku = ["${var.vmSize}"]

  storage_profile = ["${var.storage}"]

  administrator_login = "psqladminun"
  administrator_login_password = "${random_string.db-password.result}"
  version = "${var.postgres_version}"
  ssl_enforcement = "Disabled"
}

provider "postgresql" {
  version         = "0.1.0"
  host            = "${azurerm_postgresql_server.postgresserver.fqdn}"
  port            = 5432
  database        = "postgres"
  username        = "${azurerm_postgresql_server.postgresserver.administrator_login}@${azurerm_postgresql_server.postgresserver.name}". 
  password        = "${azurerm_postgresql_server.postgresserver.administrator_login_password}"
 }

resource "azurerm_postgresql_database" "db" {
  name                = "${var.db_name}"
  resource_group_name = "${var.resource_group}"
  server_name         = "${azurerm_postgresql_server.postgresserver.name}"
  charset             = "UTF8"
  collation           = "English_United States.1252"
}

Put your PostgreSQL resources in db-1-pgsql.tf

resource "postgresql_role" "role" {
    name             = "${random_string.user.result}"
    login            = true
    connection_limit = 100
    password         = "${random_string.pass.result}"
    create_role      = true
    create_database     = true

    depends_on = ["azurerm_postgresql_database.db"]

}

When you want to get rid of your database, first delete the file db-1-pgsql.tf and apply. Next, delete db-1-infra.tf and apply again.

The first step will destroy all postgres resources and free you up to run the second step, which will remove the postgres provider for that database.