My previous post on using Azure DevOps Pipeline with Workload Identity federation described how to use a context of already authenticated service principal (the identity of service connection used by the pipeline), to refresh the existing token and exchange it for a SPO token.
The example I provided was very simple, and probably not a very useful one. On top of that, @eduardpaul pointed out that it doesn't work for deploying apps to the app catalog.
I utilized the
Sites.FullControl.All
permissions for both Graph and SharePoint APIs, along withAppCatalog.ReadWrite.All
for testing purposes (global app catalog). [...] I consistently encountered a "(401) Unauthorized" error as soon as the code reached theAdd-PnPApp
section (the only different line from your example). Interestingly, a previous app registration with identical permissions (but using a pfx+password) was functioning correctly.
I couldn't sleep if I didn't check it out.
The test: deploy SPFx app
Service Connection
Please note the url displayed on the bottom- it contains the app id (3f1d79ad-xxxx-xxxx-xxxx-51799f7bb56d
) I'm configuring in the next step.
API Permissions
Although I really don't like using Sites.FullControl.All
, I'm going to be a bit lazy here.
UPDATE: The minimum required permissions required to deploy the SPFx solution to the tenant-level app catalog is Application Sites.Selected
with FullControl
granted using the Grant-PnPAzureADAppSitePermission
script:
Grant-PnPAzureADAppSitePermission -AppId $clientId -DisplayName $clientDisplayName -Permissions FullControl
The pipeline
variables:
- name: tenantName
value: "{your-tenant-name}"
- name: siteName
value: "AppCatalog"
- name: spfxPackage
value: "spfx/spfx-1-16-1-pnpm.sppkg"
steps:
- task: AzurePowerShell@5
name: DeploySPFx
inputs:
azureSubscription: DEV_Connection
azurePowerShellVersion: latestVersion
ScriptType: InlineScript
Inline: |
Write-Host "##[group]Install/Import PS modules"
Install-Module PnP.PowerShell -Scope "CurrentUser" -Verbose -AllowClobber -Force
Write-Host "##[endgroup]"
Write-Host "##[group]Who am I"
$azContext = (Get-AzContext).Account.Id
$sp = Get-AzADServicePrincipal -ApplicationId $azContext
Write-Host "##[debug] ServicePrincipal: $($sp.Id)"
Write-Host "##[endgroup]"
$url = "https://$(tenantName).sharepoint.com"
$path="$(Build.SourcesDirectory)/$(spfxPackage)"
try {
$azAccessToken = Get-AzAccessToken -ResourceUrl $url
$conn = Connect-PnPOnline -Url "$url/sites/$(siteName)" -AccessToken $azAccessToken.Token -ReturnConnection
Write-Host "##[debug]Get-PnPConnection"
Write-Host $conn.Url
$packageInSite = Add-PnPApp -Path $path -Overwrite -Publish -SkipFeatureDeployment -Connection $conn
}
catch {
Write-Host "##[error]$($_.Exception.Message)"
}
displayName: Deploy spfx
As you see, I'm using the DEV_Connection service connection. The authentication is done by the pipeline, the identity exists in the same tenant as my SPO site.
The run
Seems like everything worked out.
The Id you see here (bd6a8b8e-xxxx-xxxx-xxxx-91ad2d479534
) is different than the id you saw above. It's still the same identity, though. One is the application id, and the other one is the object id.
Errors?
Getting The term 'Install-Module' is not recognized
when running on ubuntu-latest? See Azure DevOps and "The term 'Install-Module' is not recognized" issue
Did it deploy?
Let's check the app catalog:
Seems like it did. The Federated Identity added and published the package successfully.
"Works for me" :/
I really don't know, @eduardpaul, what the problem with your deployment might be. I hate giving you the "works for me" answer 🙈, but... it really does
One thing that comes to my mind is that maybe you have to wait:
"Managed identity tokens are cached by the underlying Azure infrastructure for performance and resiliency purposes: the back-end services for managed identities maintain a cache per resource URI for around 24 hours. It can take several hours for changes to a managed identity's permissions to take effect, for example. Today, it is not possible to force a managed identity's token to be refreshed before its expiry. For more information, see Limitation of using managed identities for authorization."
Are managed identities tokens cached?
This refers to API permissions (Sites.Selected
,Sites.FullControl.All
) that you grant usingNew-MgServicePrincipalAppRoleAssignment
.
Permissions granted to SPO site take effect immediately (at least for now, according to my tests)
Sites.Selected
Using Sites.FullControl.All
is not only "quick'n'dirty" but also rather difficult to get in enterprise environment. Myself, even if I could convince global admin that I need it and they can trust me, I think I would never request it. It certainly has its use when testing, as it helps us to isolate the problem, but once we are sure the code works, it's time to go back to minimum required permissions.
The good news is that Sites.Selected
works as well. Just make sure you grant FullControl
permissions on the AppCatalog site, using for example Grant-PnPAzureADAppSitePermission
Grant-PnPAzureADAppSitePermission -AppId $clientId -DisplayName $displayName -Permissions FullControl
Top comments (0)