terraform-provider-azuread icon indicating copy to clipboard operation
terraform-provider-azuread copied to clipboard

Terraform destroy on azuread_service_principal returns 403, but is destroyed

Open avinashpancham opened this issue 2 years ago • 7 comments

Currently I am using the Terraform's azurerm and azuread provider to create the same resources as az ad sp create-for-rbac would do. For this I apply the following code:

provider "azurerm" {
  features {}
}

data "azurerm_client_config" "current" {}

data "azurerm_resource_group" "rg" {
  name = "my_rg"
}


resource "azuread_application" "app-registration" {
  display_name = "my-app-registration"
  owners       = [data.azurerm_client_config.current.object_id]
}

resource "azuread_service_principal" "service-principal" {
  application_id = azuread_application.app-registration.application_id
}

resource "azuread_application_password" "password" {
  application_object_id = azuread_application.app-registration.object_id
  end_date              = "2022-01-01T00:00:00Z"
}

resource "azurerm_role_assignment" "role" {
  principal_id         = azuread_service_principal.service-principal.id
  role_definition_name = "Contributor"
  scope                = data.azurerm_resource_group.rg.id
}

For now I authenticate via a User principal (i.e. az login) to an Azure subscription of which I am the Owner. In AAD I am a regular User (with no additional assigned roles such as Application Administrator or Global Administrator). Applying the above code with terraform apply works and creates the app registration, service principal and service principal secret.

When I, however, use terraform destroy on this code I get the following error

│ Error: Deleting service principal with object ID "XXX", got status 403
│ 
│ ServicePrincipalsClient.BaseClient.Delete(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to complete the operation.

At that moment the following resources are left in my terraform state:

data.azurerm_client_config.current
azuread_application.app-registration
azuread_service_principal.service-principal

If I then again use terraform destroy, the azuread_application.app-registration is destroyed. Meaning that the service principal is deleted in Azure and not accessible anymore, but azuread_service_principal.service-principal is still present in my terraform state.

Terraform (and AzureAD Provider) Version

Terraform v1.0.5 on linux_amd64

  • provider registry.terraform.io/hashicorp/azuread v2.0.1
  • provider registry.terraform.io/hashicorp/azurerm v2.74.0

Affected Resource(s)

  • azuread_application
  • azuread_service_principal

Terraform Configuration Files

See above

# Copy-paste your Terraform configurations here - for large Terraform configs,
# please use a service like Dropbox and share a link to the ZIP file. For
# security, you can also encrypt the files using our GPG public key: https://keybase.io/hashicorp

Debug Output

Panic Output

Expected Behavior

Either the azuread_application.app-registration and azuread_service_principal.service-principal should both be deletable from my Terraform state, or they should both be not deletable from my Terraform state.

Actual Behavior

azuread_application.app-registration and azuread_service_principal.service-principal are deleted from Azure, but azuread_service_principal.service-principal is still present in my Terraform state.

Steps to Reproduce

  1. terraform apply
  2. terraform destroy (throws error)
  3. terraform destroy (on the remaining resources)

Important Factoids

References

  • #0000

avinashpancham avatar Aug 31 '21 10:08 avinashpancham

May be related to #535

manicminer avatar Aug 31 '21 13:08 manicminer

I also hit this before reaching #535 - I think that it can be resolved by adding owners to the service principal, just as on the application.

I think that the reason that the service principal is destroyed is that, although destroying it as a standalone object failed, it goes when the application goes.

Would be nice if there's anything that can be done to make the terraform behaviour better, but I'm not sure what.

dimbleby avatar Aug 31 '21 13:08 dimbleby

This would usually be taken care of by the DAG if you reference the application_id in the service principal resource, from the application resource - I'll play around with this and see what can be improved.

manicminer avatar Aug 31 '21 14:08 manicminer

@dimbleby thanks for your solution, that indeed works! But I agree with @manicminer if the parent resource (in this case azuread_application.app-registration) is owned by my object-id then I would expect that you can delete the child resource (azuread_service_principal.service-principal) without specifying the owner.

avinashpancham avatar Aug 31 '21 17:08 avinashpancham

@avinashpancham Given the authentication setup you describe, I suspect this is down to API/object permissions. There is a useful Azure docs page describing the default permissions for users when no additional directory roles are assigned - which states that a regular (non-guest) user can register applications and read service principals (noting that service principals are referred to as 'enterprise applications' versus 'registered applications' which are app registrations).

I agree with @dimbleby that specifying yourself in the owners list for both the application and service principal may resolve the deletion issue for you. There is a bug in 2.0.0/2.0.1 with service principal owners so I'd suggest waiting for 2.1.0 to confirm that.

Failing that, the service principal resource has a new property use_existing. When you set this to true, the provider will first look for an existing service principal that might have been auto-created and will use that instead of creating a new one - and subsequently it will ignore any permissions errors when deleting/destroying that same service principal. It's not ideal behavior but does work around SPs for first-party applications and cases such as this where you don't have permission to delete but you can delete the linked app registration.

manicminer avatar Sep 02 '21 11:09 manicminer

Just as a heads up this is still an issue in 2.2.1

slyons avatar Sep 15 '21 16:09 slyons

Hi @avinashpancham, @dimbleby, @slyons

Thanks for reporting on this issue. I have not observed this error myself however it's likely in my testing I don't have the same configurations of principals and API/directory roles which would cause this to occur.

I would suggest checking that the following conditions are being met by your Azure and Terraform configurations:

  • If your authenticated principal is a user principal, that it is a Member of the tenant directory. If it is a Guest, it must have the Applications Administrator directory role assigned in the tenant in which it is a guest.
  • If your authenticated principal is a service principal, that one of these are true:
    • The linked application has the Applications.ReadWrite.All API role assigned and admin consent is granted to the service principal
    • The linked application has the Applications.ReadWrite.OwnedBy API role assigned with admin consent granted to the service principal, and in your Terraform configuration, that the owners is specified for both the application and service principal that Terraform is trying to create, and that owners for both resources includes the authenticated (calling) principal, e.g. the value of azuread_client_config.object_id

If you are confident these conditions are met, and you still receive the error, feel free to post HTTP traces for both the apply and the destroy operations to this issue, and I'll be happy to verify that Terraform is sending the correct request(s). Should Terraform be demonstrated to be doing the right thing, I'll pass the evidence to the service team for further investigation. Thanks!

manicminer avatar Sep 16 '21 08:09 manicminer