How to use MSAL Python library to create an Azure Activity log - azure

I am trying to use MSAL Python library to create an alert in Azure Activity Log. Here is my code:
from azure.identity import ClientSecretCredential
from azure.mgmt.monitor import MonitorManagementClient
MSAL_CLIENT_ID = "<My Client Id>"
MSAL_CLIENT_SECRET = "<My Client Secret>"
TENANT_ID = "<My Tenant Id>"
credentials = ClientSecretCredential(
client_id = MSAL_CLIENT_ID,
client_secret = MSAL_CLIENT_SECRET,
tenant_id = TENANT_ID
)
SUBSCRIPTION_ID = "<My Subscription Id>"
monitor_client = MonitorManagementClient(
credential=credentials,
subscription_id=SUBSCRIPTION_ID
)
GROUP_NAME = "<My Resource Group Name>"
ACTIVITY_LOG_ALERT_NAME = "test"
log_alert = monitor_client.activity_log_alerts.create_or_update(
GROUP_NAME,
ACTIVITY_LOG_ALERT_NAME,
{
"location": "Global",
"scopes": [
"subscriptions/" + SUBSCRIPTION_ID
],
"enabled": True,
"condition": {
"all_of": [
{
"field": "category",
"equals": "Administrative"
},
{
"field": "level",
"equals": "Error"
}
]
},
"actions": {
"action_groups": [
]
},
"description": "Sample activity log alert description"
}
)
print("Create activity log alert:\n{}".format(log_alert))
But I got the following error
HttpResponseError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_24988\3130136741.py in
----> 1 log_alert = monitor_client.activity_log_alerts.create_or_update(
2 GROUP_NAME,
3 ACTIVITY_LOG_ALERT_NAME,
4 {
5 "location": "Global",
C:\ProgramData\Anaconda3\envs\hlgdev\lib\site-packages\azure\core\tracing\decorator.py in wrapper_use_tracer(*args, **kwargs)
76 span_impl_type = settings.tracing_implementation()
77 if span_impl_type is None:
---> 78 return func(*args, **kwargs)
79
80 # Merge span is parameter is set, but only if no explicit parent are passed
C:\ProgramData\Anaconda3\envs\hlgdev\lib\site-packages\azure\mgmt\monitor\v2020_10_01\operations_activity_log_alerts_operations.py in create_or_update(self, resource_group_name, activity_log_alert_name, activity_log_alert_rule, **kwargs)
381 map_error(status_code=response.status_code, response=response, error_map=error_map)
382 error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
--> 383 raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)
384
385 if response.status_code == 200:
HttpResponseError: (AuthorizationFailed) The client '17abcd' with object id '17abcd' does not have authorization to perform action 'Microsoft.Insights/activityLogAlerts/write' over scope '/subscriptions/59abcd/resourceGroups/MyResource/providers/Microsoft.Insights/activityLogAlerts/test' or the scope is invalid. If access was recently granted, please refresh your credentials.
Code: AuthorizationFailed
Message: The client '17abcd'' with object id '17abcd'' does not have authorization to perform action 'Microsoft.Insights/activityLogAlerts/write' over scope '/subscriptions/59abcd/resourceGroups/MyResource/providers/Microsoft.Insights/activityLogAlerts/test' or the scope is invalid. If access was recently granted, please refresh your credentials.
Please help. Thank you very much!

I tried running the below code with a Service Principal with an Owner or Contributor role at the Subscription level the code ran successfully like below:-
Service Principal role at the subscription level :-
Now, I tried creating the same Activity log with another Service Principal without the Owner or contributor role but just the reader role. It failed with the same error code as yours:-
Output failed:-
I added the same service principal with a Contributor role scoped at the resource group level and the error was resolved:-
Output ran successfully:-
Make sure your app/service principal have at least Contributor or Owner role assigned at the Subscription or Resource Group Level in your Azure Subscription.

Related

Azure : GroupsClient.BaseClient.Get(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges

I’m trying to create the Azure AD Group using the following terraform code
# Required Provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0.2"
}
}
required_version = ">= 1.1.0"
}
# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
....
....
}
data "azuread_client_config" "current" {}
# Variables
variable "ad_groups" {
description = "Azure AD groups to be added"
type = list(object({
display_name = string,
description = string
}))
default = [
{
display_name = "Group1"
description = "some description"
},
{
display_name = "Group2"
description = "some description"
}
]
}
# Create AD Groups and add the Current User
resource "azuread_group" "this"{
count = length(var.ad_groups)
display_name = var.ad_groups[count.index].display_name
description = var.ad_groups[count.index].description
security_enabled = true
prevent_duplicate_names = true
owners = [data.azuread_client_config.current.object_id]
}
and I am getting the following error
**Error:** could not check for existing group(s): unable to list Groups with filter "displayName eq 'Group1'": GroupsClient.BaseClient.Get(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to complete the operation.
This service principal has the following roles at the Management group level
Does it need both the Directory.ReadWrite.All and Group.ReadWrite.All API Permissions? If not, what access does it need?
Note: If I disable the "prevent_duplicate_names = true" and apply the terraform, it throws the following error
GroupsClient.BaseClient.Post(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to
│ complete the operation.
I tried to reproduce the same in my environment via Postman and got below results:
By default, newly created application will have User.Read API permission already added to it.
I registered one new Azure AD application named GroupSP and has API permission like below:
Without adding any extra API permission, I generated one access token using client credentials flow via Postman like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:<appID>
grant_type:client_credentials
client_secret:<secret>
scope: https://graph.microsoft.com/.default
Response:
When I used the above token to create Azure AD group with owner, I got same error as below:
POST https://graph.microsoft.com/v1.0/groups
Content-Type: application/json
{
"description": "Group with designated owner",
"displayName": "Group1",
"groupTypes": [ ],
"mailEnabled": false,
"mailNickname": "srigroup",
"securityEnabled": true,
"owners#odata.bind": [
"https://graph.microsoft.com/v1.0/users/<userID>"
]
}
Response:
To resolve the error, I added Directory.ReadWrite.All API permission to the service principal like below:
After granting admin consent to above permission, I generated access token again and ran the same query and got response successfully as below:
POST https://graph.microsoft.com/v1.0/groups
Content-Type: application/json
{
"description": "Group with designated owner",
"displayName": "Group1",
"groupTypes": [ ],
"mailEnabled": false,
"mailNickname": "srigroup",
"securityEnabled": true,
"owners#odata.bind": [
"https://graph.microsoft.com/v1.0/users/<userID>"
]
}
Response:
To confirm that, I checked the Portal where Azure AD group is created, and owner added successfully like below:
You can also check Audit logs of that created group like below:
In your case, make sure to add Directory.ReadWrite.All API permission to your service principal that resolves 403 Forbidden error.
If Directory.ReadWrite.All permission is added to the service principal, Group.ReadWrite.All permission is not required.

403 Error: Create & Assign Azure Policy Definition at Management Group Level using Terraform

provider "azurerm" {
features {}
}
data "azurerm_management_group" "management_group" {
display_name = var.management_group_display_name
}
resource "azurerm_policy_definition" "deployment_policy_definition" {
name = "resources-in-eastus-policy"
policy_type = "Custom"
mode = "All"
display_name = "Allowed to only deploy in East US location"
management_group_id = data.azurerm_management_group.management_group.id
policy_rule = <<POLICY_RULE
{
"if": {
"not": {
"field": "location",
"in": "[parameters('allowedLocations')]"
}
},
"then": {
"effect": "audit"
}
}
POLICY_RULE
parameters = <<PARAMETERS
{
"allowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of allowed locations for resources.",
"displayName": "Allowed locations",
"strongType": "location"
}
}
}
PARAMETERS
}
resource "azurerm_management_group_policy_assignment" "mngmt_grp_dep_pol_assign" {
name = "assign-pol-to-mgmt-grp"
policy_definition_id = azurerm_policy_definition.deployment_policy_definition.id
management_group_id = data.azurerm_management_group.management_group.id
parameters = <<PARAMETERS
{
"allowedLocations": {
"value": [ "eastus" ]
}
}
PARAMETERS
}
Error: creating/updating Policy Definition "resources-in-eastus-policy": policy.DefinitionsClient#CreateOrUpdateAtManagementGroup: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client 'live.com#XXX#gmail.com' with object id '0ab7dad7-dba2-46d9-8cc6-878647e9a5cb' does not have authorization to perform action 'Microsoft.Management/managementGroups/Microsoft.Management/1/Microsoft.Authorization/resources-in-eastus-policy/write' over scope '/providers/Microsoft.Management/managementGroups/providers/Microsoft.Management/managementGroups/1/providers/Microsoft.Authorization/policyDefinitions' or the scope is invalid. If access was recently granted, please refresh your credentials."
Azure Roles added for the owner/user of the azure-cli
The ID of the target management group where I am trying to create and assign the policy under the Tenant Root Group is 1
Error:
The client 'live.com#XXX#gmail.com' with object id
'0ab7daxxxxxxx-xxxxe9a5cb' does not have authorization to perform
action
'Microsoft.Management/managementGroups/Microsoft.Management/1/Microsoft.Authorization/resources-in-eastus-policy/write
As the error mentions the client doesn’t have proper RBAC role to perform policy definition creation on management groups.
Try to assign that ObjectId mentioned in the error , the proper role like Management Group Contributor OR Management Group Reader role.
Note: The principal/user which is deploying ,must have permissions like Contributor to create resources at the tenant scope and to assign that permission one must have Owner role
Also see below table from management-group-access :
From the management group , Go to Access control (IAM), add your client(user/service principal) as an RBAC role
or provide role through powershell:
New-AzRoleAssignment -Scope '/' -RoleDefinitionName 'Owner' -ObjectId <objectidofftheclient>
Then wait for some time for the role to reflect and then try to create policy assignment to management group:
Policy assignment made to management group.
Please make sure if the management group is reflected properly and check the id is correct in terraform, if it is already created in portal. Else import them using terraform import and then perform terraform operations.

