How Do I Add Active Directory To APIM Using Terraform? - terraform

Following this article you can link Azure API Management to Users/Groups in Azure Active Directory.
At the moment I am creating the APIM instance with Terraform
resource "azurerm_api_management" "test" {
name = "example-apim"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
publisher_name = "My Company"
publisher_email = "company#terraform.io"
sku {
name = "Developer"
capacity = 1
}
}
How do I add the Active Directory Identity Provider to this?

Terraform added support for this in December 2019
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_identity_provider_aad
You can now link it with:
resource "azurerm_api_management_identity_provider_aad" "example" {
resource_group_name = azurerm_resource_group.example.name
api_management_name = azurerm_api_management.example.name
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "00000000000000000000000000000000"
allowed_tenants = ["00000000-0000-0000-0000-000000000000"]
}

This doesn't seem to be possible with terraform, however, it can be added by calling the REST API from the Azure CLI.
az rest -m put -u "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/my-resource-group/providers/Microsoft.ApiManagement/service/my-apim/identityProviders/aad?api-version=2019-01-01" -b "{'properties':{'clientId':'xxxxx-xxx-xxxx-xxxx-xxxxxxxxxx','clientSecret':'super-secret-password','allowedTenants':['mysite.com']}}"
The body -b is json that has been formatted to a single line.
You need to look up the clientId from active directory and know what the clientSecret is.
You can embedd this command in terraform if you wish:
resource "null_resource" "add-ad-identity-provider" {
provisioner "local-exec" {
command = "az rest -m put -u \"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/my-resource-group/providers/Microsoft.ApiManagement/service/my-apim/identityProviders/aad?api-version=2019-01-01\" -b \"{'properties':{'clientId':'xxxxx-xxx-xxxx-xxxx-xxxxxxxxxx','clientSecret':'super-secret-password','allowedTenants':['mysite.com']}}\""
}
depends_on = ["azurerm_api_management.test"]
}

the original answer from March 4th mostly works. However, a piece is missing. You also need to set up an app registration via https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-aad
That supplies the answers you need (Other than the allowed tenants, which is the tenant-id's to allow).
And that is also missing a piece, which is to, when configurating the app registration, to also go to API Permissions, add a new permission for Azure Active Directory Graph (in supported legacy APIs), create an Application permission, and add Directory.Read.All. Then grant admin consent.

If you combine resources from both azurerm and azuread providers, you can now automate the process of deploying APIM with an app registration and AAD authentication on the developer portal. It covers these two guides from Microsoft:
Quickstart: Create a new Azure API Management service instance by using the Azure portal
Authorize developer accounts by using Azure Active Directory in Azure API Management
Terraform code example:
terraform {
required_version = ">=1.0.9"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.81.0"
}
azuread = {
source = "hashicorp/azuread"
version = "=2.7.0"
}
}
backend "azurerm" {}
}
provider "azurerm" {
features {}
}
provider "azuread" {}
resource "azurerm_api_management" "api_management" {
name = var.api_management_name
location = var.location
resource_group_name = var.resource_group_name
publisher_name = var.publisher_name
publisher_email = var.publisher_email
sku_name = var.api_management_sku
identity {
type = "SystemAssigned"
}
}
resource "azuread_application" "application" {
display_name = var.application_name
web {
redirect_uris = ["${azurerm_api_management.api_management.developer_portal_url}/"]
}
}
resource "azuread_application_password" "password" {
application_object_id = azuread_application.application.object_id
}
resource "azurerm_api_management_identity_provider_aad" "identity_provider_aad" {
resource_group_name = var.resource_group_name
api_management_name = azurerm_api_management.api_management.name
client_id = azuread_application.application.application_id
client_secret = azuread_application_password.password.value
allowed_tenants = var.id_provider_allowed_tenants
}

Related

Using terraform to update existing Azure APIM

