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.
Related
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
}
I have following code to create azure key vault:
resource "azurerm_key_vault" "key_vault" {
name = "example"
location = var.location
resource_group_name = azurerm_resource_group.resource_group.name
sku_name = "standard"
tenant_id = var.tenant_id
access_policy {
tenant_id = var.tenant_id
object_id = azurerm_user_assigned_identity.user_assigned_identity.principal_id
secret_permissions = [
"get",
]
}
}
Assume following scenario:
deploy infrastructure using terraform
change key vault access policies manually
redeploy using terraform
Terraform will remove access policies that were created manually.
Is there a way to tell terraform to not remove existing access policies?
The following terraform configuration is supposed to:
Obtain the id of the relevant Key Vault
Obtain the id of the certificate secret
Setup custom hostname binding
Setup app service certificate
data "azurerm_key_vault" "hosting_secondary_kv" {
name = local.ctx.HostingSecondaryKVName
resource_group_name = local.ctx.HostingSecondaryRGName
}
data "azurerm_key_vault_secret" "cert" {
name = var.env == "prod" ? local.ctx.ProdCertificateName : local.ctx.NonProdCertificateName
key_vault_id = data.azurerm_key_vault.hosting_secondary_kv.id
}
resource "azurerm_app_service_custom_hostname_binding" "webapp_fqdn" {
for_each = local.apsvc_map
hostname = each.value.fqdn
app_service_name = azurerm_app_service.webapp[each.key].name
resource_group_name = var.regional_web_rg[each.value.location].name
ssl_state = "SniEnabled"
thumbprint = azurerm_app_service_certificate.cert[each.value.location].thumbprint
depends_on = [
azurerm_traffic_manager_endpoint.ep
]
}
resource "azurerm_app_service_certificate" "cert" {
for_each = local.locations
name = var.env == "prod" ? local.ctx.ProdCertificateName : local.ctx.NonProdCertificateName
resource_group_name = var.regional_web_rg[each.value].name
location = each.value
key_vault_secret_id = data.azurerm_key_vault_secret.cert.id
}
I have configured all the permissions as explained in https://www.terraform.io/docs/providers/azurerm/r/app_service_certificate.html
Running the code yields the following error:
Error: Error creating/updating App Service Certificate "wildcard-np-xyzhcm-com" (Resource Group "MyAppServiceResourceGroup"): web.CertificatesClient#CreateOrUpdate: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="LinkedAuthorizationFailed" Message="The client '5...8' with object id '5...8' has permission to perform action 'Microsoft.Web/certificates/write' on scope '/subscriptions/0...7/resourceGroups/MyAppServiceResourceGroup/providers/Microsoft.Web/certificates/wildcard-np-xyzhcm-com'; however, it does not have permission to perform action 'write' on the linked scope(s) '/subscriptions/0...7/resourceGroups/MyKeyVaultResourceGroup/providers/Microsoft.KeyVault/vaults/MyKeyVault' or the linked scope(s) are invalid."
All the resources are in the same subscription.
I do not understand. Does Azure want me to grant the Service Principal performing the deployment (5...8) the 'write' permission on the key vault containing the certificate? What am I missing?
EDIT 1
I used terraform to create the access policy to the Key Vault. Here is the relevant code:
A custom role definition allowing the "Microsoft.KeyVault/vaults/read" action:
resource "azurerm_role_definition" "key_vault_reader" {
name = "Key Vault Reader"
scope = data.azurerm_subscription.current.id
permissions {
actions = ["Microsoft.KeyVault/vaults/read"]
not_actions = []
}
assignable_scopes = [
data.azurerm_subscription.current.id
]
}
Letting the Microsoft WebApp Service Principal access the certificate:
data "azurerm_key_vault" "hosting_secondary_kv" {
name = local.ctx.HostingSecondaryKVName
resource_group_name = local.ctx.HostingSecondaryRGName
}
data "azuread_service_principal" "MicrosoftWebApp" {
application_id = "abfa0a7c-a6b6-4736-8310-5855508787cd"
}
resource "azurerm_key_vault_access_policy" "webapp_sp_access_to_hosting_secondary_kv" {
key_vault_id = data.azurerm_key_vault.hosting_secondary_kv.id
object_id = data.azuread_service_principal.MicrosoftWebApp.object_id
tenant_id = data.azurerm_subscription.current.tenant_id
secret_permissions = ["get"]
certificate_permissions = ["get"]
}
Next grant the Service Principal used by the deployment the custom Key Vault Reader role in the resource group of the respective Key Vault:
data "azurerm_key_vault" "hosting_secondary_kv" {
name = local.ctx.HostingSecondaryKVName
resource_group_name = local.ctx.HostingSecondaryRGName
}
data "azurerm_role_definition" "key_vault_reader" {
name = "Key Vault Reader"
scope = data.azurerm_subscription.current.id
}
resource "azurerm_role_assignment" "sp_as_hosting_secondary_kv_reader" {
scope = "${data.azurerm_subscription.current.id}/resourceGroups/${local.ctx.HostingSecondaryRGName}"
role_definition_id = data.azurerm_role_definition.key_vault_reader.id
principal_id = azuread_service_principal.sp.id
}
Finally setup the access policy for the aforementioned Service Principal:
resource "azurerm_key_vault_access_policy" "sp_access_to_hosting_secondary_kv" {
key_vault_id = data.azurerm_key_vault.hosting_secondary_kv.id
object_id = azuread_service_principal.sp.object_id
tenant_id = data.azurerm_subscription.current.tenant_id
secret_permissions = ["get"]
certificate_permissions = ["get"]
}
And the snapshots from the portal:
So we have discussed it with the Microsoft Support and the solution they have provided is that we can use a custom Role Definition based on the built-in Reader role + Key Vault deploy action.
The terraform role definition looks like this:
resource "random_uuid" "reader_with_kv_deploy_id" {}
resource "azurerm_role_definition" "reader_with_kv_deploy" {
role_definition_id = random_uuid.reader_with_kv_deploy_id.result
name = "Key Vault Reader with Action for ${var.sub}"
scope = data.azurerm_subscription.current.id
description = "Can deploy/import secret from key vault to Web App"
permissions {
actions = ["*/read", "Microsoft.KeyVault/vaults/deploy/action"]
not_actions = []
}
assignable_scopes = [
data.azurerm_subscription.current.id
]
}
Anyway, using this role instead of "Key Vault Contributor" does allow to link an App Service to a certificate in a Key Vault.
Those two questions remain:
Why on earth this complication is even necessary and just Reader was not deemed good enough?
Why there is no built-in role for this? I cannot believe anyone would agree to grant a service principal Key Vault Contributor where a mere Reader should be enough.
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.
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}"
}