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.51k stars 4.6k forks source link

Azure Elastic SAN volumes fail in Terraform: `provisioningState` or `status` to be returned from the LRO API but both were empty #26604

Open darrens280 opened 1 month ago

darrens280 commented 1 month ago

Is there an existing issue for this?

Community Note

Terraform Version

1.9.0

AzureRM Provider Version

3.111.0

Affected Resource(s)/Data Source(s)

azurerm_elastic_san_volume

Terraform Configuration Files

###########################################################
# dev.tfvars

elastic_sans = {
  "1" = {
    base_storage_in_tb = 1
    volume_config = {
      "ABC" = [
        {
          "function"   = "DATA"
          "size_in_gb" = 10
        },
        {
          "function"   = "LOGS"
          "size_in_gb" = 20
        },
        {
          "function"   = "TEMPDB"
          "size_in_gb" = 30
        }
      ],
      "DEF" = [
        {
          "function"   = "DATA"
          "size_in_gb" = 40
        },
        {
          "function"   = "LOGS"
          "size_in_gb" = 50
        },
        {
          "function"   = "TEMPDB"
          "size_in_gb" = 60
        }
      ]
    }
  }
}

###########################################################
# locals.tf

locals {
  # Create a flattened list of objects (flattened_volumes) from the nested structure. We then convert this flattened list into a map where the key is a combination of the group key and index. The `azurerm_elastic_san_volume` resource iterates over this map.
  flattened_volumes = flatten([
    for key, disks in var.volume_config :
    [for idx, disk in disks : {
      key        = key
      idx        = idx
      function   = disk.function
      size_in_gb = disk.size_in_gb
    }]
  ])
}

###########################################################
#main.tf

# Create Azure Elastic SAN
resource "azurerm_elastic_san" "this" {
  name                 = var.name
  resource_group_name  = var.resource_group_name
  location             = var.location
  base_size_in_tib     = var.base_size_in_tib
  extended_size_in_tib = var.extended_size_in_tib
  zones                = var.zones

  sku {
    name = "Premium_LRS"
  }

  tags = var.tags

}

# Create the eSAN Volume Groups
resource "azurerm_elastic_san_volume_group" "this" {

  for_each = var.volume_config

  name            = lower("esanvg-${each.key}1-${local.location}-${var.environment}")
  elastic_san_id  = azurerm_elastic_san.this.id
  protocol_type   = "Iscsi"
  encryption_type = "EncryptionAtRestWithPlatformKey"

  identity {
    type = "SystemAssigned"
  }

}

resource "azurerm_private_endpoint" "this" {

  for_each = var.volume_config

  name                = lower("pep-esanvg-${each.key}1-${local.location}-${var.environment}")
  location            = var.location
  resource_group_name = var.resource_group_name
  subnet_id           = var.subnet_id

  private_service_connection {
    name                           = lower("pep-${each.key}1-${local.location}-${var.environment}")
    private_connection_resource_id = azurerm_elastic_san.this.id
    subresource_names              = [lower("esanvg-${each.key}1-${local.location}-${var.environment}")]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = "<MY_DNS_ZONE>"
    private_dns_zone_ids = ["<MY_DNS_ZONE_ID>"]
  }

  tags = var.tags

  depends_on = [ azurerm_elastic_san_volume_group.this ]
}

# Create the eSAN Volumes
resource "azurerm_elastic_san_volume" "this" {

  for_each = tomap({
    for vol in local.flattened_volumes :
    "${vol.key}-${vol.function}" => vol
  })

  name            = lower("esanvol-${each.value.key}1-${each.value.function}-${local.location}-${var.environment}")
  volume_group_id = azurerm_elastic_san_volume_group.this[each.value.key].id
  size_in_gib     = each.value.size_in_gb

  depends_on = [ azurerm_private_endpoint.this ]
}

Debug Output/Panic Output

