承接上文Terraform Azure diagnostic setting升级,之前说到azurerm_monitor_diagnostic_setting里的retention policy已经deprecated了,需要用azurerm_storage_management_policy替换
以recovery service vault的诊断设置为例,对应的azurerm_storage_management_policy可以参考下边的代码,其中各个Azure服务唯一有区别的地方就是prefix_match这里,因为每个服务对应的文件名都是不一样的,所以prefix也各不相同,这就导致很难用一段代码覆盖所有的服务
resource "azurerm_storage_management_policy" "recovery_vault_diagnostic" {
for_each = local.settings
storage_account_id = each.value.storage_account_id
rule {
name = format("%s%s", "RecoveryVaultRetentionPolicy_", each.value.diagnostic_settings.storage_account_name)
enabled = true
filters {
blob_types = ["blockBlob"]
prefix_match = [
format("%s%s%s%s%s%s", "insights-logs-addonazurebackupjobs/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key)),
format("%s%s%s%s%s%s", "insights-logs-azurebackupreport/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key)),
format("%s%s%s%s%s%s", "insights-logs-coreazurebackup/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key))
]
}
actions {
base_blob {
delete_after_days_since_modification_greater_than = each.value.retention_policy.days
}
}
}
}
在上一篇说过部署的时候这里会有一些坑,现在这个坑来了,上边的代码在部署之后会遇到报错,提示这个policy已经存在了
这是因为我之前在storage account里有其他的lifecycle management rule,这本来是很正常事情,没想到用terraform部署其他rule的时候会报错,而这个报错的原因也并不复杂,就是因为这个management policy的rules其实是个[],这个policy也是一个单独的对象,导致在用terraform部署的时候是新建一个policy,而不是修改原来的,这就会导致提示资源已经存在
这应该属于是设计上的问题,terraform只能部署新的policy,而一旦之前创建过rule,policy就会同时被创建出来,再用terraform部署policy就会报错了,这种问题看起来应该是没办法绕过去了,只能换个别的方向,尝试用ARM Template来部署,具体的代码就不贴了,直接说下结果,ARM Template确实可以部署成功,不会再报错,但是新的问题是ARM Template部署出来之后,会直接覆盖掉之前的rule。。而且这个policy里default这名字也是写死了的,只允许存在一个命名为default的policy,没办法同时存在多个policy
这导致ARM Template这条路也走不通,剩下的只能尝试用脚本来做了,事实证明这是可以的,原理就是在Terraform里通过null_resource来运行PowerShell命令,而在PowerShell命令里我们就不需要受Terraform或者ARM Template的限制了,完全可以做自己想做的任何事,我们可以先获取到当前的policy,然后把新建的rule合并到之前的policy里,这样就可以解决之前的问题了
逻辑基本就是这样,接下来直接把代码贴出来
resource "null_resource" "recovery_vault_diagnostic" {
for_each = local.settings
provisioner "local-exec" {
interpreter = ["pwsh", "-Command"]
command = <<-EOT
select-azSubscription ${element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)}
$rgName = "${each.value.diagnostic_settings.storage_account_resource_group_name}"
$accountName = "${each.value.diagnostic_settings.storage_account_name}"
$prefix1="${format("%s%s%s%s%s%s", "insights-logs-addonazurebackupjobs/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key))}"
$prefix2="${format("%s%s%s%s%s%s", "insights-logs-azurebackupreport/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key))}"
$prefix3="${format("%s%s%s%s%s%s", "insights-logs-coreazurebackup/resourceId=/SUBSCRIPTIONS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].id), 2)), "/RESOURCEGROUPS/", upper(element(split("/", azurerm_recovery_services_vault.recovery_vault[each.key].resource_group_name), 4)), "/PROVIDERS/MICROSOFT.RECOVERYSERVICES/VAULTS/", upper(each.key))}"
# Create a new action object.
$action = Add-AzStorageAccountManagementPolicyAction -BaseBlobAction Delete `
-daysAfterModificationGreaterThan ${each.value.retention_policy.days}
# Create a new filter object.
$filter = New-AzStorageAccountManagementPolicyFilter -PrefixMatch $prefix1,$prefix2,$prefix3 `
-BlobType blockBlob
# Create a new rule object.
$rule1 = New-AzStorageAccountManagementPolicyRule -Name "${format("%s%s%s%s", "RecoveryVaultRetentionPolicy_", each.key, "_", each.value.diagnostic_settings.storage_account_name)}" `
-Action $action `
-Filter $filter
# Get the current policy.
$Policy=Get-AzStorageAccountManagementPolicy -ResourceGroupName $rgName -StorageAccountName $accountName
if ($null -ne $Policy.Rules) {
# rules already exists
$rules=$Policy.Rules += $rule1
}else{
# rules not exist
$rules=$rule1
}
# Create the policy.
Set-AzStorageAccountManagementPolicy -ResourceGroupName $rgName `
-StorageAccountName $accountName `
-Rule $rules
EOT
}
depends_on = [azurerm_recovery_services_vault.recovery_vault]
}
最后附上截图,可以看到rules里会包含两条rule,一个是新建的,一个是之前就已经有的