Skip to content

Commit ade98c3

Browse files
authored
Feat: add Test-PnPConnectionPermission cmdlet for API permission validation (#5428)
* Feat: add Test-PnPConnectionPermission cmdlet for API permission validation * Feat: update CHANGELOG and documentation for Test-PnPConnectionPermission cmdlet enhancements
1 parent 629f8d2 commit ade98c3

6 files changed

Lines changed: 637 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
99
## [Current nightly]
1010

1111
### Added
12+
- Added `Test-PnPConnectionPermission` which compares the current connection's access token claims with the permission metadata of a PnP PowerShell cmdlet. Returns `$true` or `$false` and names the missing permissions through a non-terminating error, so it can be used both in a conditional and, with `-ErrorAction Stop`, as a preflight check that fails a script before anything is changed. A more privileged scope satisfies a lesser one, and requirements which cannot be established are reported as indeterminate rather than as missing permissions. [#5428](https://github.com/pnp/powershell/pull/5428)
1213
- Added `ApiPermissionsDependOnResource` attribute for cmdlets of which the required permissions follow from the resource they are pointed at or from how they are invoked, and applied it to `Get-PnPGraphSubscription`, `New-PnPGraphSubscription`, `Set-PnPGraphSubscription`, `Remove-PnPGraphSubscription`, `Invoke-PnPGraphMethod`, `New-PnPSite`, `Set-PnPSiteClassification` and `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory`. The attribute is informational and is not evaluated when validating an access token. [#5427](https://github.com/pnp/powershell/pull/5427)
1314
- Added `ApiPermissionsNotRequired` attribute for cmdlets which need no permissions on the application registration PnP PowerShell connects with, and applied it to `Get-PnPConnection`, `Get-PnPContext`, `Set-PnPContext`, `Get-PnPPowerShellTelemetryEnabled`, `Enable-PnPPowerShellTelemetry`, `Disable-PnPPowerShellTelemetry`, `Register-PnPEntraIDApp` and `Register-PnPEntraIDAppForInteractiveLogin`. The attribute is informational and is not evaluated when validating an access token. [#5427](https://github.com/pnp/powershell/pull/5427)
1415
- Added `Get-PnPCommandPermission` cmdlet which returns the delegated and application API permissions and the minimum SharePoint role required to run a cmdlet. Permissions are read from the permission attributes where declared and derived from the cmdlet type and operation where they are not. Supports wildcards, cmdlet aliases and filtering on resource type, and returns all cmdlets when no name is provided. [#5427](https://github.com/pnp/powershell/pull/5427)
@@ -26,6 +27,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
2627
- Changed `Export-PnPFlow -AsZipPackage` and `Export-PnPPowerApp` to ask for confirmation before overwriting an existing file when `-OutPath` is omitted, as they already did when `-OutPath` is specified. Unattended scripts that rely on the previous silent overwrite need to specify `-Force`. [#5421](https://github.com/pnp/powershell/pull/5421)
2728

2829
### Fixed
30+
- Fixed the access token permission validation reporting a required permission as missing when the token holds a scope which covers it, such as a token with `Sites.FullControl.All` being reported as lacking `Sites.Read.All`. The permission metadata states the least privileged scope which suffices, so a more privileged scope now satisfies it. This affects the validation message written to the trace log by every cmdlet as well as `Test-PnPConnectionPermission`. [#5428](https://github.com/pnp/powershell/pull/5428)
2931
- Fixed the documented permissions of `Get-PnPGraphSubscription`, which listed `Subscription.Read.All` as required. That permission is only needed to also return subscriptions created by other applications; reading back a subscription created by the current application requires the same permissions on the subscribed resource that were needed to create it. Also documented the resource dependent permissions of `New-PnPGraphSubscription`, `Set-PnPGraphSubscription`, `Remove-PnPGraphSubscription` and `Invoke-PnPGraphMethod`, and corrected `Set-PnPGraphSubscription` linking to the delete instead of the update API documentation. [#5427](https://github.com/pnp/powershell/pull/5427)
3032
- Fixed the documented Microsoft Graph permission of `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory`, which listed `User.Read`. Listing all users from Entra ID requires `User.Read.All`, and Microsoft Graph is only called when `-Users` is not provided. Its SharePoint permissions are now declared as metadata as well. [#5427](https://github.com/pnp/powershell/pull/5427)
3133
- Fixed requested permissions not being applied by `Register-PnPEntraIDApp` and `Register-PnPEntraIDAppForInteractiveLogin`: `-Scopes` silently dropped every delegated permission, such as `SPO.AllSites.FullControl`, and `-O365ManagementApplicationPermissions` and `-O365ManagementDelegatePermissions` were never applied. A permission that cannot be resolved is now reported as an error, as is combining `-Scopes` with the per resource parameters. [#5424](https://github.com/pnp/powershell/pull/5424)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
Module Name: PnP.PowerShell
3+
schema: 2.0.0
4+
applicable: SharePoint Online
5+
online version: https://pnp.github.io/powershell/cmdlets/Test-PnPConnectionPermission.html
6+
external help file: PnP.PowerShell.dll-Help.xml
7+
title: Test-PnPConnectionPermission
8+
---
9+
10+
# Test-PnPConnectionPermission
11+
12+
## SYNOPSIS
13+
Tests whether the current connection has the API permissions required by a PnP PowerShell cmdlet.
14+
15+
## SYNTAX
16+
17+
```powershell
18+
Test-PnPConnectionPermission [-CommandName] <String> [-Connection <PnPConnection>] [-Verbose]
19+
```
20+
21+
## DESCRIPTION
22+
Acquires the access tokens needed by the specified cmdlet and compares their `scp` or `roles` claims with the cmdlet's permission metadata as reported by [Get-PnPCommandPermission](Get-PnPCommandPermission.md). Returns `$true` when the connection holds one complete permission set and `$false` when it does not.
23+
24+
A required permission is satisfied by the very same scope, but also by any scope which is more privileged. `Sites.FullControl.All` therefore satisfies a required `Sites.Read.All`, and the delegated notation and the application notation of the same SharePoint level are treated as equivalent, so `AllSites.FullControl` satisfies a required `Sites.ReadWrite.All`. This matters because the permission metadata reports the least privileged scope which suffices rather than the exact scope to hold. `Sites.Selected` is deliberately excluded from this: it grants access only to the sites the application has explicitly been granted access to, so it neither covers nor is covered by the tenant wide scopes.
25+
26+
When permissions are missing the cmdlet returns `$false` and writes a non-terminating `RequiredPermissionMissing` error which names each missing permission and preserves the `AND` and `OR` relationship between permission sets. Because the error is non-terminating, the cmdlet fits all three common usages:
27+
28+
| Usage | Behaviour |
29+
|---|---|
30+
| `if (Test-PnPConnectionPermission ...)` | Branch on the result, with the missing permissions reported on the error stream |
31+
| `-ErrorAction SilentlyContinue` | Return the boolean without writing the error |
32+
| `-ErrorAction Stop` | Terminate the script, for use as a preflight check in unattended runs |
33+
34+
Where the requirement cannot be established, the cmdlet writes a non-terminating `PermissionRequirementsIndeterminate` error and returns nothing at all rather than `$false`. Reporting `$false` would state that the connection does not hold the permissions, while in these cases the check could not be performed. This covers cmdlets whose permissions depend on the resource they are pointed at or on how they are invoked, cmdlets for which no permissions are declared for the token type in use, and resources for which no access token could be acquired.
35+
36+
The check validates API permissions only. SharePoint permission levels, `Sites.Selected` site grants and the additional roles reported by `Get-PnPCommandPermission` cannot be inferred from an access token and are not tested. A `$true` result therefore confirms the token claims, not every authorization requirement of the target resource.
37+
38+
Validation is supported for Microsoft Graph, SharePoint Online, Azure Resource Manager, Power Apps and Graph Connector Service access tokens. The Dynamics CRM audience used by `Get-PnPPowerPlatformSolution` depends on the selected environment and cannot be determined from a cmdlet name, so that permission set is reported as indeterminate. Connections made with an ACS app only token are also reported as indeterminate, as such a token cannot be exchanged for a token carrying permission scopes.
39+
40+
A connection made with `Connect-PnPOnline -AccessToken` holds one fixed token and returns it whatever resource is asked for. The audience of every token is therefore verified before its scopes are used, and a token issued for another API is reported as indeterminate rather than compared against the requirement. In practice this means such a connection can only be tested for cmdlets which use the API the token was issued for.
41+
42+
Testing a SharePoint permission requires a connection created with a SharePoint site URL, because that URL determines the access token audience.
43+
44+
Permissions with a source of `Inferred` or `DeclaredAndInferred` remain estimates. This cmdlet tests the estimate returned by `Get-PnPCommandPermission`; it cannot make inferred metadata authoritative.
45+
46+
## EXAMPLES
47+
48+
### EXAMPLE 1
49+
```powershell
50+
Test-PnPConnectionPermission -CommandName Get-PnPTeamsTeam
51+
```
52+
53+
Returns `$true` if the current connection contains one complete delegated or application permission set required by `Get-PnPTeamsTeam`. Otherwise, it returns `$false` and reports the missing permissions.
54+
55+
### EXAMPLE 2
56+
```powershell
57+
if (-not (Test-PnPConnectionPermission -CommandName Set-PnPList -ErrorAction SilentlyContinue)) {
58+
Write-Host "Skipping the list configuration step, the connection is not permitted to change lists."
59+
}
60+
```
61+
62+
Branches on the result without writing the missing permissions to the error stream.
63+
64+
### EXAMPLE 3
65+
```powershell
66+
'Get-PnPTeamsTeam', 'Set-PnPList', 'Get-PnPList' | Test-PnPConnectionPermission
67+
```
68+
69+
Tests a sequence of cmdlets and returns one boolean per cmdlet. Access tokens are acquired once and reused across all of them. Processing continues after a cmdlet for which a permission is missing.
70+
71+
### EXAMPLE 4
72+
```powershell
73+
$connection = Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/project -Interactive -ReturnConnection
74+
Test-PnPConnectionPermission -CommandName Set-PnPList -Connection $connection -ErrorAction Stop
75+
```
76+
77+
Tests the supplied connection and terminates the script when a required permission is missing, for use as a preflight check in an unattended run.
78+
79+
## PARAMETERS
80+
81+
### -CommandName
82+
The name or alias of the PnP PowerShell cmdlet whose required permissions should be tested. Tab completion is available for PnP cmdlet names.
83+
84+
```yaml
85+
Type: String
86+
Parameter Sets: (All)
87+
Aliases: Identity, Name
88+
89+
Required: True
90+
Position: 0
91+
Default value: None
92+
Accept pipeline input: True
93+
Accept wildcard characters: False
94+
```
95+
96+
### -Connection
97+
Optional connection to be tested. Retrieve a connection by specifying `-ReturnConnection` on `Connect-PnPOnline` or by executing `Get-PnPConnection`.
98+
99+
```yaml
100+
Type: PnPConnection
101+
Parameter Sets: (All)
102+
Aliases: None
103+
104+
Required: False
105+
Position: Named
106+
Default value: Current connection
107+
Accept pipeline input: False
108+
Accept wildcard characters: False
109+
```
110+
111+
## OUTPUTS
112+
113+
### System.Boolean
114+
`$true` when one complete required permission set is present, `$false` when a required permission is missing. Nothing is returned where the requirement could not be established; use `-Verbose` to see the scopes read from each access token.
115+
116+
## RELATED LINKS
117+
118+
[Get-PnPCommandPermission](https://pnp.github.io/powershell/cmdlets/Get-PnPCommandPermission.html)
119+
120+
[How to determine which permissions you need](https://pnp.github.io/powershell/articles/determinepermissions.html)
121+
122+
[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)

pages/articles/determinepermissions.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ Always check `PermissionSource`, it states how authoritative the answer is:
4848

4949
Anything other than `Declared` is guidance rather than a guarantee, so verify it against your own scenario.
5050

51+
### Testing the permissions of your current connection
52+
53+
After connecting, use [Test-PnPConnectionPermission](../cmdlets/Test-PnPConnectionPermission.md) to compare the `scp` or `roles` claims in the current connection's access tokens with the permissions reported for a cmdlet:
54+
55+
```powershell
56+
Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/project -Interactive -ClientId $clientId
57+
Test-PnPConnectionPermission -CommandName Set-PnPList
58+
```
59+
60+
The cmdlet returns `$true` when the connection holds one complete permission set and `$false` when a required permission is missing, naming the missing `AND` and `OR` alternatives on the error stream. A more privileged scope satisfies a lesser one, so a connection holding `AllSites.FullControl` passes a check for `AllSites.Read`.
61+
62+
Because the error is non-terminating, the same cmdlet serves both a conditional and a preflight style:
63+
64+
```powershell
65+
# Branch on the result
66+
if (Test-PnPConnectionPermission -CommandName Set-PnPList -ErrorAction SilentlyContinue) { ... }
67+
68+
# Or fail the script before anything is changed
69+
Test-PnPConnectionPermission -CommandName Set-PnPList -ErrorAction Stop
70+
```
71+
72+
The test covers API permission claims only. It cannot verify the signed-in user's SharePoint permission level, a `Sites.Selected` grant on the target site, directory roles or the other requirements listed in `MinimumSharePointRole` and `AdditionalRoles`. Where `Get-PnPCommandPermission` reports unknown, conditional or resource-dependent permissions, or where no token could be acquired for a resource, the cmdlet returns nothing and reports the requirement as indeterminate rather than claiming a permission is missing. `Inferred` permissions remain estimates even when the current token contains them. Read the error and the `Guidance` from `Get-PnPCommandPermission` for those cases.
73+
5174
### Working out the permissions for an entire script
5275

5376
The most useful application is composing the permission set for a script before you run it. Extract the PnP cmdlets it uses and ask for their permissions in one go:

0 commit comments

Comments
 (0)