Home > Back-end >  Terraform throwing error related to Azure AD provider
Terraform throwing error related to Azure AD provider

Time:05-03

I am trying to create a module for Azure AD App Registration.My workspace is configured in Terraform Cloud and the Workspace has variable sets for my client_id, client_secret, subscription_id and tenant_id.

I have four files in my modules sub-folder.

  1. main.tf
  2. output.tf
  3. resources.sp.tf (This is for creating a service principal for my app registration)
  4. var.tf

And then in the main folder, i have main.tf, resources.app-reg.tf and var.tf

The code in the modules subfolder are as follows :

File --> main.tf

terraform {
  required_version = "~> 1.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 2.0"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.0"
    }
    random = {
      source  = "hashicorp/random"
      version = ">=3.1.0"
    }
  }
}




resource "azuread_application" "app-reg" {
  # mandatory arguments
  display_name = var.display_name

  # Optional arguments
 # fallback_public_client_enabled = var.fallback_public_client_enabled
  group_membership_claims        = var.group_membership_claims
  identifier_uris                = var.identifier_uris
  oauth2_post_response_required  = var.oauth2_post_response_required
  owners                         = var.owners
  prevent_duplicate_names        = var.prevent_duplicate_names
  template_id                    = var.template_id
  sign_in_audience               = var.sign_in_audience
 # support_url                    = var.support_url
  tags                           = var.tags

  dynamic "optional_claims" {
    for_each = var.optional_claims != null ? ["true"] : []
    content {

      dynamic "access_token" {
        for_each = lookup(var.optional_claims, "access_token", [])
        content {
          additional_properties = lookup(var.optional_claims.access_token, "additional_properties", [])
          essential             = lookup(var.optional_claims.access_token, "essential", null)
          name                  = lookup(var.optional_claims.access_token, "name", [])
          source                = lookup(var.optional_claims.access_token, "source", null)
        }
      }

      dynamic "id_token" {
        for_each = lookup(var.optional_claims, "id_token", [])
        content {
          additional_properties = lookup(var.optional_claims.id_token, "additional_properties", null)
          essential             = lookup(var.optional_claims.id_token, "essential", null)
          name                  = lookup(var.optional_claims.id_token, "name", null)
          source                = lookup(var.optional_claims.id_token, "source", null)
        }
      }

    #   dynamic "saml2_token" {
    #     for_each = lookup(var.optional_claims, "saml2_token", [])
    #     content {
    #       additional_properties = lookup(var.optional_claims.saml2_token, "additional_properties", null)
    #       essential             = lookup(var.optional_claims.saml2_token, "essential", null)
    #       name                  = lookup(var.optional_claims.saml2_token, "name", null)
    #       source                = lookup(var.optional_claims.saml2_token, "source", null)
    #     }
    #   }
    }
  }

#   dynamic "public_client" {
#     for_each = var.public_client != null ? ["true"] : []
#     content {
#       redirect_uris = lookup(var.public_client, "redirect_uris", null)
#     }
#   }

  dynamic "single_page_application" {
    for_each = var.single_page_application != null ? ["true"] : []
    content {
      redirect_uris = lookup(var.single_page_application, "redirect_uris", null)
    }
  }

  dynamic "api" {
    for_each = var.api != null ? ["true"] : []
    content {
      mapped_claims_enabled          = lookup(var.api, "mapped_claims_enabled", null)
      requested_access_token_version = lookup(var.api, "requested_access_token_version", null)
      known_client_applications      = lookup(var.api, "known_client_applications", null)

      dynamic "oauth2_permission_scope" {
        for_each = lookup(var.api, "oauth2_permission_scope", [])
        content {
          admin_consent_description  = oauth2_permission_scope.value["admin_consent_description"]
          admin_consent_display_name = oauth2_permission_scope.value["admin_consent_display_name"]
          id                         = oauth2_permission_scope.value["id"]
          enabled                    = lookup(oauth2_permission_scope.value, "enabled", true)
          type                       = oauth2_permission_scope.value["type"]
          user_consent_description   = lookup(oauth2_permission_scope.value, "user_consent_description", null)
          user_consent_display_name  = lookup(oauth2_permission_scope.value, "user_consent_display_name", null)
          value                      = lookup(oauth2_permission_scope.value, "value", null)
        }
      }
    }
  }

  dynamic "web" {
    for_each = var.web != null ? ["true"] : []
    content {
      redirect_uris = lookup(var.web, "redirect_uris", null)
 #     homepage_url  = lookup(var.web, "homepage_url", null)
      logout_url    = lookup(var.web, "logout_url", null)

      dynamic "implicit_grant" {
        for_each = lookup(var.web, "implicit_grant", null) != null ? [1] : []
        content {
          access_token_issuance_enabled = lookup(var.web.implicit_grant, "access_token_issuance_enabled", null)
          id_token_issuance_enabled     = lookup(var.web.implicit_grant, "id_token_issuance_enabled", null)
        }
      }
    }
  }

  dynamic "app_role" {
    for_each = var.app_role != null ? var.app_role : []
    content {
      allowed_member_types = app_role.value.allowed_member_types
      description          = app_role.value.description
      display_name         = app_role.value.display_name
      enabled              = lookup(app_role.value, "enabled", true)
      id                   = app_role.value.id
      value                = lookup(app_role.value, "value", null)
    }
  }

  dynamic "required_resource_access" {
    for_each = var.required_resource_access != null ? var.required_resource_access : []

    content {
      resource_app_id = required_resource_access.value.resource_app_id

      dynamic "resource_access" {
        for_each = required_resource_access.value.resource_access
        iterator = access
        content {
          id   = access.value.id
          type = access.value.type
        }
      }
    }
  }
}