I have a no. of API management services setup on Azure already but they are not provisioned using Terraform. Now I want to using terraform to update the backend HTTP endpoint and policies. Is it possible to do so? Is there any data sources for apim I can use? Any example I can refer to?
I tried in my environment and got below results:
Initially, I created Azure APIM management service using below terraform code.
main.tf
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "rg3"
location = "West Europe"
}
resource "azurerm_api_management" "example" {
name = "venkat-demo"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
publisher_name = "My Company"
publisher_email = "company#terraform.io"
sku_name = "Developer_1"
}
resource "azurerm_api_management_api" "example" {
name = "venkatdemo326"
resource_group_name = azurerm_resource_group.example.name
api_management_name = azurerm_api_management.example.name
revision = "1"
display_name = "vjtestAPI"
path = "example"
protocols = ["https"]
import {
content_format = "swagger-link-json"
content_value = "http://conferenceapi.azurewebsites.net/?format=json"
}
}
Portal:
Now I want to using terraform to update the backend HTTP endpoint and policies
You can use the below terraform code to create resources that manage the backend HTTP endpoint and policies of that service.
main.tf
provider "azurerm" {
features {}
}
data "azurerm_api_management" "example" {
name = "venkat-demo"
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_api_management_backend" "example" {
name = "backend1"
api_management_name = data.azurerm_api_management.example.name
resource_group_name = data.azurerm_api_management.example.resource_group_name
protocol = "http"
url = "https://example.com/api"
}
resource "azurerm_api_management_policy" "example" {
api_management_id = data.azurerm_api_management.example.id
xml_content = <<XML
<policies>
<inbound>
<find-and-replace from="xyz" to="abc" />
</inbound>
</policies>
XML
}
Console:
Portal:
Reference:
azurerm_api_management_backend | Resources | hashicorp/azurerm | Terraform Registry
azurerm_api_management_policy | Resources | hashicorp/azurerm | Terraform Registry
If you want to change an Azure resource (in this case Azure APIM), you'll have to first declare the resource with the corresponding resource type (in this case azurerm_api_management) and then to import your existing resource into your Terraform's state.
After importing, on the subsequent execution of terraform plan, you'll see the changes terraform would like to execute based on the difference between your declaration and the current state of your APIM instance.
You cannot use a terraform data source (like this) if you want to change (=manage) the resource. Data sources can only be used to read attributes from resources.

How to configure an Azure app service to pull images from an ACR with terraform?

