terraform-provider-azapi
terraform-provider-azapi copied to clipboard
Microsoft.security/securityContacts 2023-12-01-preview does not support HTTP method "POST"
Short context: AzureRM provider's underlying API Schema currently does not support the newest additions from the version 2023. azapi provider is instead used to deploy the securityContacts using the JSON ARM schema directly.
Issue 1.
When deploying resource using Microsoft.Security/securityContacts@2023-12-01-preview the resource almost always runs into issues with "default" contact existing. While the AzureRM's version works OK and both deploys and destroys the object the azapi's version forced me to remove the contact manually (also called "email notifications" tab in MDC's nomenclature) in order to deploy through 2023 API (even though the AzureRM's version does seem to get removed both in statefile and in the Portal's view on destroy action).
This issue might be related to the fact that the AzureRM's schema seems to be based on the 2017 version and looking up the provider's code it defaults the name to "default1": https://github.com/hashicorp/terraform-provider-azurerm/blob/main/internal/services/securitycenter/security_center_contact_resource.go https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_contact
The newest API only accepts the name of "default". Once the "default" is manually removed (in the Portal) then the azapi_resource seemingly does not run into a conflict.
Issue 2.
First the azapi_resource was used to deploy the resource, but it would either conflict with the "default" or run into the error from the title above (although it has been a bit chaotic). As a workaround to that issue, the wrapper from https://github.com/Azure/terraform-provider-azapi/issues/57 is used (to instead of creating/destroying, just update the existing default using "PUT"), like so (randomized example):
resource "azapi_resource_action" "<randomized-name>" {
type = "Microsoft.Security/securityContacts@2023-12-01-preview"
resource_id = "${var.subscription_id}/providers/Microsoft.security/securityContacts/default"
when = "apply"
method = "PUT"
body = jsonencode({
properties = {
emails = "${randomized-variable}"
isEnabled = "${randomized-variable}"
notificationsByRole = {
roles = [
"${randomized-variable}"
]
state = "${randomized-variable}"
}
notificationsSources = [
{
sourceType = "Alert"
minimalSeverity = "<randomized-value>"
},
{
sourceType = "AttackPath"
minimalSeverity = "<randomized-value>"
},
]
}
})
}
The idea here is to update the resource on apply and reset it on destroy. However, even though the method is provided explicitly, the API responds that "POST" is used and gives back 405.
This issue seems related: https://github.com/Azure/terraform-provider-azapi/issues/271
Summary:
Finally, the most bizarre thing is (I tested this with azapi_resource and commented out azapi_resource_action). If I attempt to clear up the entire MDC deployment, then redeploy - the Terraform deployment will fail due to 2 separate issues:
- the azapi_resource with the Microsoft.security/securityContacts running into the conflict with "default"
- the azapi_resource_action failing due to the "POST" method (which will effectively break statefiles in some production environments as it happened in my case. This object becomes irremovable from the statefile and needs to be removed manually).
As I said - the new API would eventually deploy using azapi_resource and would in fact update MDC's security contact(!). The weird thing is - during the deployment it seems that a part of the MDC (Subscriptions?) are already creating the "default" security contact.
This is why the first deployment fails - the contact indeed exists but with the default settings such as "High" alerts and "Owner" roles. If at this point I destroy it manually and redeploy, then the azapi_resource will in fact deploy and then the deployment fails due to the error with "POST" method not supported by the azapi_resource_action (which at this point becomes corrupted and irremovable through "destroy").
The new API locks out names other than "default". The old API would allow e.g. "default1". Something already creates the "default" during MDC setup and hence the security center conflicts with the name of "default". Reference: https://learn.microsoft.com/en-us/azure/templates/microsoft.security/2017-08-01-preview/securitycontacts?pivots=deployment-language-terraform
Explicit security contact hence fails to destroy/overwrite as it cannot import what doesn't belong to it and the "PUT" method inside the azapi_resource_action is for some reason ignored.
Solution/Testing: To test this idea I followed up by replacing azapi_resource and azapi_resource_action with azapi_update_resource. The default secure contact must already exist in runtime when creating MDC Subscriptions (plans) because it then gets succesfully updated.
This method managed to successfully modify the default security_contact with neither name conflicts nor method conflicts. However, I would expect the API to either allow different names (like it used to) or the resource_action to work with different methods (in case one would like to e.g. revert to different settings on resource destroy action).
This method does not explain why azapi_resource would work after the manual step involving the removal of the "default" security_contact. This update method is also lacking, because the destroy action does nothing. Destroyed changes don't revert to the defaults as documented:
"When delete azapi_update_resource, no operation will be performed, and these properties will stay unchanged." https://registry.terraform.io/providers/Azure/azapi/latest/docs/resources/azapi_update_resource