File --> var.tf

variable "display_name" {
  type        = string
  description = "The display name for the application."
}

variable "api" {
  description = "An optional api block, which configures API related settings for this application."
  type        = any
  default     = null
}

variable "app_role" {
  description = "A collection of app_role blocks."
  type        = any
  default     = []
}


variable "fallback_public_client_enabled" {
  description = "Specifies whether the application is a public client. Appropriate for apps using token grant flows that don't use a redirect URI."
  type        = bool
  default     = false
}

variable "group_membership_claims" {
  description = "Configures the groups claim issued in a user or OAuth 2.0 access token that the app expects. Possible values are `None`, `SecurityGroup` or `All`."
  type        = list(string)
  default     = ["SecurityGroup"]
}

variable "identifier_uris" {
  description = "A list of user-defined URI(s) that uniquely identify a Web application within it's Azure AD tenant, or within a verified custom domain if the application is multi-tenant."
  type        = list(string)
  default     = []
}


variable "oauth2_post_response_required" {
  description = "Specifies whether, as part of OAuth 2.0 token requests, Azure AD allows POST requests, as opposed to GET requests."
  type        = bool
  default     = false
}

variable "optional_claims" {
  description = "An optional claim block."
  type        = any
  default     = null
}

variable "owners" {
  description = "A set of object IDs of principals that will be granted ownership of the application. Supported object types are users or service principals."
  type        = list(string)
  default     = []
}

variable "prevent_duplicate_names" {
  description = "If true, will return an error if an existing application is found with the same name."
  type        = bool
  default     = false
}



variable "required_resource_access" {
  description = "A collection of required resource access for this application."
  type        = any
  default     = null
}

variable "sign_in_audience" {
  description = "The Microsoft account types that are supported for the current application. Must be one of `AzureADMyOrg`, `AzureADMultipleOrgs`, `AzureADandPersonalMicrosoftAccount` or `PersonalMicrosoftAccount`."
  type        = string
  default     = "AzureADMyOrg"
}

variable "single_page_application" {
  description = "A single_page_application block, which configures single-page application (SPA) related settings for this application. Must be https."
  type        = any
  default     = null
}

