Skip to content

Pipeline fails with 401 Unauthorized despite Terraform deploying SPN with Graph PermissionsΒ #3

@alxm8

Description

@alxm8

I followed the guide Azure Devops and terraform from the URL: https://maester.dev/docs/monitoring/azure-devops/

We are using the Terraform code which deploys an Azure DevOps service connection that uses Workload Identity Federation. The Terraform code is configured to create a Service Principal and grant it a specific set of Microsoft Graph API permissions.

However, when the pipeline runs, the Connect-MgGraph step fails with a 401 Unauthorized error, and the logs indicate that the Graph API permissions are missing from the token. This suggests a disconnect between the permissions being granted in Terraform and the permissions available to the pipeline at runtime.

Expected Behavior:

The pipeline should successfully connect to Microsoft Graph using the federated credential and have all the permissions that were assigned via Terraform.

Actual Behavior:

The pipeline fails with a 401 Unauthorized error when calling the Graph API.

Clear-AzContext -Scope Process
Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue
 Connect-AzAccount -ServicePrincipal -Tenant [REDACTED_TENANT_ID] -ApplicationId [REDACTED_APP_ID] -FederatedToken [REDACTED_FEDERATED_TOKEN] -Environment AzureCloud -Scope Process
WARNING: You're using Az version 12.5.0. The latest version of Az is 15.1.0.
 Set-AzContext -SubscriptionId [REDACTED_SUBSCRIPTION_ID] -TenantId [REDACTED_TENANT_ID]
Welcome to Microsoft Graph!

Connected via userprovidedaccesstoken access using [REDACTED_APP_ID_2]
Readme: https://aka.ms/graph/sdk/powershell
SDK Docs: https://aka.ms/graph/sdk/powershell/docs
API Docs: https://aka.ms/graph/docs

NOTE: You can use the -NoWelcome parameter to suppress this message.

WARNING: The version '5.7.1' of module 'Pester' is currently in use. Retry the operation after closing the applications.

β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•  β•šβ•β•β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
β•šβ•β•     β•šβ•β•β•šβ•β•  β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•   β•šβ•β•   β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β• v2.0.0

WARNING: ⚠️  Continuing with missing permissions; expect failures.
WARNING: ⚠️  These Graph permissions are missing in the current connection => (DeviceManagementConfiguration.Read.All DeviceManagementManagedDevices.Read.All DeviceManagementRBAC.Read.All Directory.Read.All DirectoryRecommendations.Read.All IdentityRiskEvent.Read.All Policy.Read.All Policy.Read.ConditionalAccess PrivilegedAccess.Read.AzureAD Reports.Read.All ReportSettings.Read.All RoleManagement.Read.All SecurityIdentitiesSensors.Read.All SecurityIdentitiesHealth.Read.All SharePointTenantSettings.Read.All ThreatHunting.Read.All UserAuthenticationMethod.Read.All). Add the missing 'Application' permissions in the Microsoft Entra portal and grant consent. You will also need to Disconnect-Graph to refresh the permissions.
GET https://graph.microsoft.com/v1.0/organization
HTTP/2.0 401 Unauthorized
request-id: [REDACTED_REQUEST_ID]
client-request-id: [REDACTED_CLIENT_REQUEST_ID]
WWW-Authenticate: Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id="00000003-0000-0000-c000-000000000000"
Date: Mon, 22 Dec 2025 14:36:56 GMT
Content-Type: application/json

{"error":{"code":"InvalidAuthenticationToken","message":"IDX14102: Unable to decode the header '[PII is hidden]' as Base64Url encoded string.","innerError":{"date":"2025-12-22T14:36:56","request-id":"[REDACTED_REQUEST_ID]","client-request-id":"[REDACTED_CLIENT_REQUEST_ID]"}}}
At /home/vsts/.local/share/powershell/Modules/Maester/2.0.0/internal/Invoke-MtGraphRequestCache.ps1:49 char:24
+ …  $results = Invoke-MgGraphRequest -Method $Method -Uri $Uri -Headers  …
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

##[error]PowerShell exited with code '1'.

Finishing: Run Maester
$env:AZDO_PERSONAL_ACCESS_TOKEN="xyz"
$env:AZDO_ORG_SERVICE_URL="https://dev.azure.com/myorgname

Terraform code used:

module "maester" {
  source  = "maester365/maester/azuredevops"
  version = "1.0.0"
  azure_tenant_id = "mytenantid"
  azure_subscription_id = "mysubscriptionid"
  azure_subscription_name = "management"
  azure_devops_org_name  = "mydevopsorgname"
  azure_devops_project_name = "maester-pipelines"
  azure_resourcegroup_location = "uksouth"
  azure_resourcegroup_name = "rg-uks-maester-tst-001"
  graph_app_roles = [ 
   "DeviceManagementConfiguration.Read.All",
   "DeviceManagementApps.Read.All",
   "DeviceManagementApps.ReadWrite.All",
   "DeviceManagementManagedDevices.Read.All",
   "DeviceManagementRBAC.Read.All",
   "Directory.Read.All",
   "DirectoryRecommendations.Read.All",
   "IdentityRiskEvent.Read.All",
   "Policy.Read.All",
   "Policy.Read.ConditionalAccess",
   "PrivilegedAccess.Read.AzureAD",
   "Reports.Read.All",
   "ReportSettings.Read.All",
   "RoleManagement.Read.All",
   "SecurityIdentitiesSensors.Read.All",
   "SecurityIdentitiesHealth.Read.All",
   "SharePointTenantSettings.Read.All",
   "ThreatHunting.Read.All",
   "UserAuthenticationMethod.Read.All",
   "RoleEligibilitySchedule.Read.Directory"
    ]
}

I just want to add, that when the pipeline runs for the first time you give it permissions to run, which is expected. Also the SPN API permissions are showing up as granted in Entra ID.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions