Azure devops pipeline terraform error - 403 when attempting role assignment - azure

I'm attempting to deploy an aks cluster and role assignment for the system assigned managed identity that is created via terraform but I'm getting a 403 response
azurerm_role_assignment.acrpull_role: Creating...
╷
│ Error: authorization.RoleAssignmentsClient#Create: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client '626eac40-c9dd-44cc-a528-3c3d3e069e85' with object id '626eac40-c9dd-44cc-a528-3c3d3e069e85' does not have authorization to perform action 'Microsoft.Authorization/roleAssignments/write' over scope '/subscriptions/7b73e02c-dbff-4eb7-9d73-e73a2a17e818/resourceGroups/myaks-rg/providers/Microsoft.ContainerRegistry/registries/aksmattcloudgurutest/providers/Microsoft.Authorization/roleAssignments/c144ad6d-946f-1898-635e-0d0d27ca2f1c' or the scope is invalid. If access was recently granted, please refresh your credentials."
│
│ with azurerm_role_assignment.acrpull_role,
│ on main.tf line 53, in resource "azurerm_role_assignment" "acrpull_role":
│ 53: resource "azurerm_role_assignment" "acrpull_role" {
│
╵
This is only occurring in an Azure Devops Pipeline. My pipeline looks like the following...
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: TerraformInstaller#0
inputs:
terraformVersion: '1.0.7'
- task: TerraformCLI#0
inputs:
command: 'init'
workingDirectory: '$(System.DefaultWorkingDirectory)/Shared/Pipeline/Cluster'
backendType: 'azurerm'
backendServiceArm: 'Matt Local Service Connection'
ensureBackend: true
backendAzureRmResourceGroupName: 'tfstate'
backendAzureRmResourceGroupLocation: 'UK South'
backendAzureRmStorageAccountName: 'tfstateq7nqv'
backendAzureRmContainerName: 'tfstate'
backendAzureRmKey: 'terraform.tfstate'
allowTelemetryCollection: true
- task: TerraformCLI#0
inputs:
command: 'plan'
workingDirectory: '$(System.DefaultWorkingDirectory)/Shared/Pipeline/Cluster'
environmentServiceName: 'Matt Local Service Connection'
allowTelemetryCollection: true
- task: TerraformCLI#0
inputs:
command: 'validate'
workingDirectory: '$(System.DefaultWorkingDirectory)/Shared/Pipeline/Cluster'
allowTelemetryCollection: true
- task: TerraformCLI#0
inputs:
command: 'apply'
workingDirectory: '$(System.DefaultWorkingDirectory)/Shared/Pipeline/Cluster'
environmentServiceName: 'Matt Local Service Connection'
allowTelemetryCollection: false
I'm using the terraform tasks from here - https://marketplace.visualstudio.com/items?itemName=charleszipp.azure-pipelines-tasks-terraform
This is my terraform file
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.46.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "TerraformCluster" {
name = "terraform-cluster"
location = "UK South"
}
resource "azurerm_kubernetes_cluster" "TerraformClusterAKS" {
name = "terraform-cluster-aks1"
location = azurerm_resource_group.TerraformCluster.location
resource_group_name = azurerm_resource_group.TerraformCluster.name
dns_prefix = "terraform-cluster-aks1"
network_profile {
network_plugin = "azure"
}
default_node_pool {
name = "default"
node_count = 1
vm_size = "Standard_D2_v2"
}
identity {
type = "SystemAssigned"
}
tags = {
Environment = "Production"
}
}
data "azurerm_container_registry" "this" {
depends_on = [
azurerm_kubernetes_cluster.TerraformClusterAKS
]
provider = azurerm
name = "aksmattcloudgurutest"
resource_group_name = "myaks-rg"
}
resource "azurerm_role_assignment" "acrpull_role" {
scope = data.azurerm_container_registry.this.id
role_definition_name = "AcrPull"
principal_id = azurerm_kubernetes_cluster.TerraformClusterAKS.identity[0].principal_id
}
Where am I going wrong here?

The Service Principal in AAD associated with the your ADO Service Connection ('Matt Local Service Connection') will need to be assigned the Owner role at the scope of the resource, or above (depending on where else you will be assigning permissions). You can read details about the various roles here the two most commonly used roles are Owner and Contributor, the key difference being that Owner allows for managing role assignments.
As part of this piece of work, you should also familiarize yourself with the principle of least privilege (if you do not already know about it). How it would apply in this case would be; if the Service Principal only needs Owner at the Resource level, then don't assign it Owner at the Resource Group or Subscription Level just because that is more convenient, you can always update the scope later on but it is much harder to undo any damage (assuming a malicious or inexperienced actor) on overly permissive role assignment after it has been exploited.

I tried everything to get this existing storage service to re-connect to the Azure Devops Pipeline to enable Terraform deployments.
Attempted and did not work: Break lease on tf state, remove tf state, update lease on tfstate, inline commands in ADO via powershell and bash to Purge terraform, re-install the Terraform plugin etc. etc. etc.)
What worked:
What ended up working is to create a new storage account with a new storage container and new SAS token.
This worked to overcome the 403 forbidden error on the access of the Blob containing the TFState in ADLS from Azure Devops for Terraform deployments. This does not explain the how or the why, access controls / iam / access policies did not change. A tear down and recreate of the storage containing the TFState with the exact same settings under a difference storage account name worked.

