Skip to content

Commit 260127a

Browse files
priyanshu92claude
andauthored
[Pages] Allow az auth without an Azure subscription (#132)
Pass `--allow-no-subscriptions` on AAD-only Azure CLI calls so the plugin works for users whose Microsoft account has no Azure subscription (Dataverse and Power Platform tokens are AAD-scoped and don't require a subscription). - Update the shared `getAuthToken` helper in `validation-helpers.js` to pass the flag on `az account get-access-token`. - Refresh user-facing `az login` hint strings across scripts, SKILL.md, agent, and reference files to recommend `az login --allow-no-subscriptions`. - Clarify the `list-azure-keyvaults.js` error message — Key Vault is subscription-scoped, the flag does not apply there. - Document the convention in `plugins/power-pages/AGENTS.md`: apply the flag on AAD-only commands, never on subscription-scoped ones. - Add `validation-helpers.test.js` (stubs `child_process.execSync` via require.cache) to lock the flag in. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d0e8837 commit 260127a

20 files changed

Lines changed: 67 additions & 24 deletions

plugins/power-pages/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Read `PLUGIN_DEVELOPMENT_GUIDE.md` for UX and reliability standards when creatin
1414
- **Power Pages config loading** must reuse `scripts/lib/powerpages-config.js` anywhere a script reads `.powerpages-site` table-permission or site-setting YAML. Keep that module focused on loading/parsing code-site config only; put validation or business rules in separate validator modules.
1515
- **Script changes require tests** — Whenever you add a new script or modify an existing script, add or update `node:test` coverage under `scripts/tests/`. Prefer one `*.test.js` file per script/module being tested, and keep the test command passing: `node --test plugins/power-pages/scripts/tests/` (Node's built-in runner discovers `*.test.js` files under the given directory). Validator changes are not an exception; they must always ship with test coverage.
1616
- **Dataverse-backed validation** must stay opt-in for local runs only. Do not require live Dataverse connectivity in CI workflows or default test runs; gate it behind explicit local flags such as `--validate-dataverse-relationships`.
17+
- **Azure CLI `--allow-no-subscriptions`** — pass this flag on AAD-only `az` operations (`az account get-access-token`, `az login`, `az account show`) so the plugin works for users whose Microsoft account has no Azure subscription. Do NOT add it to subscription-scoped commands (`az keyvault create|list`, `az group ...`, `az resource ...`) — those genuinely require a subscription. Reuse the shared `getAuthToken` helper in `scripts/lib/validation-helpers.js` instead of shelling out to `az` directly.
1718
- **Reference docs** shared across skills live in `references/` — reference via `${CLAUDE_PLUGIN_ROOT}/references/` paths, don't duplicate.
1819
- **Templates** use `__PLACEHOLDER__` tokens (e.g., `__SITE_NAME__`) replaced during scaffolding. The `gitignore` file is stored without the dot prefix and renamed to `.gitignore` during scaffolding.
1920
- **Hooks** are defined centrally in `hooks/hooks.json`, using `PostToolUse` with matcher `Skill` so validation runs when a tracked Power Pages skill completes.

plugins/power-pages/agents/data-model-architect.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Verify Dataverse access and obtain authentication details using the shared scrip
9393
node "${CLAUDE_PLUGIN_ROOT}/scripts/verify-dataverse-access.js" <envUrl>
9494
```
9595

96-
This outputs JSON with `token`, `userId`, `organizationId`, and `tenantId`. If it fails, inform the user that Azure CLI login is required (`az login`).
96+
This outputs JSON with `token`, `userId`, `organizationId`, and `tenantId`. If it fails, inform the user that Azure CLI login is required (`az login --allow-no-subscriptions` — works whether or not the user has an Azure subscription).
9797

9898
### 2.3 Query Existing Tables
9999

@@ -148,7 +148,7 @@ If still unable to determine the prefix, use `cr` as a placeholder and note in t
148148
If any of the above commands fail, include the error in your plan output so the user can see what went wrong:
149149

150150
- If `pac env who` fails: Note that PAC CLI auth is required (`pac auth create`)
151-
- If `verify-dataverse-access.js` fails: Note that Azure CLI login is required (`az login`)
151+
- If `verify-dataverse-access.js` fails: Note that Azure CLI login is required (`az login --allow-no-subscriptions`)
152152
- If `dataverse-request.js` returns a non-2xx `status`: Check the status code — 401/403 means permissions are insufficient, 404 means the environment URL or API path may be incorrect
153153
- The `dataverse-request.js` script handles 401 token refresh and 429/5xx retries automatically
154154

plugins/power-pages/agents/table-permissions-architect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ Result:
378378
If any API calls fail:
379379

380380
- **`pac env who` fails**: Note that PAC CLI auth is required (`pac auth create`)
381-
- **`verify-dataverse-access.js` fails**: Note that Azure CLI login is required (`az login`)
381+
- **`verify-dataverse-access.js` fails**: Note that Azure CLI login is required (`az login --allow-no-subscriptions`)
382382
- **OData 401/403**: The `dataverse-request.js` script handles 401 token refresh automatically; persistent 401/403 indicates insufficient privileges — note in plan
383383
- **OData 404**: Table doesn't exist — exclude from plan
384384

plugins/power-pages/agents/webapi-settings-architect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ Store the results as a lookup map for each table:
238238
If any API calls fail:
239239
240240
- **`pac env who` fails**: Note that PAC CLI auth is required (`pac auth create`)
241-
- **`verify-dataverse-access.js` fails**: Note that Azure CLI login is required (`az login`)
241+
- **`verify-dataverse-access.js` fails**: Note that Azure CLI login is required (`az login --allow-no-subscriptions`)
242242
- **OData 401/403**: Token expired or insufficient privileges — note in plan
243243
- **OData 404**: Table doesn't exist — exclude from plan
244244

plugins/power-pages/references/dataverse-prerequisites.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ Run the shared script to obtain an Azure CLI token and verify Dataverse API acce
2424
node "${CLAUDE_PLUGIN_ROOT}/scripts/verify-dataverse-access.js" <envUrl>
2525
```
2626

27-
On success (exit 0), it outputs JSON to stdout with `token`, `userId`, `organizationId`, and `tenantId`. On failure (exit 1), stderr explains the issue (missing `az login`, expired token, etc.).
27+
On success (exit 0), it outputs JSON to stdout with `token`, `userId`, `organizationId`, and `tenantId`. On failure (exit 1), stderr explains the issue (missing `az login --allow-no-subscriptions`, expired token, etc.).
28+
29+
> **Note for users without an Azure subscription**: Dataverse and Power Platform tokens are AAD-scoped, so they work even when the signed-in Microsoft account has no Azure subscriptions attached. Use `az login --allow-no-subscriptions` to authenticate — it succeeds in both cases. Subscription-scoped Azure operations (Key Vault, resource groups, etc.) still require an actual subscription.

plugins/power-pages/scripts/clear-site-cache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const ppApiBaseUrl = CLOUD_TO_API[pacInfo.cloud] || CLOUD_TO_API['Public'];
5353
// --- Get Power Platform API token ---
5454
const token = getAuthToken(ppApiBaseUrl);
5555
if (!token) {
56-
output({ success: false, error: 'Failed to get Azure CLI access token. Ensure you are logged in with: az login' });
56+
output({ success: false, error: 'Failed to get Azure CLI access token. Ensure you are logged in with: az login --allow-no-subscriptions' });
5757
}
5858

5959
// --- Find the website and restart it to clear cache ---

plugins/power-pages/scripts/create-environment-variable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async function apiPost(envUrl, token, entitySet, body) {
9797
async function main() {
9898
const token = getAuthToken(envUrl);
9999
if (!token) {
100-
process.stderr.write('Failed to get Azure CLI token. Run `az login` first.\n');
100+
process.stderr.write('Failed to get Azure CLI token. Run `az login --allow-no-subscriptions` first.\n');
101101
process.exit(1);
102102
}
103103

plugins/power-pages/scripts/dataverse-request.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ async function main() {
7676

7777
let token = getAuthToken(envUrl);
7878
if (!token) {
79-
process.stderr.write('Failed to get Azure CLI token. Run `az login` first.\n');
79+
process.stderr.write('Failed to get Azure CLI token. Run `az login --allow-no-subscriptions` first.\n');
8080
process.exit(1);
8181
}
8282

@@ -94,7 +94,7 @@ async function main() {
9494
if (res.statusCode === 401 && attempt < maxRetries) {
9595
token = getAuthToken(envUrl);
9696
if (!token) {
97-
process.stderr.write('Token refresh failed. Run `az login` again.\n');
97+
process.stderr.write('Token refresh failed. Run `az login --allow-no-subscriptions` again.\n');
9898
process.exit(1);
9999
}
100100
continue;

plugins/power-pages/scripts/lib/validation-helpers.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,13 @@ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12
9494

9595
/**
9696
* Gets an Azure CLI access token for the given resource URL.
97+
* `--allow-no-subscriptions` lets accounts without an Azure subscription mint AAD-scoped Dataverse/PP tokens.
9798
* @returns {string|null} Access token, or null if unavailable
9899
*/
99100
function getAuthToken(resourceUrl) {
100101
try {
101102
return execSync(
102-
`az account get-access-token --resource "${resourceUrl}" --query accessToken -o tsv`,
103+
`az account get-access-token --resource "${resourceUrl}" --allow-no-subscriptions --query accessToken -o tsv`,
103104
{ encoding: 'utf8', timeout: 15000 }
104105
).trim();
105106
} catch {

plugins/power-pages/scripts/list-azure-keyvaults.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function main() {
2828
}
2929

3030
if (result.status !== 0) {
31-
process.stderr.write('Failed to list Azure Key Vaults. Ensure you are logged in (`az login`).\n');
31+
process.stderr.write('Failed to list Azure Key Vaults. Ensure `az login` succeeded AND your account has a subscription with Key Vault access (Key Vaults are subscription-scoped).\n');
3232
if (result.stderr) process.stderr.write(result.stderr);
3333
process.exit(1);
3434
}

0 commit comments

Comments
 (0)