│ Error: creating Volume (Subscription: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
│ Resource Group Name: "rg-esan-euw-dev"
│ Elastic San Name: "esan1-euw-dev"
│ Volume Group Name: "esanvg-abc1-euw-dev"
│ Volume Name: "esanvol-abc1-data-euw-dev"): polling after Create: expected either `provisioningState` or `status` to be returned from the LRO API but both were empty
│ 
│   with module.esan["1"].azurerm_elastic_san_volume.this["ABC-DATA"],
│   on .terraform\modules\esan\terraform\main.tf line 120, in resource "azurerm_elastic_san_volume" "this":
│  120: resource "azurerm_elastic_san_volume" "this" {
│ 
│ creating Volume (Subscription: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
│ Resource Group Name: "rg-esan-euw-dev"
│ Elastic San Name: "esan1-euw-dev"
│ Volume Group Name: "esanvg-abc1-euw-dev"
│ Volume Name: "esanvol-abc1-data-euw-dev"): polling after Create: expected
│ either `provisioningState` or `status` to be returned from the LRO API but
│ both were empty

Expected Behaviour

Elastic SAN Volumes should get created without issue.

Actual Behaviour

Azure Portal confirms the Elastic SAN Volume/s do get created under the Volume Groups, as desired, however the Terraform APPLY shows as failed with the error about provisioningState.

I did even try add a time_sleep resource to wait 60 seconds, before attempting to create the azurerm_elastic_san_volume however this made no difference.

Steps to Reproduce

terraform apply

Important Factoids

n/a

References

No response

teowa commented 1 month ago

Hi @darrens280 , thanks for reporting this! I can successfully reproduce the issue, this should be upstream API issue that the polling operation API response does not include the properties.provisionState property, let me report this to the service team.

AzureRMResponseforhttps: //management.azure.com/subscriptions/<redacted>/providers/Microsoft.ElasticSan/locations/westeurope/asyncoperations/<redacted>
{
  "properties": {
    "creationData": {
      "createSource": "None"
    },
    "sizeGiB": 1,
    "storageTarget": {
      "targetIqn": "iqn.2024-07.net.windows.core.blob.ElasticSan.es-<redacted>:esanvol2",
      "provisioningState": "Succeeded",
      "status": "Running",
      "targetPortalHostname": "es-<redacted>.z30.blob.storage.azure.net",
      "targetPortalPort": 3260
    },
    "volumeId": "<redacted>"
  },
  "id": "/subscriptions/<redacted>/resourceGroups/wt-rg-example/providers/Microsoft.ElasticSan/elasticSans/testeswangta/volumeGroups/esanvg/volumes/esanvol2",
  "name": "esanvol2",
  "type": "Microsoft.ElasticSan/elasticSans/volumeGroups/volumes",
  "systemData": {
    "createdAt": "2024-07-17T01:45:54.6372003Z",
    "createdBy": "<redacted>",
    "createdByType": "Application",
    "lastModifiedAt": "2024-07-17T01:45:54.6372003Z",
    "lastModifiedBy": "<redacted>",
    "lastModifiedByType": "Application"
  }
}

but I can successfully create if skip the private endpoing, the normal response is like:

{
  "properties": {
    "managedBy": {
      "resourceId": "None"
    },
    "provisioningState": "Succeeded",
    "creationData": {
      "createSource": "None"
    },
    "sizeGiB": 1,
    "storageTarget": {
      "targetIqn": "iqn.2024-07.net.windows.core.blob.ElasticSan.es-<redacted>:esanvol2",
      "provisioningState": "Succeeded",
      "status": "Running",
      "targetPortalHostname": "es-<redacted>.z28.blob.storage.azure.net",
      "targetPortalPort": 3260
    },
    "volumeId": "<redacted>"
  },
  "id": "/subscriptions/<redacted>/resourceGroups/wt-rg-example/providers/Microsoft.ElasticSan/elasticSans/testeswangta/volumeGroups/esanvg/volumes/esanvol2",
  "name": "esanvol2",
  "type": "Microsoft.ElasticSan/elasticSans/volumeGroups/volumes",
  "systemData": {
    "createdAt": "2024-07-17T01:37:24.3919886Z",
    "createdBy": "<redacted>",
    "createdByType": "Application",
    "lastModifiedAt": "2024-07-17T01:37:24.3919886Z",
    "lastModifiedBy": "<redacted>",
    "lastModifiedByType": "Application"
  }
}
darrens280 commented 2 weeks ago

Hi @teowa. Any chance there is some progress with a fix for this? Thank you