hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.6k stars 4.64k forks source link

`postgresql_flexible_server_virtual_endpoint_resource` incorrectly sets Replica Server ID #27453

Closed bruceharrison1984 closed 2 weeks ago

bruceharrison1984 commented 1 month ago

Is there an existing issue for this?

Community Note

Terraform Version

1.9.0

AzureRM Provider Version

4.3.0

Affected Resource(s)/Data Source(s)

postgresql_flexible_server_virtual_endpoint_resource

Terraform Configuration Files

resource "random_pet" "name_prefix" {
  length = 1
}

###### EAST RG ######

resource "azurerm_resource_group" "east" {
  name     = "${random_pet.name_prefix.id}-east"
  location = "eastus"
}

resource "azurerm_virtual_network" "east" {
  name                = "${random_pet.name_prefix.id}-east-vn"
  location            = azurerm_resource_group.east.location
  resource_group_name = azurerm_resource_group.east.name
  address_space       = ["10.0.0.0/16"]
}

resource "azurerm_network_security_group" "east" {
  name                = "${random_pet.name_prefix.id}-east-nsg"
  location            = azurerm_resource_group.east.location
  resource_group_name = azurerm_resource_group.east.name

  security_rule {
    name                       = "allow_all"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "azurerm_subnet" "east" {
  name                 = "${random_pet.name_prefix.id}-east-sn"
  resource_group_name  = azurerm_resource_group.east.name
  virtual_network_name = azurerm_virtual_network.east.name
  address_prefixes     = ["10.0.1.0/24"]
  service_endpoints    = ["Microsoft.Storage"]

  delegation {
    name = "fs"
    service_delegation {
      name = "Microsoft.DBforPostgreSQL/flexibleServers"
      actions = [
        "Microsoft.Network/virtualNetworks/subnets/join/action",
      ]
    }
  }
}

resource "azurerm_subnet_network_security_group_association" "east" {
  subnet_id                 = azurerm_subnet.east.id
  network_security_group_id = azurerm_network_security_group.east.id
}

resource "azurerm_private_dns_zone" "east" {
  name                = "${random_pet.name_prefix.id}-pdz.postgres.database.azure.com"
  resource_group_name = azurerm_resource_group.east.name

  depends_on = [azurerm_subnet_network_security_group_association.east]
}

resource "azurerm_virtual_network_peering" "east" {
  name                         = "east-to-west"
  resource_group_name          = azurerm_resource_group.east.name
  virtual_network_name         = azurerm_virtual_network.east.name
  remote_virtual_network_id    = azurerm_virtual_network.west.id
  allow_virtual_network_access = true
  allow_forwarded_traffic      = true
}

resource "azurerm_private_dns_zone_virtual_network_link" "east" {
  name                  = "${random_pet.name_prefix.id}-east-pdzvnetlink.com"
  private_dns_zone_name = azurerm_private_dns_zone.east.name
  virtual_network_id    = azurerm_virtual_network.east.id
  resource_group_name   = azurerm_resource_group.east.name

  depends_on = [azurerm_virtual_network_peering.east]
}

resource "random_password" "pass" {
  length = 20
}

resource "azurerm_postgresql_flexible_server" "east" {
  name                          = "${random_pet.name_prefix.id}-east-pg"
  resource_group_name           = azurerm_resource_group.east.name
  location                      = azurerm_resource_group.east.location
  version                       = "13"
  public_network_access_enabled = false
  administrator_login           = "adminTerraform"
  administrator_password        = random_password.pass.result
  sku_name                      = "GP_Standard_D2s_v3"

  delegated_subnet_id = azurerm_subnet.east.id
  private_dns_zone_id = azurerm_private_dns_zone.east.id

  depends_on = [azurerm_private_dns_zone_virtual_network_link.east]

  lifecycle {
    ignore_changes = [zone]
  }

  timeouts {
    create = "120m"
  }
}

resource "azurerm_postgresql_flexible_server_virtual_endpoint" "example" {
  name              = "${random_pet.name_prefix.id}-endpoint"
  source_server_id  = azurerm_postgresql_flexible_server.east.id
  replica_server_id = azurerm_postgresql_flexible_server.west.id
  type              = "ReadWrite"
}

###### WEST RG ######

resource "azurerm_resource_group" "west" {
  name     = "${random_pet.name_prefix.id}-west"
  location = "westus"
}

resource "azurerm_virtual_network" "west" {
  name                = "${random_pet.name_prefix.id}-west-vn"
  location            = azurerm_resource_group.west.location
  resource_group_name = azurerm_resource_group.west.name
  address_space       = ["11.0.0.0/16"]
}

resource "azurerm_network_security_group" "west" {
  name                = "${random_pet.name_prefix.id}-west-nsg"
  location            = azurerm_resource_group.west.location
  resource_group_name = azurerm_resource_group.west.name

  security_rule {
    name                       = "allow_all"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "azurerm_subnet" "west" {
  name                 = "${random_pet.name_prefix.id}-west-sn"
  resource_group_name  = azurerm_resource_group.west.name
  virtual_network_name = azurerm_virtual_network.west.name
  address_prefixes     = ["11.0.1.0/24"]
  service_endpoints    = ["Microsoft.Storage"]

  delegation {
    name = "fs"
    service_delegation {
      name = "Microsoft.DBforPostgreSQL/flexibleServers"
      actions = [
        "Microsoft.Network/virtualNetworks/subnets/join/action",
      ]
    }
  }
}

resource "azurerm_subnet_network_security_group_association" "west" {
  subnet_id                 = azurerm_subnet.west.id
  network_security_group_id = azurerm_network_security_group.west.id
}

resource "azurerm_private_dns_zone" "west" {
  name                = "${random_pet.name_prefix.id}-pdz.postgres.database.azure.com"
  resource_group_name = azurerm_resource_group.west.name

  depends_on = [azurerm_subnet_network_security_group_association.west]
}

resource "azurerm_virtual_network_peering" "west" {
  name                         = "west-to-east"
  resource_group_name          = azurerm_resource_group.west.name
  virtual_network_name         = azurerm_virtual_network.west.name
  remote_virtual_network_id    = azurerm_virtual_network.east.id
  allow_virtual_network_access = true
  allow_forwarded_traffic      = true
}

resource "azurerm_private_dns_zone_virtual_network_link" "west" {
  name                  = "${random_pet.name_prefix.id}-west-pdzvnetlink.com"
  private_dns_zone_name = azurerm_private_dns_zone.west.name
  virtual_network_id    = azurerm_virtual_network.west.id
  resource_group_name   = azurerm_resource_group.west.name

  depends_on = [azurerm_virtual_network_peering.west]
}

resource "azurerm_postgresql_flexible_server" "west" {
  name                          = "${random_pet.name_prefix.id}-west-pg"
  resource_group_name           = azurerm_resource_group.west.name
  location                      = azurerm_resource_group.west.location
  create_mode                   = "Replica"
  source_server_id              = azurerm_postgresql_flexible_server.east.id
  version                       = azurerm_postgresql_flexible_server.east.version
  public_network_access_enabled = azurerm_postgresql_flexible_server.east.public_network_access_enabled
  sku_name                      = azurerm_postgresql_flexible_server.east.sku_name

  delegated_subnet_id = azurerm_subnet.west.id
  private_dns_zone_id = azurerm_private_dns_zone.west.id

  depends_on = [azurerm_private_dns_zone_virtual_network_link.west]

  lifecycle {
    ignore_changes = [zone]
  }

  timeouts {
    create = "120m"
  }
}

Debug Output/Panic Output

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # azurerm_postgresql_flexible_server_virtual_endpoint.example will be updated in-place
  ~ resource "azurerm_postgresql_flexible_server_virtual_endpoint" "example" {
        id                = "/subscriptions/8ffedf3e-82d2-49f5-9603-8b318198e483/resourceGroups/cardinal-east/providers/Microsoft.DBforPostgreSQL/flexibleServers/cardinal-east-pg/virtualEndpoints/cardinal-endpoint"
        name              = "cardinal-endpoint"
      ~ replica_server_id = "/subscriptions/8ffedf3e-82d2-49f5-9603-8b318198e483/resourceGroups/cardinal-east/providers/Microsoft.DBforPostgreSQL/flexibleServers/cardinal-west-pg" -> "/subscriptions/8ffedf3e-82d2-49f5-9603-8b318198e483/resourceGroups/cardinal-west/providers/Microsoft.DBforPostgreSQL/flexibleServers/cardinal-west-pg"
        # (2 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Expected Behaviour

No changes should be detected after initial creation

Actual Behaviour

Resources deploy correctly, however the Replica Server ID changes on the endpoint on second run due to the way it is derived from the Source Server within the provider.

The Virtual Endpoint presumes the replica server is in the same RG as the source server, which is not always the case.

Steps to Reproduce

Deploy resources, then immediately deploy them again.

Important Factoids

No response

References

https://github.com/hashicorp/terraform-provider-azurerm/blob/22b75edb3669a1d0fb48d0108fde66a49c73eed8/internal/services/postgres/postgresql_flexible_server_virtual_endpoint_resource.go#L162-L164

neil-yechenwei commented 1 month ago

Thanks raising this issue. Seems I didn't reproduce this issue with above tf config. Does this issue happen when they're in the same resource group?

bruceharrison1984 commented 1 month ago

It does not, it only occurs when they are in separate RGs. I had planned on putting in a more correct example on Monday.

I hadn't expected anyone to take a look at this before then 😬

leonrob commented 1 month ago

@bruceharrison1984 is the real MVP