5
votes

I have read the write-ups online but they dont seem to cover this topic completely and was hoping someone who has done it may have some direction for me. We are setting up a complicated Terraform template to satisfy our IaC requirements relating to our SaaS offering. In doing so we want the template to use the user's credentials at launch to create a new service principal in Azure AD (This part I have no problem doing). Then in the next portion of the template we are using that service principal as the provider. Problem is that it throws errors in the plan/apply because the service principal doesnt exist (aka the id is non existent due to the service provider section not running yet). So is there a way that I can do this? Create a service principal and then us it in a provider alias that uses that service principal without splitting this into multiple templates?

In the end I want this template to create the service provider using the local user's permissions or MSI, give it RBAC to a subscription, then use that service provider to create assets in that subscription.

main.ts (root)

provider "azurerm" {
    alias               = "ActiveDirectory"
    subscription_id     = "${var.subscriptionNucleus}"
}

provider "azurerm" {
    alias               = "Infrastructure"
    subscription_id     = "${var.subscriptionInfrastructure}"
}
module "activedirectory" {
    providers                       = { azurerm = "azurerm.ActiveDirectory" 
}
    source                          = "./modules/activedirectory"
    subscription_id_infrastructure  = "${var.subscriptionInfrastructure}"
}
module "infrastructure" {
    providers                   = { azurerm = "azurerm.Infrastructure"}
    source                      = "./modules/infrastructure"
    location                    = "${var.location}"
    application_id              = 
 "${module.activedirectory.service_principal_application_id}"
    subscription_id             = "${var.subscriptionInfrastructure}"
    prefix                      = "${var.prefix}"
}

main.ts (./modules/infrastructure)

data "azurerm_azuread_service_principal" "serviceprincipal" {
    application_id = "${var.application_id}"
}

provider "azurerm" {
    alias           = "InfrastructureSP"
    subscription_id = "${var.subscription_id}"
    client_id       = "${var.application_id}"
    client_secret   = "secret"
    tenant_id       = 
"${data.azurerm_client_config.clientconfig.tenant_id}"  
}
2

2 Answers

12
votes

For Azure Service Principal, there are two ways to use the service principal.

First: If you already have a service principal and want to use it in the Terraform. You can make use of the Terraform Data and the test like this:

data "azurerm_azuread_service_principal" "sp" {
        application_id  = "21f3e1de-54e2-4951-9743-c280ad7bd74a"
}

output "test" {
        value = "${data.azurerm_azuread_service_principal.sp.id}"
}

The screenshot of the result is here:

enter image description here

Second: You don't have the service principal and you can just create a service principal in the Terraform like this:

resource "azurerm_azuread_service_principal" "test" {
  application_id = "${azurerm_azuread_application.test.application_id}"
}

resource "azurerm_azuread_service_principal_password" "test" {
  service_principal_id = "${azurerm_azuread_service_principal.test.id}"
  value                = "your pasword"
  end_date             = "2020-01-01T01:02:03Z" 
}

Then, no matter which way you choose, there is an important step you should do for most resources. The step is that you need to create the role to give the permission and then assign it to the resource which needs. You can do that like this:

resource "azurerm_role_assignment" "test" {
  scope                = "yourScope"   # the resource id
  role_definition_name = "the Role In need" # such as "Contributor"
  principal_id         = "your service principal id"
}

Hope this will help you.

-2
votes

There is currently no working "depends_on" that works with modules that is not a hack (null_reference). This means that if you are breaking your template into modules(separating concerns) your order of operation is required to be correct to complete this successfully as one module will not know that the data source of service provider has to wait on the previous module to complete. I have had to break this into 2 separate templates where the first creates the service principal and the second has the modular separation that can then use a data source of azurerm_azuread_service_principal. Once Hashicorp can implement the module depends_on, this will become easier.