Мой код Terraform построен примерно так:
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...
}
т. е. ресурс брандмауэра Azure создается в module.firewall_hub
, который используется в качестве входных данных для module.firewall_spoke
и module.another_firewall_spoke
, которые создают необходимые ресурсы и внедряют наборы правил брандмауэра в ресурс брандмауэра. Важно отметить, что наборы правил являются взаимоисключающими между лучевыми модулями и разработаны таким образом, что их приоритеты не перекрываются.
Когда я пытаюсь развернуть этот код (собрать или уничтожить), Azure выдает ошибку:
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=[]
Моя рабочая гипотеза заключается в том, что нельзя сделать несколько запросов на создание/обновление/удаление наборов правил брандмауэра для одного и того же брандмауэра одновременно, даже если наборы правил являются взаимоисключающими. Действительно, если вы подождете минуту или около того после неудачного развертывания и перезапустите его — без изменения кода Terraform или ручного обновления ресурсов в Azure — оно успешно продолжится без ошибок и завершится успешно.
Чтобы проверить свое предположение, я попытался обойти это, принудительно сериализовав модули:
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]
}
Однако, к сожалению, это невозможно с тем, как написаны мои модули:
Providers cannot be configured within modules using count, for_each or depends_on.
Если не считать переписывания моих модулей (не вариант), можно ли обойти это состояние гонки — если это проблема — или вы считаете это ошибкой поставщика azurerm
(т. е. он должен распознавать этот ответ об ошибке API и ждать своей очереди, до некоторого таймаута)?
(Терраформ v1.1.7, azurerm
v2.96.0)
Потребовалось немало усилий, чтобы донести ресурс depends_on
до глубины моего модуля, но это работает. Спасибо :)
рад это слышать!
После того, как @silent сообщил этот ответ, я смог разрешить гонку, используя метод, описанный в нем.
Что-то вроде этого:
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
# Has an output "blockers" containing resources that cannot be deployed concurrently
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
waits_for = module.firewall_spoke.blockers
# attribute = value...
}
Таким образом, трюк заключается в том, чтобы ваши модули экспортировали выходные данные, содержащие список всех зависимых ресурсов, которые необходимо развернуть в первую очередь. Затем это может быть входом для последующих модулей, которые передаются к фактическим ресурсам, которым требуется значение depends_on
.
То есть в недрах моего модуля ресурсы есть:
resource "some_resource" "foo" {
# attribute = value...
depends_on = [var.waits_for]
}
При использовании этого метода следует помнить два важных замечания:
Переменная wait_for
в вашем модуле должен имеет тип any
; list(any)
не работает, так как Terraform интерпретирует это как однородный список (которым, скорее всего, не будет).
Как ни странно, imo, предложение depends_on
требует, чтобы вы явно использовали литерал списка (то есть [var.waits_for]
, а не просто var.waits_for
), даже, если переменная, через которую вы проходите, является списком. Это не печатает проверку в моей голове, но, видимо, Terraform не только не возражает против этого, но и ожидает этого!
Вы пробовали это обходное решение в зависимости от? stackoverflow.com/a/58277124/1537195