与 Azure AD 提供商相关的 Terraform 抛出错误
Terraform throwing error related to Azure AD provider
我正在尝试为 Azure AD 应用程序创建一个模块 Registration.My 工作区是在 Terraform Cloud 中配置的,工作区有我的 client_id、client_secret、[=90 的变量集=] 和 tenant_id.
我的模块子文件夹中有四个文件。
- main.tf
- output.tf
- resources.sp.tf(这是为我的应用程序注册创建服务主体)
- var.tf
然后在主文件夹中,我有 main.tf、resources.app-reg.tf 和 var.tf
modules子目录下代码如下:
文件 --> 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
}
}
}
}
}
文件 --> 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 = []
}
文件 --> resource.sp.tf
resource "azuread_service_principal" "default" {
application_id = azuread_application.app-reg.application_id
owners = var.owners
app_role_assignment_required = false
}
现在我的主要文件引用模块子文件夹中的模块如下所示
文件 --> 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" {}
文件 --> var.tf
variable "client_id" {}
variable "client_secret" {}
variable "tenant_id" {}
variable "subscription_id" {}
文件 --> 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"
}]
}
#使用此代码添加 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/"]
}
}
但是每当我在 Terraform Cloud Workspace 中 运行 terraform plan 时,我总是收到附加错误。
知道为什么会出现错误,因为我已经在模块中声明了提供程序以及模块子文件夹外的 main.tf 文件
您需要正确配置您的 azuread,方法是为其添加提供商块。例如:
# Configure the Azure Active Directory Provider
provider "azuread" {
tenant_id = "00000000-0000-0000-0000-000000000000"
}
我正在尝试为 Azure AD 应用程序创建一个模块 Registration.My 工作区是在 Terraform Cloud 中配置的,工作区有我的 client_id、client_secret、[=90 的变量集=] 和 tenant_id.
我的模块子文件夹中有四个文件。
- main.tf
- output.tf
- resources.sp.tf(这是为我的应用程序注册创建服务主体)
- var.tf
然后在主文件夹中,我有 main.tf、resources.app-reg.tf 和 var.tf
modules子目录下代码如下:
文件 --> 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
}
}
}
}
}
文件 --> 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 = []
}
文件 --> resource.sp.tf
resource "azuread_service_principal" "default" {
application_id = azuread_application.app-reg.application_id
owners = var.owners
app_role_assignment_required = false
}
现在我的主要文件引用模块子文件夹中的模块如下所示
文件 --> 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" {}
文件 --> var.tf
variable "client_id" {}
variable "client_secret" {}
variable "tenant_id" {}
variable "subscription_id" {}
文件 --> 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"
}]
}
#使用此代码添加 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/"]
}
}
但是每当我在 Terraform Cloud Workspace 中 运行 terraform plan 时,我总是收到附加错误。
知道为什么会出现错误,因为我已经在模块中声明了提供程序以及模块子文件夹外的 main.tf 文件
您需要正确配置您的 azuread,方法是为其添加提供商块。例如:
# Configure the Azure Active Directory Provider
provider "azuread" {
tenant_id = "00000000-0000-0000-0000-000000000000"
}