How can I fix Authorization_RequestDenied: Insufficient privileges to complete the operation?

I've created a service principal with an Owner role:
➜ ~ az account set --subscription="47a..."
➜ ~ az ad sp create-for-rbac --role="Owner" --scopes="/subscriptions/47a...
and then I copied
{
"appId": "bc8a...",
"displayName": "azure-cli-2022-04-25-23-55-25",
"password": "...",
"tenant": "..."
}
the params to auth for azuread TF Provider that has the following code:
data "azuread_service_principal" "peering_creator" {
# Harcoded custom client_id
application_id = "abc..."
}
and get
Error: Listing service principals for filter "appId eq '...'"
on main.tf line 248, in data "azuread_service_principal" "peering_creator":
248: data "azuread_service_principal" "peering_creator" {
ServicePrincipalsClient.BaseClient.Get(): unexpected status 403 with OData
error: Authorization_RequestDenied: Insufficient privileges to complete the
operation.

How to edit Azure App Service using Azure Python SDK?

I'm following the example here https://learn.microsoft.com/en-us/azure/developer/python/azure-sdk-example-web-app?tabs=cmd#4-write-code-to-provision-and-deploy-a-web-app trying to update an existing App Service using the Python SDK.
Here is my code
from azure.mgmt.web import WebSiteManagementClient
from azure.common.client_factory import get_client_from_cli_profile
import os
def insert_access_restriction():
rg_name = os.environ.get('RESOURCE_GROUP_NAME', None)
location = os.environ.get('LOCATION', None)
sp_name = os.environ.get('SERVICE_PLAN_NAME', None)
web_app_name = os.environ.get('WEB_APP_NAME', None)
sub_id = os.environ.get('AZURE_SUBSCRIPTION_ID', None)
app_service_client = get_client_from_cli_profile(WebSiteManagementClient)
poller = app_service_client.app_service_plans.create_or_update(rg_name,
sp_name,
{
"location": location,
"reserved": True,
"sku" : {"name" : "S1"}
}
)
plan_result = poller.result()
poller = app_service_client.web_apps.create_or_update(rg_name,
web_app_name,
{
"location": location,
"server_farm_id": plan_result.id,
"site_config": {
"ip_restriction": {
"ip_address": "3.3.3.3/32"
},
"ip_restriction": {
"ip_address": "4.4.4.4/32"
}
}
}
)
The call to this function app_service_client.app_service_plans.create_or_update returns
azure.mgmt.web.v2019_08_01.models._models_py3.DefaultErrorResponseException:
Operation returned an invalid status code 'Bad Request'
My location is centralus. The goal of this program is to update the ip restrictions on an existing app service programmatically from a Function App when a new list of ip addresses is added to a storage container. The error is very vague, how do I get an existing app service plan, its app service, and then update the app service using the Python SDK?
if you want to update the ip restrictions settings on an existing app service with python sdk, please refer to the following code
Create a service principal and assign Contributor to the sp
az login
# create sp and assign Contributor role to the sp at subscription level
az ad sp create-for-rbac -n "MyApp"
Code
client_id = 'your sp appId'
secret = 'your sp password'
tenant = 'your sp tenant'
credentials = ServicePrincipalCredentials(
client_id = client_id,
secret = secret,
tenant = tenant
)
Subscription_Id = ''
web_client=WebSiteManagementClient(
credentials,
Subscription_Id
)
resorurce_group_name='your appservice group name'
name='you appservice name'
web_client.web_apps.create_or_update_configuration(resorurce_group_name, name,{
'ip_security_restrictions':[
{
'ip_address': "0.0.0.0/0",
'action': "Allow",
'priority': 30,
'name': "test"
}
]
})
for more details, please refer to here and here
#Update
If you want to run the script in Azure function, please refer to the following steps.
Create Azure function
Enable Azure MSI for Azure Function
Assign role fro the MSI
code (I use HTTP trigger to get web app configuration)
import logging
import pyodbc
import json
import azure.functions as func
from msrestazure.azure_active_directory import MSIAuthentication
from azure.mgmt.web import WebSiteManagementClient
async def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
creds =MSIAuthentication()
Subscription_Id = 'e5b0fcfa-e859-43f3-8d84-5e5fe29f4c68'
group_name='0730BowmanWindowAndLinux2'
name='413Bowman'
web_client=WebSiteManagementClient(
creds,
Subscription_Id
)
result =web_client.web_apps.get_configuration(group_name, name,raw=True)
return func.HttpResponse(json.dumps(result.response.json()))

Azure DevOps Server is unable to create an Azure resource using a Service Principal which is a Contributor to the subscription

I am following the tutorial Implementing Terraform on Microsoft Azure to the letter. So far so good and I have reached the module where terraform workspaces are used to create environments from the Azure DevOps CI/CD pipeline. The module name is "Using Azure DevOps"
The Azure DevOps project is public - https://dev.azure.com/MarkKharitonov0271/_git/Globomantics-testing
Running the workspaces release pipeline consistently fails for me:
2020-01-19T03:23:20.5420275Z Error: authorization.RoleDefinitionsClient#CreateOrUpdate: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client '0e648d2d-a49f-407e-99de-9d6343876a8c' with object id '0e648d2d-a49f-407e-99de-9d6343876a8c' does not have authorization to perform action 'Microsoft.Authorization/roleDefinitions/write' over scope '/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3' or the scope is invalid. If access was recently granted, please refresh your credentials."
2020-01-19T03:23:20.5420512Z
2020-01-19T03:23:20.5420989Z on vnet-peering.tf line 52, in resource "azurerm_role_definition" "vnet-peering":
2020-01-19T03:23:20.5421339Z 52: resource "azurerm_role_definition" "vnet-peering" {
(You can view the vnet-peering.tf in the project repo - https://dev.azure.com/MarkKharitonov0271/_git/Globomantics-testing?path=%2Fnetworking%2Fvnet-peering.tf)
Anyway, if I understand the error message correctly, it claims that the Service Principal 0e648d2d-a49f-407e-99de-9d6343876a8c does not have the rights to create a new role definition in the subscription 2b38509c-a310-4c8f-bd78-9e400cc874e3
So, I checked the Service Principal:
PS /home/mark> az ad sp show --id '0e648d2d-a49f-407e-99de-9d6343876a8c'
{
"accountEnabled": "True",
"addIns": [],
"alternativeNames": [],
"appDisplayName": "MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3",
"appId": "0ae4ffc7-149d-45ac-ab15-c9f61e4591f8",
"appOwnerTenantId": "717e5a4d-529c-4ab2-a1c5-6a5f6345d8e4",
"appRoleAssignmentRequired": false,
"appRoles": [],
"applicationTemplateId": null,
"deletionTimestamp": null,
"displayName": "MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3",
"errorUrl": null,
"homepage": "https://VisualStudio/SPN",
"informationalUrls": {
"marketing": null,
"privacy": null,
"support": null,
"termsOfService": null
},
"keyCredentials": [],
"logoutUrl": null,
"notificationEmailAddresses": [],
"oauth2Permissions": [
{
"adminConsentDescription": "Allow the application to access MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3 on behalf of the signed-in user.",
"adminConsentDisplayName": "Access MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3",
"id": "d0f141b9-fc6b-4f3c-9217-018d74712ee1",
"isEnabled": true,
"userConsentDescription": "Allow the application to access MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3 on your behalf.",
"userConsentDisplayName": "Access MarkKharitonov0271-Globomantics-testing-2b38509c-a310-4c8f-bd78-9e400cc874e3",
}
],
"objectId": "0e648d2d-a49f-407e-99de-9d6343876a8c",
"objectType": "ServicePrincipal",
"odata.metadata": "https://graph.windows.net/717e5a4d-529c-4ab2-a1c5-6a5f6345d8e4/$metadata#directoryObjects/#Element",
"odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
"passwordCredentials": [],
"preferredSingleSignOnMode": null,
"preferredTokenSigningKeyEndDateTime": null,
"preferredTokenSigningKeyThumbprint": null,
"publisherName": "Default Directory",
"replyUrls": [
"https://VisualStudio/SPN"
],
"samlMetadataUrl": null,
"samlSingleSignOnSettings": null,
"servicePrincipalNames": [
"https://VisualStudio/SPN136d4f76-7262-4ab0-8fbb-7be74dfc803b",
"0ae4ffc7-149d-45ac-ab15-c9f61e4591f8"
],
"servicePrincipalType": "Application",
"signInAudience": "AzureADMyOrg",
"tags": [],
"tokenEncryptionKeyId": null
}
Notice that the Service Principal has appId equal to 0ae4ffc7-149d-45ac-ab15-c9f61e4591f8.
And it seems to correspond to the one created by Azure DevOps when I added the Terraform tasks to the pipeline when it wanted to authorize access to the subscription. Indeed:
Now, the terraform apply step references the same service principal:
Finally, this Service Principal seems to have Contributor access to the subscription:
PS /home/mark> az role assignment list --assignee '0e648d2d-a49f-407e-99de-9d6343876a8c'
[
{
"canDelegate": null,
"id": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleAssignments/346f1b92-0621-44c0-b88a-343c52637a0f",
"name": "346f1b92-0621-44c0-b88a-343c52637a0f",
"principalId": "0e648d2d-a49f-407e-99de-9d6343876a8c",
"principalName": "https://VisualStudio/SPN136d4f76-7262-4ab0-8fbb-7be74dfc803b",
"principalType": "ServicePrincipal",
"roleDefinitionId": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
"roleDefinitionName": "Contributor",
"scope": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3",
"type": "Microsoft.Authorization/roleAssignments"
}
]
PS /home/mark>
So, I do not understand what is going on.
EDIT 1
So, I was able to run exactly the same terraform configuration with exactly the same Service Principal from the Azure Cloud Shell.
First I reset the credentials, logged out and logged in back as that Service Principal:
PS /home/mark/terraform> az ad sp credential reset --name https://VisualStudio/SPN136d4f76-7262-4ab0-8fbb-7be74dfc803b
{
"appId": "0ae4ffc7-149d-45ac-ab15-c9f61e4591f8",
"name": "https://VisualStudio/SPN136d4f76-7262-4ab0-8fbb-7be74dfc803b",
"password": "e...3",
"tenant": "717e5a4d-529c-4ab2-a1c5-6a5f6345d8e4"
}
PS /home/mark/terraform> az account clear
Logout successful. Re-login to your initial Cloud Shell identity with 'az login --identity'. Login with a new identity with 'az login'.
PS /home/mark/terraform> az login --service-principal -u https://VisualStudio/SPN136d4f76-7262-4ab0-8fbb-7be74dfc803b -p e...3 --tenant 717e5a4d-529c-4ab2-a1c5-6a5f6345d8e4
Cloud Shell is automatically authenticated under the initial account signed-in with. Run 'az login' only if you need to use a different account
{
"cloudName": "AzureCloud",
"id": "2b38509c-a310-4c8f-bd78-9e400cc874e3",
"isDefault": true,
"name": "Visual Studio Enterprise",
"state": "Enabled",
"type": "servicePrincipal"
}
}
]
Then I cloned the Git repository to obtain the Terraform code and copied over the backend configuration saved from the previous modules:
PS /home/mark/terraform> git clone https://MarkKharitonov0271#dev.azure.com/MarkKharitonov0271/Globomantics-testing/_git/Globomantics-testing
Cloning into 'Globomantics-testing'...
remote: Azure Repos
remote: We noticed you're using an older version of Git. For the best experience, upgrade to a newer version.
remote: Found 9 objects to send. (2 ms)
Unpacking objects: 100% (9/9), done.
Checking connectivity... done.
PS /home/mark/terraform> cd ./Globomantics-testing/networking/
PS /home/mark/terraform/Globomantics-testing/networking> dir
Directory: /home/mark/terraform/Globomantics-testing/networking
Mode LastWriteTime Length Name
---- ------------- ------ ----
------ 1/19/20 4:41 AM 40 backend.tf
------ 1/19/20 4:41 AM 2256 main.tf
------ 1/19/20 4:41 AM 436 terraform.tfvars
------ 1/19/20 4:41 AM 2423 vnet-peering.tf
------ 1/19/20 4:41 AM 279 workspacetest.sh
PS /home/mark/terraform/Globomantics-testing/networking> copy ../../1-main-vnet/backend-config.txt .
Now I am ready to initialize Terraform and set the development workspace:
PS /home/mark/terraform/Globomantics-testing/networking> terraform init -backend-config='backend-config.txt'
Initializing modules...
Downloading Azure/vnet/azurerm 1.2.0 for vnet-main...
- vnet-main in .terraform/modules/vnet-main
Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 1.41.0...
- Downloading plugin for provider "template" (hashicorp/template) 2.1.2...
...
PS /home/mark/terraform/Globomantics-testing/networking> terraform workspace select development
Switched to workspace "development".
Now terraform plan:
PS /home/mark/terraform/Globomantics-testing/networking> terraform plan -var sec_client_secret='F...$' -out main.tfplan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
data.template_file.subnet_prefixes[0]: Refreshing state...
data.template_file.subnet_prefixes[1]: Refreshing state...
data.template_file.subnet_prefixes[2]: Refreshing state...
azurerm_resource_group.main: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet]
data.azurerm_subscription.current: Refreshing state...
module.vnet-main.azurerm_resource_group.vnet: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet]
module.vnet-main.azurerm_virtual_network.vnet: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet]
module.vnet-main.azurerm_subnet.subnet[0]: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet/subnets/web]
module.vnet-main.azurerm_subnet.subnet[2]: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet/subnets/app]
module.vnet-main.azurerm_subnet.subnet[1]: Refreshing state... [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet/subnets/database]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_role_assignment.vnet will be created
+ resource "azurerm_role_assignment" "vnet" {
+ id = (known after apply)
+ name = (known after apply)
+ principal_id = "63309b8d-908a-4dcf-b95b-25eae33aaceb"
+ principal_type = (known after apply)
+ role_definition_id = (known after apply)
+ role_definition_name = (known after apply)
+ scope = "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet"
+ skip_service_principal_aad_check = (known after apply)
}
# azurerm_role_definition.vnet-peering will be created
+ resource "azurerm_role_definition" "vnet-peering" {
+ assignable_scopes = [
+ "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3",
]
+ id = (known after apply)
+ name = "allow-vnet-peer-action-development"
+ role_definition_id = (known after apply)
+ scope = "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3"
+ permissions {
+ actions = [
+ "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/write",
+ "Microsoft.Network/virtualNetworks/peer/action",
+ "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/read",
+ "Microsoft.Network/virtualNetworks/virtualNetworkPeerings/delete",
]
+ not_actions = []
}
}
# azurerm_virtual_network_peering.main will be created
+ resource "azurerm_virtual_network_peering" "main" {
+ allow_forwarded_traffic = (known after apply)
+ allow_gateway_transit = (known after apply)
+ allow_virtual_network_access = (known after apply)
+ id = (known after apply)
+ name = "development_2_sec"
+ remote_virtual_network_id = "/subscriptions/2b1285d1-f4a7-4cc3-a7b1-0d0fc31d6192/resourceGroups/security/providers/Microsoft.Network/virtualNetworks/security"
+ resource_group_name = "development-vnet"
+ use_remote_gateways = (known after apply)
+ virtual_network_name = "development-vnet"
}
# azurerm_virtual_network_peering.sec will be created
+ resource "azurerm_virtual_network_peering" "sec" {
+ allow_forwarded_traffic = (known after apply)
+ allow_gateway_transit = (known after apply)
+ allow_virtual_network_access = (known after apply)
+ id = (known after apply)
+ name = "sec_2_development"
+ remote_virtual_network_id = "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet"
+ resource_group_name = "security"
+ use_remote_gateways = (known after apply)
+ virtual_network_name = "security"
}
Plan: 4 to add, 0 to change, 0 to destroy.
...
------------------------------------------------------------------------
This plan was saved to: main.tfplan
To perform exactly these actions, run the following command to apply:
terraform apply "main.tfplan"
And finally terraform apply:
PS /home/mark/terraform/Globomantics-testing/networking> terraform apply main.tfplan
azurerm_role_definition.vnet-peering: Creating...
azurerm_role_definition.vnet-peering: Creation complete after 1s [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleDefinitions/b9614597-de13-c2c6-a275-9186847642ed]
azurerm_role_assignment.vnet: Creating...
azurerm_role_assignment.vnet: Creation complete after 1s [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet/providers/Microsoft.Authorization/roleAssignments/7f6a642d-d43e-05b5-cf34-3d20d02cd255]
azurerm_virtual_network_peering.main: Creating...
azurerm_virtual_network_peering.sec: Creating...
azurerm_virtual_network_peering.main: Still creating... [10s elapsed]
azurerm_virtual_network_peering.sec: Still creating... [10s elapsed]
azurerm_virtual_network_peering.main: Creation complete after 11s [id=/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet/virtualNetworkPeerings/development_2_sec]
azurerm_virtual_network_peering.sec: Creation complete after 11s [id=/subscriptions/2b1285d1-f4a7-4cc3-a7b1-0d0fc31d6192/resourceGroups/security/providers/Microsoft.Network/virtualNetworks/security/virtualNetworkPeerings/sec_2_development]
...
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
resource_group_name = development-vnet
vnet_id = /subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/resourceGroups/development-vnet/providers/Microsoft.Network/virtualNetworks/development-vnet
vnet_name = development-vnet
And it all works fine using exactly the same Service Principal that was created by Azure DevOps.
I will try next to delete the Service Connection on the Azure DevOps project, create a new one manually and retry the release.
EDIT 2
Same error with the new Service Connection. I do not understand it. What I find "curious" is that all the resources in main.tf were provisioned just fine, but none in vnet-peering.tf.
What is going on here?
EDIT 3
Replaced the Terraform tasks by Microsoft DevLabs with those by Charles Zipp, because the Pluralsight course author used them. Same result.
this happens because you are trying to modify permissions, that requires owner role or custom rbac role.
Microsoft.Authorization/roleDefinitions/write
https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor
(Posted answer on behalf of the question author to move it to the answer space).
Following the understanding that the Contributor role is unable to create the role definition and assignment required by the pipeline I have manually enhanced the capabilities of the Service Principal used by the Azure Pipelines.
See below (Azure Cloud Shell running powershell):
PS Azure:\> $RoleDef = #{
>> Name = 'AuthWrite'
>> Description = 'Allows to create role definitions and assignments'
>> AssignableScopes = #('/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3')
>> Actions = #('Microsoft.Authorization/*/Write')
>> }
Azure:/
PS Azure:\> az role definition create --role-definition $(($RoleDef | ConvertTo-Json -Compress) -replace '"','""')
{
"assignableScopes": [
"/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3"
],
"description": "Allows to create role definitions and assignments",
"id": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleDefinitions/be9504ee-704b-4a74-b4e3-3fa9b1ba2a23",
"name": "be9504ee-704b-4a74-b4e3-3fa9b1ba2a23",
"permissions": [
{
"actions": [
"Microsoft.Authorization/*/Write"
],
"dataActions": [],
"notActions": [],
"notDataActions": []
}
],
"roleName": "AuthWrite",
"roleType": "CustomRole",
"type": "Microsoft.Authorization/roleDefinitions"
}
Azure:/
PS Azure:\> az role assignment create --assignee https://VisualStudio/SPNf0e6b6b5-984e-4b5b-a6fb-86532ad1b0ce --role 'AuthWrite'
{
"canDelegate": null,
"id": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleAssignments/1550bf9a-fcfc-4310-bf3a-3c70fd250578",
"name": "1550bf9a-fcfc-4310-bf3a-3c70fd250578",
"principalId": "fac663cd-4e74-4bc9-b685-fb0e182beec2",
"principalType": "ServicePrincipal",
"roleDefinitionId": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3/providers/Microsoft.Authorization/roleDefinitions/be9504ee-704b-4a74-b4e3-3fa9b1ba2a23",
"scope": "/subscriptions/2b38509c-a310-4c8f-bd78-9e400cc874e3",
"type": "Microsoft.Authorization/roleAssignments"
}
Azure:/
Now that the Service Principal used by the Azure Pipelines has the additional AuthWrite I was able to run the pipeline successfully.
Yeehaa!

Resources