Azure ACR - how to assign service principle through terraform? - azure

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.

Related

How to access Managed Entities inside managed aks cluster on azure through terraform

I have setup a AKS cluster through terraform and in the same Resource group, I have create an Application gateway. As such
resource "azurerm_kubernetes_cluster" "cluster" {
name = var.aks_name
tags = var.tags
location = var.vnet_location
resource_group_name = local.aks_resource_group
dns_prefix = "k8s"
oidc_issuer_enabled = true
role_based_access_control_enabled = true
azure_policy_enabled = true
#workload_identity_enabled = true
default_node_pool {
name = var.node_pool_name
node_count = var.node_count
vm_size = var.node_type
os_disk_size_gb = var.os_disk_size_gb
vnet_subnet_id = local.aks_subnet_id
}
identity {
type = "SystemAssigned"
}
ingress_application_gateway {
gateway_id = var.application_gateway_id
}
...
During creation, Azure creates a MC_******* resource group for the managed cluster and I use a service principal to deploy all Terraform resources
The ingress controller for the application "ingress_application_gateway" acquires automatically a managed entity deployed inside the MC_**** resource group.
I need to access that managed entity in order to set some needed access policies on dependent resources.
Ingress controller need access to both the application gateway aswell as the original Resource group, as such;
#----------------------------------------------------------------
# Ingress controller running on AKS
#-----------------------------------------------------------------
data "azurerm_resource_group" "aks_resource_group" {
name = local.aks_resource_group
}
#Gateway ingress controller should have reader access on resource group
resource "azurerm_role_assignment" "igc_reader_access" {
scope = data.azurerm_resource_group.aks_resource_group.id
role_definition_name = "Reader"
principal_id = data.azurerm_user_assigned_identity.ingress_controller_identity.principal_id
}
#Gateway ingress controller should have contributor access on application gateway
resource "azurerm_role_assignment" "ag_contributor_access" {
scope = var.application_gateway_id
role_definition_name = "Contributor"
principal_id = data.azurerm_user_assigned_identity.ingress_controller_identity.principal_id
}
data "azurerm_user_assigned_identity" "ingress_controller_identity" {
name = "ingressapplicationgateway-qa"
resource_group_name = "MC_RG-Digital-Service-qa_westeurope"
}
To the question, how can I setup so my Service principal (that is used to create the azurerm_kubernetes_cluster resource) to have reader access on the MC_****** resource group created by azure for aks.
I dont want to give complete subscription contributor access to my Service Principal.
You could do smtg like this, when your execute Terraform as the Service Principal (We could reduce the data resources if the Application Gateway would be in the same Terraform project):
# get current client ( your Service principal)
data "azurerm_client_config" "current" {
}
# get MC Resource Group
data "azurerm_resource_group" "aks_mc_rg" {
depends_on = [azurerm_kubernetes_cluster.cluster]
name = azurerm_kubernetes_cluster.aks.node_resource_group
}
# get Application Gateway
data "azurerm_application_gateway" "aks_igc" {
name = "existing-app-gateway"
resource_group_name = "existing-resources"
}
# read Managed identity
data "azurerm_user_assigned_identity" "ingress_controller_identity" {
name = "ingressapplicationgateway-qa"
resource_group_name = data.azurerm_resource_group.aks_mc_rg.id
}
# assign reader to current client
resource "azurerm_role_assignment" "rg_aks_nodes_owners" {
scope = data.azurerm_resource_group.aks_mc_rg.id
role_definition_name = "Reader"
principal_id = data.azurerm_client_config.current.object_id
}
#Gateway ingress controller should have reader access on resource group
resource "azurerm_role_assignment" "igc_reader_access" {
scope = data.azurerm_resource_group.aks_mc_rg.id
role_definition_name = "Reader"
principal_id = data.azurerm_user_assigned_identity.ingress_controller_identity.principal_id
}
#Gateway ingress controller should have contributor access on application gateway
resource "azurerm_role_assignment" "ag_contributor_access" {
scope = data.azurerm_application_gateway.aks_igc.id
role_definition_name = "Contributor"
principal_id = data.azurerm_user_assigned_identity.ingress_controller_identity.principal_id
}

How do you give "Storage Blob Data Contributor" permission to your Azure Devops project in Terraform?

It seems my question is related to this post but since there is no answer I will ask again.
I have an Azure Devops project which I use to deploy static content into a container inside a Storage Account via Pipelines. I've recently decided to deploy my infrastructure using Terraform as well as my code but I'm running into an issue. I managed to create all my infrastructure with Terraform inside my Pipeline except for the Role Assignment.
I basically need to add a new Role Assignment to my Storage Account, through Azure it goes :
Go to my Storage Account
Go to Access Control (IAM)
Add a new Role Assignments
Select Storage Blob Data Contributor
Click on Select members
Select my Azure Devops Project
Review + assign
From what I understand in the Terraform documentation I should do something like this :
resource "azurerm_resource_group" "resource_group" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.resource_group.name
location = azurerm_resource_group.resource_group.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_role_assignment" "role_assignment" {
scope = azurerm_storage_account.storage_account.id
role_definition_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" # Which is the Storage Blob Data Contributor role if I'm not mistaken.
principal_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy" # Which should be the Application ID ?
}
Except it doesn't work, when I try to run it in local without the Azure Pipeline to check if this works, the process is stuck in the "Still creating..." state for more than 10 minutes, which seems weird since when you do it manually it only takes up to a few seconds. I don't have any error I just end up canceling the command.
What am I missing / doing wrong here ?
I've found what was the issue. For the principal_id you need to put the Object_ID of your Service Principal and not your Application_ID. You end up with something like :
main.tf
...
locals {
sub = "/subscription"
permission_storage_blob_data_contributor = "providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe"
}
data "azurerm_subscription" "primary" { }
resource "azurerm_resource_group" "resource_group" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_storage_account" "storage_account" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.resource_group.name
location = azurerm_resource_group.resource_group.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_role_assignment" "role_assignment" {
scope = azurerm_storage_account.storage_account.id
role_definition_id = join("/", [local.sub, data.azurerm_subscription.primary.subscription_id, local.permission_storage_blob_data_contributor])
principal_id = var.devops_project_object_id
}
...
variables.tf
...
variable "location" {
type = string
description = "Location for the deployment"
default = "West Europe"
}
variable "resource_group_name" {
type = string
description = "Resource Group Name"
}
variable "storage_account_name" {
type = string
description = "Storage Account Name"
}
# yyyyyyyy-yyyy-yyyy-yyyyyyyyyyyy format
variable "devops_project_object_id" {
type = string
description = "Object ID (principal_id) for the Devops Project linked to the Azure Subscription in the Azure Active Directory."
}
...
Role assignment can be simplified to this call:
resource "azurerm_role_assignment" "blob_contributor" {
scope = azurerm_storage_account.storage_account.id
role_definition_name = "Storage Blob Data Contributor"
principal_id = var.devops_project_object_id
}