I have the following terraform module to setup app services under the same plan:
provider "azurerm" {
}
variable "env" {
type = string
description = "The SDLC environment (qa, dev, prod, etc...)"
}
variable "appsvc_names" {
type = list(string)
description = "The names of the app services to create under the same app service plan"
}
locals {
location = "eastus2"
resource_group_name = "app505-dfpg-${var.env}-web-${local.location}"
acr_name = "app505dfpgnedeploycr88836"
}
resource "azurerm_app_service_plan" "asp" {
name = "${local.resource_group_name}-asp"
location = local.location
resource_group_name = local.resource_group_name
kind = "Linux"
reserved = true
sku {
tier = "Basic"
size = "B1"
}
}
resource "azurerm_app_service" "appsvc" {
for_each = toset(var.appsvc_names)
name = "${local.resource_group_name}-${each.value}-appsvc"
location = local.location
resource_group_name = local.resource_group_name
app_service_plan_id = azurerm_app_service_plan.asp.id
site_config {
linux_fx_version = "DOCKER|${local.acr_name}/${each.value}:latest"
}
app_settings = {
DOCKER_REGISTRY_SERVER_URL = "https://${local.acr_name}.azurecr.io"
}
}
output "hostnames" {
value = {
for appsvc in azurerm_app_service.appsvc: appsvc.name => appsvc.default_site_hostname
}
}
I am invoking it through the following configuration:
terraform {
backend "azurerm" {
}
}
locals {
appsvc_names = ["gateway"]
}
module "web" {
source = "../../modules/web"
env = "qa"
appsvc_names = local.appsvc_names
}
output "hostnames" {
description = "The hostnames of the created app services"
value = module.web.hostnames
}
The container registry has the images I need:
C:\> az acr login --name app505dfpgnedeploycr88836
Login Succeeded
C:\> az acr repository list --name app505dfpgnedeploycr88836
[
"gateway"
]
C:\> az acr repository show-tags --name app505dfpgnedeploycr88836 --repository gateway
[
"latest"
]
C:\>
When I apply the terraform configuration everything is created fine, but inspecting the created app service resource in Azure Portal reveals that its Container Settings show no docker image:
Now, I can manually switch to another ACR and then back to the one I want only to get this:
Cannot perform credential operations for /subscriptions/0f1c414a-a389-47df-aab8-a351876ecd47/resourceGroups/app505-dfpg-ne-deploy-eastus2/providers/Microsoft.ContainerRegistry/registries/app505dfpgnedeploycr88836 as admin user is disabled. Kindly enable admin user as per docs: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account
This is confusing me. According to https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account the admin user should not be used and so my ACR does not have one. On the other hand, I understand that I need somehow configure the app service to authenticate with the ACR.
What is the right way to do it then?
So this is now possible since the v2.71 version of the Azure RM provider. A couple of things have to happen...
Assign a Managed Identity to the application (can also use User Assigned but a bit more work)
Set the site_config.acr_use_managed_identity_credentials property to true
Grant the application's identity ACRPull rights on the container.
Below is a modified version of the code above, not tested but should be ok
provider "azurerm" {
}
variable "env" {
type = string
description = "The SDLC environment (qa, dev, prod, etc...)"
}
variable "appsvc_names" {
type = list(string)
description = "The names of the app services to create under the same app service plan"
}
locals {
location = "eastus2"
resource_group_name = "app505-dfpg-${var.env}-web-${local.location}"
acr_name = "app505dfpgnedeploycr88836"
}
resource "azurerm_app_service_plan" "asp" {
name = "${local.resource_group_name}-asp"
location = local.location
resource_group_name = local.resource_group_name
kind = "Linux"
reserved = true
sku {
tier = "Basic"
size = "B1"
}
}
resource "azurerm_app_service" "appsvc" {
for_each = toset(var.appsvc_names)
name = "${local.resource_group_name}-${each.value}-appsvc"
location = local.location
resource_group_name = local.resource_group_name
app_service_plan_id = azurerm_app_service_plan.asp.id
site_config {
linux_fx_version = "DOCKER|${local.acr_name}/${each.value}:latest"
acr_use_managed_identity_credentials = true
}
app_settings = {
DOCKER_REGISTRY_SERVER_URL = "https://${local.acr_name}.azurecr.io"
}
identity {
type = "SystemAssigned"
}
}
data "azurerm_container_registry" "this" {
name = local.acr_name
resource_group_name = local.resource_group_name
}
resource "azurerm_role_assignment" "acr" {
for_each = azurerm_app_service.appsvc
role_definition_name = "AcrPull"
scope = azurerm_container_registry.this.id
principal_id = each.value.identity[0].principal_id
}
output "hostnames" {
value = {
for appsvc in azurerm_app_service.appsvc: appsvc.name => appsvc.default_site_hostname
}
}
EDITED 21 Dec 2021
The MS documentation issue regarding the value being reset by Azure has now been resolved and you can also control Managed Identity via the portal.
So you can use service principal auth with App Service, but you'd have to create service principal grant it ACRpull permissions over the registry and use service principal login\password in App Service site_config
DOCKER_REGISTRY_SERVER_USERNAME
DOCKER_REGISTRY_SERVER_PASSWORD

Azure ACR - how to assign service principle through terraform?

Trying to assign a service principal to an Azure container registry to no avail. The Azure docs state the following...
You can assign a service principal to your registry, and your application or service can use it for headless authentication
I wish to do this through terraform, but I do not see any sort of service principal option in the Azure provider azurerm_container_registry docs (managed)...
resource "azurerm_resource_group" "rg" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_container_registry" "acr" {
name = "containerRegistry1"
resource_group_name = "${azurerm_resource_group.rg.name}"
location = "${azurerm_resource_group.rg.location}"
sku = "Premium"
admin_enabled = false
georeplication_locations = ["East US", "West Europe"]
}
I was hoping to find a service_principal stanza such as what is available in the azurerm_kubernetes_cluster resource...
resource "azurerm_kubernetes_cluster" "test" {
// [...]
service_principal {
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "00000000000000000000000000000000"
}
}
Am I thinking about this the wrong way? There seems to be a disconnect with a lot of the Azure resources on how service principals play a part. I assume evolving APIs are to blame, but this seems like a simple ask... Perhaps I'm totally off base here. Thoughts?
There is something different between the AKS and ACR with the Service Principal.
For AKS, it needs to create a secret for the pods or services to access the ACR and the secret is set in the yaml file. So it needs both client_id and client_secret at the same time.
But for ACR, you need a Service Principal with specific permission associated with it for others to access it. So you just need to assign the Service Principal to the ACR with the permission setting and client_id. The terraform code would like this:
resource "azurerm_resource_group" "rg" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_container_registry" "acr" {
name = "containerRegistry1"
resource_group_name = "${azurerm_resource_group.rg.name}"
location = "${azurerm_resource_group.rg.location}"
sku = "Premium"
admin_enabled = false
georeplication_locations = ["East US", "West Europe"]
}
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 = "VT=uSgbTanZhyz#%nL9Hpd+Tfay_MRV#"
end_date = "2020-01-01T01:02:03Z"
}
resource "azurerm_role_assignment" "test" {
scope = "${azurerm_container_registry.acr.id}"
role_definition_id = "Contributor"
principal_id = "${azurerm_azuread_service_principal_password.test.service_principal_id}"
}
For more details, see azurerm_role_assignment. Hope this will help you. If you need more help please let me know.