# variable "support_url" {
#   description = "URL of the application's support page."
#   type        = string
#   default     = null
# }


variable "template_id" {
  description = "Unique ID for a templated application in the Azure AD App Gallery, from which to create the application."
  type        = string
  default     = null
}


variable "web" {
  description = "Configures web related settings for this application."
  type        = any
  default     = null
}

variable "tags" {
  description = "A set of tags to apply to the application. Cannot be used together with the feature_tags block"
  type        = list(string)
  default     = []
}

File --> resource.sp.tf

resource "azuread_service_principal" "default" {
  application_id               = azuread_application.app-reg.application_id
  owners                       = var.owners
  app_role_assignment_required = false
}

Now my main files which are referencing the module from the Modules subfolder are shown below

File --> main.tf

terraform {
  required_version = "~> 1.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 2.0"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.0"
    }
    random = {
      source  = "hashicorp/random"
      version = ">=3.1.0"
    }
  }
}

provider "azurerm" {
  tenant_id       = var.tenant_id
  client_id       = var.client_id
  client_secret   = var.client_secret
  subscription_id = var.subscription_id
  features {}
}

data "azuread_client_config" "current" {}

File --> var.tf

variable "client_id" {}

variable "client_secret" {}

variable "tenant_id" {}

variable "subscription_id" {}

File --> resources-app-reg.tf

resource "random_uuid" "random_id" {
  count = 4
}

module "azurerm_app_reg" {

  source       = "../App-Registration/Modules"
  providers    = { azuread = azuread, azurerm = azurerm }
  display_name = "GL-Application"
  tags         = ["Sample App", "Terraform"]

  owners = [data.azuread_client_config.current.object_id]

  # To set application uri to api//<app_id>, you need to update via script, this is not possible in terraform
  identifier_uris = ["https://gl-application.onmicrosoft.com"]

  prevent_duplicate_names = true

  #use this code for adding scopes
  api = {
    mapped_claims_enabled          = false
    requested_access_token_version = 2
    known_client_applications      = []
    oauth2_permission_scope = [{
      admin_consent_description  = "Role use to secure the api for TestScope_01"
      admin_consent_display_name = "TestScope_01"
      id                         = element(random_uuid.random_id[*].result, 0)
      type                       = "User"
      value                      = "TestScope_01"
    }]
  }

#use this code for adding app_roles

app_role = [
    {
      allowed_member_types = ["Application"]
      description          = "Giving write permission to the apim proxy as 'Query-01.Read'"
      display_name         = "Query-01.Read"
      id                   = element(random_uuid.random_id[*].result, 1)
      value                = "Query-01.Read"
    },
    {
      allowed_member_types = ["Application"]
      description          = "Giving write permission to the apim proxy as 'Query-01.Write'"
      display_name         = "Query-01.Write"
      id                   = element(random_uuid.random_id[*].result, 2)
      value                = "Query-01.Write"
    }
  ]

  #use this code for adding api permissions
  required_resource_access = [{
    # Microsoft Graph
    resource_app_id = "00000003-0000-0000-c000-000000000000"

    resource_access = [{
      # User.Read
      id   = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
      type = "Scope"
    }]
  }]


  optional_claims = {
    access_token = {
      name = "myclaim"
    }
    access_token = {
      name = "otherclaim"
    }
    id_token = {
      name                  = "userclaim"
      source                = "user"
      essential             = true
      additional_properties = ["emit_as_roles"]
    }
  }

  web = {
    redirect_uris = [ "https://cde.com/", "https://ijk.com/"]
  }

}

But whenever i am running terraform plan in Terraform Cloud Workspace i keep getting the error attached.

Any idea why the error is coming as i already have the providers declared in the module as well as my main.tf file outside the module subfolderenter image description here

CodePudding user response:

You need to correctly configure your azuread provided by adding provider block for it. For example:


# Configure the Azure Active Directory Provider
provider "azuread" {
  tenant_id = "00000000-0000-0000-0000-000000000000"
}
  • Related