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

How to create an app that also creates a service principal

Open gtmtech opened this issue 3 years ago • 7 comments

Hi @manicminer,

I wondered if you were able to help with this as its closely related to https://github.com/hashicorp/terraform-provider-azuread/issues/679

I have some config which creates an azuread_application - I'm using template_id in order to be able to specify the AWS Single-Account Gallery App as the template, for which terraform successfully creates it.

However I want to also manage the ServicePrincipal, and now my problem is - the Templating process creates both an /application and a /servicePrincipal - ie a full enterprise app. But terraform, in creating the azuread_app only, did not create an azuread_service_principal object.

This only seems to leave me with the option of doing a one-off import of the created service-principal back into terraform in order to be able to manage it. With the desire to manage thousands of enterprise apps, this isn't really a process that works too well, as it involves lots of potentially manual steps.

My question is, when you cane up with specifying the template_id field in order to be able to create a templated application (and resultant enterprise-app), did you have any ideas on how to manage this workflow in terraform, or did you concede that unfortunately the only way of then managing both going forward is to have to import the result?

I have no idea if its possible, but some other terraform resources such as the default_route_table resource in aws allow you to selectively take over managing something which is already created outside of terraform. It sounds like azuread_service_principal would need to support that "feature"

Many thanks

gtmtech avatar Apr 26 '22 15:04 gtmtech

@gtmtech We've had to make the best of a non-idiomatic API implementation with this. App templating via the API does indeed create both an application and a service principal and there's no way to separate these operations - it's both or nothing.

To try and improve the workflow here, and for other similar situations, we added the use_existing property to the azuread_service_principal resource. This is kind of a hack; it basically auto imports the service principal if it already exists, and creates it otherwise, allowing you to manage SPs that were created out of band.

There are caveats - when use_existing is true, during destroy Terraform will attempt to delete the service principal, but will silently ignore failures. I believe this results in mostly acceptable behavior for most use cases, but it does mean you'll sometimes get orphaned objects on destroy and Terraform doesn't tell you about it.

Check out the last example on this page and please let me know if this enables a usable workflow for you. Thanks!

manicminer avatar Apr 26 '22 21:04 manicminer

Hi @manicminer - makes sense and thanks for the update.

I've found that in order to set up an AWS Gallery Application up properly, you have to do the following in order:

  1. Create an app registration (with template_id)
  2. Patch the corresponding service-principal with:
PATCH
{
  "preferredSingleSignOnMode": "saml"
}
  1. Now go back to the application and add in the saml configuration
  2. Now go back to the service principal and configure the rest.

It's a little bit of a horrible workflow, but given what you said about use_existing, I think the way to get this working in terraform would be to be able to add a service_principal {} block with the azuread_application resource, to configure this preferredSingleSignOnMode. - If that gets configured before the web {} block and identifierUris, then I think the whole 4 step process will work.

I know its a bit ugly, to have a tiny bit of the SP configured within the scope of the azuread_application resource, but I dont see another way to do it. -- and in this case I think the SP block I'm talking about can be very minimal.

What do you think about this?

Example code:

resource "azuread_application" "myapp" {
    display_name = "AWS"
    template_id = "......."
    service_principal {
        preferred_single_signon_mode = "saml"
    }
    identifier_uris = [
        "https://signin.aws.amazon.com/saml#${random_uuid.application.result}",
    ]
    web {
        homepage_url  = "https://signin.aws.amazon.com/saml?metadata=aws|ISV9.1|primary|z"
        redirect_uris = [
          "https://signin.aws.amazon.com/saml",
        ]
        implicit_grant {
          access_token_issuance_enabled = false
          id_token_issuance_enabled   = true
        }
    }
}

BTW, I think another approach would be to combine the notions of an azuread_application and an azuread_serviceprincipal, and do everything in one resource (as an alternative option to using the pair of resources).

I guess one of the reasons why I'm asking is I have to do this anyway for my org as they need to manage 1000s of these kind of apps, so I do have to come up with a solution in this space. I'd much prefer to create something you're willing to accept rather than maintain a special fork of the azuread provider for this unique usecase.

I also think AWS SAML Federation is a very common usecase to setup in Azure, so it would be good to have some kind of support for being able to set one up end-to-end.

gtmtech avatar Apr 27 '22 09:04 gtmtech

@gtmtech Thanks for the feedback. Those are both very interesting suggestions, I think there's merit to trying both of these though would possibly lean more towards having a service_principal block in the azuread_application resource, as a combined resource could escalate in complexity quite quickly. It's challenging to have such an imperative workflow but it would be great to support this use case.

If you decide to work on it, just pop a note here and feel free to open an early draft PR :)

manicminer avatar Apr 27 '22 09:04 manicminer

Thanks @manicminer - yes I'll work on it immediately - I've become a bit better at writing terraform providers since working on that other issue 😅 - if it works I'll send through both PRs asap.

gtmtech avatar Apr 27 '22 09:04 gtmtech

@manicminer I have some code that works, but in the process I discovered something I want your opinion on.

If using a template_id when instantiating an application, lots of things get set by Azure itself - example being appRoles and api attributes of the application object.

If you dont specify those (or even if you do), the ApplicationResourceCreate passes control to the ApplicationResourceUpdate, which then patches those values.

However I have found that when the api is patched in an application, you can no longer add appRoles to the corresponding servicePrincipal with an error message that the properties between the servicePrincipal and the application does not match.

I am wondering how to solve this problem because the ApplicationResourceUpdate function definitely patches a whole lot of values, and some of these have adverse affects on the SP.

Perhaps there is a way of selectively telling the ApplicationResourceUpdate function "these values haven't changed, please dont patch them" - or have attributes on the resource like "ignore_api" - and if its set to true then Update wont bother patching them.

That seems a bit nonideal though. I wondered if setting lifecycle ignore_changes= [ api ] would force terraform to not then patch the api, but I dont see anything in the code that would prevent it - when you're assembling the Properties object to send to the PATCH, it has a whole lot of attributes in it, and there isn't any logic there to ignore any if ignore_changes is set.

Still wondering about the best approach on this, but thought I might reach out to you to see if you had any ideas too.

If I comment out the patching of the api (comment out the api attribute in the Properties object) then my new code works great

gtmtech avatar Apr 29 '22 10:04 gtmtech

Hi @gtmtech, I have been battling the same issue. Have you found any workaround for getting identifier_uri's to work with a non owned domain?

fwd-sol-group avatar Jun 17 '22 22:06 fwd-sol-group

I ran into a very similar issue with the GitHub Enterprise Cloud gallery application. Not sure the workaround is worth the benefit of getting an Enterprise App in IAC, as having a very fragile workaround eliminates a lot of the benefits of IAC.

angusjellis avatar Sep 05 '22 16:09 angusjellis

Sorry it has been awhile since I've been able to look to this. We are releasing a number of new resources including azuread_application_from_template and a host of others for managing individual components of an application, which I believe will enable workflows like the one discussed here.

Accordingly, I'm going to elect to close this issue with #1214, and after this if there are scenarios still unattainable via Terraform configuration, we'd welcome any new issues demoing the desired outcome and we'll work to enable those separately. Thanks!

manicminer avatar Oct 18 '23 03:10 manicminer