My Terraform code is broadly architected like so:
module "firewall_hub" {
# This creates the Azure Firewall resource
source = "/path/to/module/a"
# attribute = value...
}
module "firewall_spoke" {
# This creates, amongst other things, firewall rule sets
source = "/path/to/module/b"
hub = module.firewall_hub
# attribute = value...
}
module "another_firewall_spoke" {
# This creates, amongst other things, firewall rule sets
source = "/path/to/module/c"
hub = module.firewall_hub
# attribute = value...
}
i.e., The Azure Firewall resource is created in module.firewall_hub
, which is used as an input into module.firewall_spoke
and module.another_firewall_spoke
that create their necessary resources and inject firewall rule sets into the Firewall resource. Importantly, the rule sets are mutually exclusive between spoke modules and designed such that their priorities don't overlap.
When I try to deploy this code (either build or destroy), Azure throws an error:
Error: deleting Application Rule Collection "XXX" from Firewall "XXX (Resource Group "XXX"): network.AzureFirewallsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: autorest/azure: Service returned an error. Status= Code="AnotherOperationInProgress" Message="Another operation on this or dependent resource is in progress. To retrieve status of the operation use uri: https://management.azure.com/subscriptions/XXX" Details=[]
My working hypothesis is that one cannot make multiple create/update/delete requests of firewall rule sets against the same firewall simultaneously, even if the rule sets are mutually exclusive. Indeed, if you wait a minute-or-so after the failed deployment and restart it -- without changing any Terraform code or manually updating resources in Azure -- it will happily carry on without error and complete successfully.
To test my assumption, I tried to workaround this by forcing serialisation of modules:
module "another_firewall_spoke" {
# This creates, amongst other things, firewall rule sets
source = "/path/to/module/c"
hub = module.firewall_hub
# attribute = value...
depends_on = [module.firewall_spoke]
}
However, unfortunately, this is not possible with the way my modules are written:
Providers cannot be configured within modules using count, for_each or depends_on.
Short of rewriting my modules (not an option), is it possible to get around this race condition -- if that's the problem -- or would you consider it a bug with the azurerm
provider (i.e., it should recognise that API error response and wait its turn, up to some timeout)?
(Terraform v1.1.7, azurerm
v2.96.0)