Related

Error while assigning admin role to SCIM provisioned AAD groups in Databricks

We use Azure databricks and managing via terraform. We have configured SCIM connector provisioner(AAD Enterprise app) to sync users and groups from AAD to Databricks. This works good. I can able to assign job or cluster permissions to these SCIM synced groups but when I try to assign admin role(entire workspace admin) to SCIM synced group the terraform error shows "API is not available for this worspace". Sorry, I don't what it means, Is it related to terraform provider or Am I putting something wrong? Please suggest me what should I use or correct. Please find below code 'principal_id' argument accepts user id or group id or service principal id as per terraform documentation here https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/permission_assignment#principal_id
Provider configuration:
terraform {
required_version = ">= 1.1.4"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.8.0"
}
databricks = {
source = "databricks/databricks"
version = ">= 1.6.3"
}
}
}
provider "databricks" { #Assign databricks workspace id to provider
azure_workspace_resource_id =
data.azurerm_databricks_workspace.adb_ws.id
}
Resource Block:
resource "databricks_permission_assignment" "assign_scim_admingroup" {
principal_id = data.databricks_group.dbricks_admin_group.id
permissions = ["ADMIN"]
}
Error in terraform:
│ Error: cannot create permission assignment: Permission assignment APIs are not available for this workspace.
│
│ with databricks_permission_assignment.assign_scim_admingroup,
│ on Dbricks-permission.tf line 104, in resource "databricks_permission_assignment" "assign_scim_admingroup":
│ 43: resource "databricks_permission_assignment" "assign_scim_admingroup" {
My expectation is Databricks group synced with AAD via SCIM connecter provisioner groups should be assigned as "ADMIN" role using terraform.
I tried to reproduce the same in my environment:
Code:
resource "azuread_group" "example" {
display_name = "kavyaMyGroup"
owners = [data.azuread_client_config.current.object_id]
security_enabled = true
members = [
azuread_user.example.object_id,
# more users
]
}
resource "databricks_user" "me" {
// user_name = "testuser#databricks.com"
user_name = azuread_user.example.user_principal_name
display_name = "Test User"
}
resource "databricks_group" "this" {
// display_name = "vsakaSomeGroup"
display_name = azuread_group.example.display_name
allow_cluster_create = true
allow_instance_pool_create = true
workspace_access = true
databricks_sql_access = true
}
resource "databricks_group_member" "vip_member" {
group_id = databricks_group.this.id
member_id = databricks_user.me.id
}
I received the same error:
│ Error: cannot create permission assignment: Permission assignment APIs are not available for this workspace.
│
│ with databricks_permission_assignment.assign_scim_admingroup,
│ on main.tf line 145, in resource "databricks_permission_assignment" "assign_scim_admingroup":
Please note :
The admins group is a reserved group in Azure Databricks and cannot be
removed. Note that Workspace-local groups cannot be granted
access to data in a Unity Catalog metastore or assigned to other
workspaces .
To add groups to a workspace using the account console,
the workspace must be enabled for identity federation. Only account-level groups are assignable.
Manage groups - Azure Databricks | Microsoft Learn
If I checked my environment, the group created is local and not
account level , so I did not have permissions to assign admin role to
it.
The account admins can assign them using ,the principal ID which can be retrieved using the SCIM API.
resource "databricks_permission_assignment" "assign_scim_admingroup" {
/principal_id = databricks_group.this.id
permissions = ["ADMIN"]
}
Make sure to enable identity federation , to assign group roles and have premium plan in order to manage the assignment of users to workspaces
You can also check Automate SCIM provisioning using Microsoft Graph from
Reference: Configure SCIM provisioning using Microsoft Azure Active Directory - Azure Databricks | Microsoft Learn

Why am I getting this 404 Authorization error on Terraform OCI?

Trying to provision a network in OCI and I'm getting this same error for every single subnet even though the "terraform plan" is successful with no issues. Anybody know what the problem is here?
Error: 404-NotAuthorizedOrNotFound, Authorization failed or requested resource not found.
Suggestion: Either the resource has been deleted or service Core Subnet need policy to access this resource. Policy reference: https://docs.oracle.com/en-us/iaas/Content/Identity/Reference/policyreference.htm
Documentation: https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_subnet
API Reference: https://docs.oracle.com/iaas/api/#/en/iaas/xxxxxxxx/Subnet/CreateSubnet
Request Target: POST https://iaas.us-ashburn-1.oraclecloud.com/xxxxxxxx/subnets
Provider version: 4.88.1, released on 2022-08-11.
Service: Core Subnet
Operation Name: CreateSubnet
OPC request ID: xx/xx/xx
on .terraform/modules/network/main-region2.tf line 899, in resource "oci_core_subnet" "subnets_sec":
899: resource "oci_core_subnet" "subnets_sec" {
This is what I'm using for my subnet resource block:
resource "oci_core_subnet" "subnets_sec" {
// Description: a subnet will be created for each key within the subnet_params_sec variable
provider = oci.region_sec
for_each = var.subnet_params_sec
display_name = each.key
compartment_id = oci_core_virtual_network.vcn_sec[each.value.vcn_name].compartment_id
vcn_id = oci_core_virtual_network.vcn_sec[each.value.vcn_name].id
cidr_block = each.value.cidr_block
dns_label = each.value.dns_label
dhcp_options_id = lookup(var.dhcp_params_sec, each.key, null) != null ? oci_core_dhcp_options.default_dhcp_vcnres_sec[each.value.vcn_name].id : ""
prohibit_public_ip_on_vnic = each.value.is_subnet_private
route_table_id = each.value.rt_name
}
Last important piece of info is that this was being deployed to an empty compartment so I don't know how a resource can be missing or deleted.

Bootstraping an Azure service account in Terraform

I am trying to write the Terraform to create an Azure "service account" and am getting quite confused by the distinction between what Azure AD calls "Applications" and "Service Principals". Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
The idea would be for a human administrator to run the Terraform, to set it up once, then those credentials could later be used to authenticate for the remaining IaC. (i.e., It's a bootstrapping exercise.)
I wish to do it in Terraform, rather than a Bash script, as it seems more explicit and fits with the rest of my IaC. This is what I have so far:
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Terraform"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
# resource "azuread_service_principal" "terraform" {
# application_id = azuread_application.terraform.application_id
# owners = [data.azuread_client_config.current.object_id]
# }
#
# resource "azuread_service_principal_password" "terraform" {
# service_principal_id = azuread_service_principal.terraform.object_id
# }
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
This runs, but later authenticating with the generated credentials gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal is properly created in the tenant.
Clearly I haven't done that -- it's commented out in the above snippet -- but that's because this is where my understanding starts to break down. Why do I need both? Why do both the application and the service principal have password resources? If I generate passwords for both, which is the ARM_CLIENT_SECRET (I think the application password is the right one)? Then there's the role assignment: I see there's an azuread_app_role_assignment resource, but I'm having trouble unpicking it.
I am trying to write the Terraform to create an Azure "service
account" and am getting quite confused by the distinction between what
Azure AD calls "Applications" and "Service Principals".
Applications can be seen from Azure AD App registrations blade Where as Service Principals are other wise know as Enterprise Applications. The difference is well documented in this Microsoft Documentation.
This runs, but later authenticating with the generated credentials
gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal
is properly created in the tenant.
This is because you have no associated service principal to that azure ad application which you have created from Terraform. The association is needed to access the application or authenticating to the azure environment with contributor role. When a App registration is created from portal it by default creates an association of AD app and Service principal , which by default results in creating a service principal for that app registration. It also applies the same concept when we use az ad sp create-for-rbac.
Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
You can use the below to mimic the above command :
provider "azurerm" {
features{}
}
provider "azuread" {}
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Ansumantest"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
resource "azuread_service_principal" "terraform" {
application_id = azuread_application.terraform.application_id
owners = [data.azuread_client_config.current.object_id]
}
resource "azurerm_role_assignment" "example" {
scope = data.azurerm_subscription.current.id
role_definition_name = "Contributor"
principal_id = azuread_service_principal.terraform.object_id
}
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
Output :
Using the above details I created a reosurce group in the subscription :
provider "azurerm" {
features{}
subscription_id = "88073b30-cadd-459e-b90b-8442c93573ae"
tenant_id = "ab078f81-xxxx-xxxx-xxxx-620b694ded30"
client_id = "c022ec46-xxxx-xxxx-xxxx-c72a9b82f429"
client_secret = "wdV7Q~8Grxxxxxxxxxxxxxx~SCwbRrKIq9"
}
resource "azurerm_resource_group" "name" {
name = "testansterraform"
location = "west us 2"
}

unable to execute terraform apply in azure devops

I am trying to execute terraform scripts through azure devops. I am not able to apply and validate through different tasks, though terraform plan is successful terraform apply is failing with
##[error]TypeError: Cannot read property 'includes' of null
Here is the terraform tasks which I am using. I have tried with two different tasks
1.
- task: ms-devlabs.custom-terraform-tasks.custom-terraform-release-task.TerraformTaskV2#2
displayName: 'Terraform : apply -auto-approve'
inputs:
command: apply
workingDirectory: '$(System.DefaultWorkingDirectory)/Terraform'
commandOptions: '-auto-approve'
environmentServiceNameAzureRM: 'ps-vs-sc'
backendAzureRmResourceGroupName: '$(rgname)'
backendAzureRmStorageAccountName: $(strname)
backendAzureRmContainerName: $(tfContainer)
backendAzureRmKey: '$(storagekey)'
- task: TerraformTaskV2#2
inputs:
provider: 'azurerm'
command: 'apply'
workingDirectory: '$(System.DefaultWorkingDirectory)/Terraform'
commandOptions: '--auto-approve'
environmentServiceNameAzureRM: 'ps-vs1-sc'
Here is my terraform file
provider "azurerm" {
features {}
}
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.74.0"
}
}
}
data "azurerm_api_management" "example" {
name = var.apimName
resource_group_name = var.rgName
}
resource "azurerm_api_management_api" "example" {
name = var.apName
resource_group_name = var.rgName
api_management_name = var.apimname
revision = "1"
display_name = "Example API1"
path = "example1"
protocols = ["https"]
service_url = "http://123.0.0.0:8000"
subscription_required = true
import {
content_format = "openapi+json"
content_value = #{storageaccountlink}#
}
The terraform file looks fine, there is no issue with it. Can you check if you are using the Azure Service Principal method.
A Service Principal is considered a good practice for DevOps within your CI/CD pipeline. It is used as an identity to authenticate you within your Azure Subscription to allow you to deploy the relevant Terraform code.
You can follow this document for the detailed demo for Deploying Terraform using Azure DevOps.
If you have already followed the above steps and still facing the same issue then the problem is with the the 'Configuration directory' setting in the Terraform 'Validate and Apply' Release Pipeline step. Changing it to the path containing the build artifacts will fix the issue. Also check if the workingDirectory which you have provided is correct.
A similar but not same problem was also raised here, check if it solves the problem.
I have added the entire pipeline details here just for the reference - https://gist.github.com/PrakashRajanSakthivel/e6d8e03044a51d74803499aca75a258c

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