Terraform-Azure: Grant Access to azure resource for group

Experts,
I have a situation where I have to grant access on multiple Azure resources to a particular group, and i have to do this using Terraform only.
example:
Azure Group Name: India-group (5-6 users is there in this group)
Azure Subscription name: India
Azure Resource SQL Database: SQL-db-1
Azure Resource Key-Vault: India-key-vlt-1
Azure Resource Storage Account: India-acnt-1
and many more like PostgreSQL, storage account, blob.....
I think you do not need to care about how does the resource group can access the resources. What you need to care about is how to access the resources when it's necessary.
Generally, we use the service principal that assign roles that contain appropriate permission to access the resources. You can take a look at What is role-based access control (RBAC) for Azure resources and Create a service principal via CLI.
In Terraform, I assume you want to get the secrets from the KeyVault. Here is an example:
provider "azurerm" {
features {}
}
resource "azuread_application" "example" {
name = "example"
homepage = "http://homepage"
identifier_uris = ["http://uri"]
reply_urls = ["http://replyurl"]
available_to_other_tenants = false
oauth2_allow_implicit_flow = true
}
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
app_role_assignment_required = false
tags = ["example", "tags", "here"]
}
resource "azurerm_resource_group" "example" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_key_vault" "example" {
name = "testvault"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
enabled_for_disk_encryption = true
tenant_id = var.tenant_id
soft_delete_enabled = true
purge_protection_enabled = false
sku_name = "standard"
access_policy {
tenant_id = var.tenant_id
object_id = azuread_service_principal.example.object_id
key_permissions = [
"get",
]
secret_permissions = [
"get",
]
storage_permissions = [
"get",
]
}
network_acls {
default_action = "Deny"
bypass = "AzureServices"
}
tags = {
environment = "Testing"
}
}
Then you can access the key vault to get the secrets or keys through the service principal. You can also take a look at the example that controls Key Vault via python.
For other resources, you need to learn about the resource itself first, and then you can know how to access it in a suitable way. Finally, you can use Terraform to achieve it.

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