Azure Container Registry Permissions Using Terraform

Setting up a azurerm_container_registry with terraform, I was wondering how I can change the permissions for certain users (e.g. ReadOnly), or perhaps to create a access_key which can be used from my CI-Pipeline, but does not require a user at all.
This Terraform configuration creates an ACR registry, and Azure Service Principal,
and grants the SP contributor access to the ACR registry. This can be updated to reader.
More information can be found on ACR auth with service principals here.
resource "azurerm_resource_group" "acr-rg" {
name = "acr-rg-007"
location = "West US"
}
resource "azurerm_container_registry" "acr" {
name = "acr00722"
resource_group_name = "${azurerm_resource_group.acr-rg.name}"
location = "${azurerm_resource_group.acr-rg.location}"
sku = "standard"
}
resource "azurerm_azuread_application" "acr-app" {
name = "acr-app"
}
resource "azurerm_azuread_service_principal" "acr-sp" {
application_id = "${azurerm_azuread_application.acr-app.application_id}"
}
resource "azurerm_azuread_service_principal_password" "acr-sp-pass" {
service_principal_id = "${azurerm_azuread_service_principal.acr-sp.id}"
value = "Password12"
end_date = "2020-01-01T01:02:03Z"
}
resource "azurerm_role_assignment" "acr-assignment" {
scope = "${azurerm_container_registry.acr.id}"
role_definition_name = "Contributor"
principal_id = "${azurerm_azuread_service_principal_password.acr-sp-pass.service_principal_id}"
}
output "docker" {
value = "docker login ${azurerm_container_registry.acr.login_server} -u ${azurerm_azuread_service_principal.acr-sp.application_id} -p ${azurerm_azuread_service_principal_password.acr-sp-pass.value}"
}

Credential Failure while executing Terraform execution plan

I'm trying to execute a sample terraform plan given below.
# Configure the Microsoft Azure Provider
provider "azurerm" {
subscription_id = "..."
client_id = "..."
client_secret = "..."
tenant_id = "..."
}
# Create a resource group
resource "azurerm_resource_group" "production" {
name = "production"
location = "West US"
}
# Create a virtual network in the web_servers resource group
resource "azurerm_virtual_network" "network" {
name = "productionNetwork"
address_space = ["10.0.0.0/16"]
location = "West US"
resource_group_name = "${azurerm_resource_group.production.name}"
subnet {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet2"
address_prefix = "10.0.2.0/24"
}
subnet {
name = "subnet3"
address_prefix = "10.0.3.0/24"
}
}`enter code here`
I followed [1] to generate credentials via creating Active Directory application and used the correct subscription_id, client_id, client_secret, tenant_id in the above plan and executed 'terraform plan' against it. But I'm getting below error.
Error refreshing state: 1 error(s) occurred:
Credentials for acessing the Azure Resource Manager API are likely to be incorrect, or
the service principal does not have permission to use the Azure Service Management
API.
[1] https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-service-principal-portal/
Any idea on this?
It seems like in terraform documentation, they haven't included the step of assigning role to the service principal. Follow these steps and it works.
1) Create the service principal through Azure CLI by following this link https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal-cli/ which assigns the role as well to the service principal
2) Go to Azure RM portal-->Active Directory -->App registration --> Create the key
3) Use the appropriate values from above in .tf file.
Then run the command terraform plan.

Resources