@apexops/cli 0.10.0-next.5 → 0.11.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/capability-packs/registry.v1.json +1 -62
- package/assets/client-projection-metadata/copilot-cli-agent-tools.json +12 -0
- package/assets/client-projections/github-copilot-cli/.github/copilot/settings.json +13 -0
- package/assets/client-projections/github-copilot-cli/.github/instructions/apex-prompt-authoring.instructions.md +1 -1
- package/assets/client-projections/github-copilot-cli/.github/workflows/governance-policy-baseline.yml +101 -0
- package/assets/client-projections/github-copilot-cli/tools/schemas/governance-baseline.schema.json +118 -0
- package/assets/client-projections/github-copilot-cli/tools/scripts/collect-governance-baseline.ps1 +817 -0
- package/assets/config/capability-packs.v1.json +1 -32
- package/assets/config/governance-reference.v1.json +26379 -0
- package/assets/config/recipe-packs.v1.json +1 -26
- package/assets/config/runtime-bundle.v1.json +7 -7
- package/assets/config/toolchain.v1.json +35 -33
- package/assets/config/workflow.v1.json +103 -72
- package/assets/customizations/.github/agents/apex-codegen.agent.md +8 -22
- package/assets/customizations/.github/agents/apex-validator.agent.md +14 -20
- package/assets/customizations/.github/agents/apex.agent.md +108 -75
- package/assets/customizations/.github/copilot/settings.json +13 -0
- package/assets/customizations/.github/instructions/apex-prompt-authoring.instructions.md +1 -1
- package/assets/customizations/.github/skills/apex-architecture/SKILL.md +62 -18
- package/assets/customizations/.github/skills/apex-artifacts/SKILL.md +28 -6
- package/assets/customizations/.github/skills/apex-azure-adr/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-cloud-migrate/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-compliance/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-compute/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-cost-optimization/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-defaults/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-defaults/references/governance-effects.md +5 -3
- package/assets/customizations/.github/skills/apex-azure-deploy/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-diagnostics/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-governance/SKILL.md +19 -3
- package/assets/customizations/.github/skills/apex-azure-governance/references/evidence-interpretation.md +38 -3
- package/assets/customizations/.github/skills/apex-azure-kusto/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-prepare/SKILL.md +5 -3
- package/assets/customizations/.github/skills/apex-azure-quotas/SKILL.md +6 -2
- package/assets/customizations/.github/skills/apex-azure-quotas/references/capacity-decision-rules.md +4 -3
- package/assets/customizations/.github/skills/apex-azure-quotas/references/operational-checklist.md +3 -2
- package/assets/customizations/.github/skills/apex-azure-rbac/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-resources/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-storage/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-azure-validate/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-bicep-patterns/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-codegen/SKILL.md +6 -2
- package/assets/customizations/.github/skills/apex-entra-app-registration/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-mermaid/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-microsoft-docs/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-next/SKILL.md +99 -0
- package/assets/customizations/.github/skills/apex-operations/SKILL.md +46 -16
- package/assets/customizations/.github/skills/apex-planning/SKILL.md +48 -19
- package/assets/customizations/.github/skills/apex-requirements/SKILL.md +61 -26
- package/assets/customizations/.github/skills/apex-terraform-import/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-terraform-patterns/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-terraform-test/SKILL.md +3 -0
- package/assets/customizations/.github/skills/apex-unslop/LICENSE.txt +21 -0
- package/assets/customizations/.github/skills/apex-unslop/SKILL.md +94 -0
- package/assets/customizations/.github/skills/apex-workflow/SKILL.md +15 -5
- package/assets/customizations/.github/workflows/governance-policy-baseline.yml +101 -0
- package/assets/customizations/README.md +8 -9
- package/assets/customizations/manifest.json +167 -209
- package/assets/customizations/tools/schemas/governance-baseline.schema.json +118 -0
- package/assets/customizations/tools/scripts/collect-governance-baseline.ps1 +817 -0
- package/assets/manifest.json +423 -4241
- package/assets/schemas/governance-baseline.schema.json +118 -0
- package/dist/assets.d.ts +18 -2
- package/dist/assets.js +115 -35
- package/dist/bootstrap-wizard.d.ts +37 -0
- package/dist/bootstrap-wizard.js +259 -0
- package/dist/cli.d.ts +17 -2
- package/dist/cli.js +180 -63
- package/dist/dependency-revision.js +8 -0
- package/dist/errors.d.ts +6 -1
- package/dist/errors.js +55 -0
- package/dist/host-profile.d.ts +47 -0
- package/dist/host-profile.js +269 -0
- package/dist/mcp-output-schemas.d.ts +43 -0
- package/dist/mcp-output-schemas.js +279 -0
- package/dist/mcp-tool-effects.d.ts +101 -0
- package/dist/mcp-tool-effects.js +66 -0
- package/dist/mcp.d.ts +50 -3
- package/dist/mcp.js +800 -87
- package/dist/runtime-binding.d.ts +37 -0
- package/dist/runtime-binding.js +131 -0
- package/dist/service.d.ts +643 -38
- package/dist/service.js +4937 -662
- package/dist/state-transfer.js +21 -1
- package/dist/target-scope.d.ts +4 -0
- package/dist/target-scope.js +9 -0
- package/dist/version.d.ts +3 -1
- package/dist/version.js +19 -1
- package/dist/workflow-validators.d.ts +20 -3
- package/dist/workflow-validators.js +408 -67
- package/dist/workspace-root.d.ts +7 -0
- package/dist/workspace-root.js +67 -0
- package/package.json +11 -12
- package/assets/capability-packs/azure-governance-discovery/source/scripts/discover.py +0 -1189
- package/assets/capability-packs/azure-governance-discovery/source/scripts/render_governance.py +0 -676
- package/assets/client-projections/github-copilot-cli/.github/agents/apex-architect.agent.md +0 -91
- package/assets/client-projections/github-copilot-cli/.github/agents/apex-operator.agent.md +0 -75
- package/assets/client-projections/github-copilot-cli/.github/agents/apex-planner.agent.md +0 -88
- package/assets/client-projections/github-copilot-cli/.github/agents/apex-requirements.agent.md +0 -99
- package/assets/client-projections/github-copilot-cli/.github/agents/apex.agent.md +0 -88
- package/assets/client-projections/github-copilot-cli/.github/mcp.json +0 -65
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-architecture/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/SKILL.md +0 -65
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/references/presentation-conventions.md +0 -52
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/references/reference-only-outlines.md +0 -78
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/architecture-assessment.md +0 -35
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/cost-estimate.md +0 -47
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/deployment-summary.md +0 -35
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/governance-constraints.md +0 -35
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/implementation-plan.md +0 -43
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/operations-runbook.md +0 -35
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/requirements.md +0 -42
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-artifacts/templates/resource-inventory.md +0 -29
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-adr/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-adr/references/decision-examples.md +0 -38
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-adr/references/decision-guardrails.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-adr/references/decision-quality.md +0 -21
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-adr/references/decision-record-fields.md +0 -57
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cloud-migrate/SKILL.md +0 -55
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cloud-migrate/references/lambda-to-functions-assessment.md +0 -29
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cloud-migrate/references/migration-readiness.md +0 -27
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cloud-migrate/references/staged-validation-handoff.md +0 -26
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cloud-migrate/references/workload-mapping.md +0 -27
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compliance/SKILL.md +0 -58
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compliance/references/compliance-finding-criteria.md +0 -63
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compliance/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compute/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compute/references/compute-selection.md +0 -26
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compute/references/pricing-evidence.md +0 -11
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-compute/references/recommendation-and-scale-rules.md +0 -44
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cost-optimization/SKILL.md +0 -61
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cost-optimization/references/cost-assessment-criteria.md +0 -61
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-cost-optimization/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/SKILL.md +0 -59
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/avm-binding-guidance.md +0 -29
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/baseline-fallbacks.md +0 -53
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/cost-monitoring.md +0 -47
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/decision-boundaries.md +0 -19
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/governance-effects.md +0 -28
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/naming.md +0 -47
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/network-planning.md +0 -47
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/security-baseline.md +0 -40
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/service-selection.md +0 -51
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-defaults/references/tag-precedence.md +0 -31
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-deploy/SKILL.md +0 -50
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-deploy/references/operational-checklist.md +0 -26
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-deploy/references/preview-recovery-verification.md +0 -48
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-diagnostics/SKILL.md +0 -60
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-diagnostics/references/diagnostic-interpretation.md +0 -42
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-diagnostics/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-governance/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-governance/references/evidence-interpretation.md +0 -58
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-governance/references/operational-checklist.md +0 -25
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-kusto/SKILL.md +0 -58
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-kusto/references/kql-evidence-interpretation.md +0 -41
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-kusto/references/operational-checklist.md +0 -25
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-prepare/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-prepare/references/functions-recipe-pack.md +0 -27
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-prepare/references/preparation-lineage.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-quotas/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-quotas/references/capacity-decision-rules.md +0 -48
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-quotas/references/operational-checklist.md +0 -23
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-rbac/SKILL.md +0 -45
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-rbac/references/assignment-intent.md +0 -39
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-rbac/references/least-privilege-selection.md +0 -50
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-resources/SKILL.md +0 -54
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-resources/references/inventory-query-patterns.md +0 -49
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-resources/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-storage/SKILL.md +0 -35
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-storage/references/security-and-governance.md +0 -12
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-storage/references/service-auth-and-sdk-boundary.md +0 -33
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-storage/references/storage-selection.md +0 -12
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-validate/SKILL.md +0 -49
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-validate/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-azure-validate/references/preflight-evidence.md +0 -43
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/SKILL.md +0 -41
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/references/avm-and-codegen-acceptance.md +0 -11
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/references/codegen-acceptance-checklist.md +0 -46
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/references/compiler-and-provider-gotchas.md +0 -74
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/references/module-interfaces-and-parameters.md +0 -56
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-bicep-patterns/references/network-and-observability.md +0 -52
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-codegen/SKILL.md +0 -32
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-entra-app-registration/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-entra-app-registration/references/capability-receipts.md +0 -21
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-entra-app-registration/references/design-and-diagnostics.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-entra-app-registration/references/oauth-registration-model.md +0 -26
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-entra-app-registration/references/permissions-and-credentials.md +0 -27
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-mermaid/SKILL.md +0 -44
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-mermaid/references/styling.md +0 -33
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-mermaid/references/syntax.md +0 -99
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-microsoft-docs/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-microsoft-docs/references/research-method.md +0 -91
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-operations/SKILL.md +0 -34
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-planning/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-requirements/SKILL.md +0 -46
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-import/SKILL.md +0 -47
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-import/references/adoption-attestation.md +0 -10
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-import/references/import-assessment.md +0 -14
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-import/references/import-mapping.md +0 -27
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-import/references/mapping-and-adoption-attestation.md +0 -12
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/SKILL.md +0 -49
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/references/module-composition-and-refactor.md +0 -20
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/references/module-composition-and-state-boundaries.md +0 -11
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/references/module-locks-and-codegen-acceptance.md +0 -14
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/references/network-and-observability.md +0 -12
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-patterns/references/plan-and-change-assessment.md +0 -24
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-test/SKILL.md +0 -45
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-test/references/evidence-acceptance.md +0 -10
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-test/references/plan-mode-and-mock-design.md +0 -12
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-test/references/plan-mode-test-design.md +0 -23
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-terraform-test/references/test-design.md +0 -13
- package/assets/client-projections/github-copilot-cli/.github/skills/apex-workflow/SKILL.md +0 -37
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-architect.agent.md +0 -100
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-codegen.agent.md +0 -62
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-operator.agent.md +0 -79
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-planner.agent.md +0 -98
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-requirements.agent.md +0 -108
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-reviewer.agent.md +0 -63
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex-validator.agent.md +0 -61
- package/assets/client-projections/github-copilot-vscode/.github/agents/apex.agent.md +0 -107
- package/assets/client-projections/github-copilot-vscode/.github/copilot-instructions.md +0 -13
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-agent-authoring.instructions.md +0 -14
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-artifact-contracts.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-automation.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-azure-yaml.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-bicep.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-code-quality.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-context.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-documentation.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-governance.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-instruction-authoring.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-javascript.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-json.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-markdown.instructions.md +0 -13
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-powershell.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-prompt-authoring.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-python.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-safe-file-edits.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-safe-shell.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-shell.instructions.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-skill-authoring.instructions.md +0 -13
- package/assets/client-projections/github-copilot-vscode/.github/instructions/apex-terraform.instructions.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-architecture/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/SKILL.md +0 -65
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/references/presentation-conventions.md +0 -52
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/references/reference-only-outlines.md +0 -78
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/architecture-assessment.md +0 -35
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/cost-estimate.md +0 -47
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/deployment-summary.md +0 -35
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/governance-constraints.md +0 -35
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/implementation-plan.md +0 -43
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/operations-runbook.md +0 -35
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/requirements.md +0 -42
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-artifacts/templates/resource-inventory.md +0 -29
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-adr/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-adr/references/decision-examples.md +0 -38
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-adr/references/decision-guardrails.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-adr/references/decision-quality.md +0 -21
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-adr/references/decision-record-fields.md +0 -57
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cloud-migrate/SKILL.md +0 -55
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cloud-migrate/references/lambda-to-functions-assessment.md +0 -29
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cloud-migrate/references/migration-readiness.md +0 -27
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cloud-migrate/references/staged-validation-handoff.md +0 -26
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cloud-migrate/references/workload-mapping.md +0 -27
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compliance/SKILL.md +0 -58
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compliance/references/compliance-finding-criteria.md +0 -63
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compliance/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compute/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compute/references/compute-selection.md +0 -26
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compute/references/pricing-evidence.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-compute/references/recommendation-and-scale-rules.md +0 -44
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cost-optimization/SKILL.md +0 -61
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cost-optimization/references/cost-assessment-criteria.md +0 -61
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-cost-optimization/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/SKILL.md +0 -59
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/avm-binding-guidance.md +0 -29
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/baseline-fallbacks.md +0 -53
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/cost-monitoring.md +0 -47
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/decision-boundaries.md +0 -19
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/governance-effects.md +0 -28
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/naming.md +0 -47
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/network-planning.md +0 -47
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/security-baseline.md +0 -40
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/service-selection.md +0 -51
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-defaults/references/tag-precedence.md +0 -31
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-deploy/SKILL.md +0 -50
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-deploy/references/operational-checklist.md +0 -26
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-deploy/references/preview-recovery-verification.md +0 -48
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-diagnostics/SKILL.md +0 -60
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-diagnostics/references/diagnostic-interpretation.md +0 -42
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-diagnostics/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-governance/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-governance/references/evidence-interpretation.md +0 -58
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-governance/references/operational-checklist.md +0 -25
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-kusto/SKILL.md +0 -58
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-kusto/references/kql-evidence-interpretation.md +0 -41
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-kusto/references/operational-checklist.md +0 -25
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-prepare/SKILL.md +0 -52
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-prepare/references/functions-recipe-pack.md +0 -27
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-prepare/references/preparation-lineage.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-quotas/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-quotas/references/capacity-decision-rules.md +0 -48
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-quotas/references/operational-checklist.md +0 -23
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-rbac/SKILL.md +0 -45
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-rbac/references/assignment-intent.md +0 -39
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-rbac/references/least-privilege-selection.md +0 -50
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-resources/SKILL.md +0 -54
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-resources/references/inventory-query-patterns.md +0 -49
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-resources/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-storage/SKILL.md +0 -35
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-storage/references/security-and-governance.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-storage/references/service-auth-and-sdk-boundary.md +0 -33
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-storage/references/storage-selection.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-validate/SKILL.md +0 -49
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-validate/references/operational-checklist.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-azure-validate/references/preflight-evidence.md +0 -43
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/SKILL.md +0 -41
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/references/avm-and-codegen-acceptance.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/references/codegen-acceptance-checklist.md +0 -46
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/references/compiler-and-provider-gotchas.md +0 -74
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/references/module-interfaces-and-parameters.md +0 -56
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-bicep-patterns/references/network-and-observability.md +0 -52
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-codegen/SKILL.md +0 -32
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-entra-app-registration/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-entra-app-registration/references/capability-receipts.md +0 -21
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-entra-app-registration/references/design-and-diagnostics.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-entra-app-registration/references/oauth-registration-model.md +0 -26
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-entra-app-registration/references/permissions-and-credentials.md +0 -27
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-mermaid/SKILL.md +0 -44
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-mermaid/references/styling.md +0 -33
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-mermaid/references/syntax.md +0 -99
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-microsoft-docs/SKILL.md +0 -53
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-microsoft-docs/references/research-method.md +0 -91
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-operations/SKILL.md +0 -34
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-planning/SKILL.md +0 -36
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-requirements/SKILL.md +0 -46
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-import/SKILL.md +0 -47
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-import/references/adoption-attestation.md +0 -10
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-import/references/import-assessment.md +0 -14
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-import/references/import-mapping.md +0 -27
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-import/references/mapping-and-adoption-attestation.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/SKILL.md +0 -49
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/references/module-composition-and-refactor.md +0 -20
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/references/module-composition-and-state-boundaries.md +0 -11
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/references/module-locks-and-codegen-acceptance.md +0 -14
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/references/network-and-observability.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-patterns/references/plan-and-change-assessment.md +0 -24
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-test/SKILL.md +0 -45
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-test/references/evidence-acceptance.md +0 -10
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-test/references/plan-mode-and-mock-design.md +0 -12
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-test/references/plan-mode-test-design.md +0 -23
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-terraform-test/references/test-design.md +0 -13
- package/assets/client-projections/github-copilot-vscode/.github/skills/apex-workflow/SKILL.md +0 -37
- package/assets/client-projections/github-copilot-vscode/.vscode/mcp.json +0 -29
- package/assets/customizations/.github/agents/apex-architect.agent.md +0 -93
- package/assets/customizations/.github/agents/apex-operator.agent.md +0 -72
- package/assets/customizations/.github/agents/apex-planner.agent.md +0 -91
- package/assets/customizations/.github/agents/apex-requirements.agent.md +0 -101
- package/assets/customizations/.github/agents/apex-reviewer.agent.md +0 -60
- package/assets/customizations/.github/mcp.json +0 -65
- package/assets/customizations/.github/skills/apex-azure-prepare/references/functions-recipe-pack.md +0 -27
- package/assets/customizations/.vscode/mcp.json +0 -29
- package/dist/azure-mcp-environment.d.ts +0 -2
- package/dist/azure-mcp-environment.js +0 -7
- package/dist/azure-mcp.d.ts +0 -2
- package/dist/azure-mcp.js +0 -21
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Collects Azure Policy assignments across all subscriptions under a
|
|
4
|
+
Management Group or one subscription and writes a governance-baseline-v2 JSON.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Authenticates via the workflow's azure/login@v2 context, discovers
|
|
8
|
+
descendant subscriptions, collects policy assignments/definitions/
|
|
9
|
+
set-definitions/exemptions per subscription, classifies findings,
|
|
10
|
+
and writes a deterministic baseline.
|
|
11
|
+
|
|
12
|
+
Every successful collection renews discovered_at timestamps,
|
|
13
|
+
including unchanged subscriptions.
|
|
14
|
+
|
|
15
|
+
.PARAMETER ManagementGroupId
|
|
16
|
+
The root Management Group ID to traverse; exclusive with SubscriptionId.
|
|
17
|
+
|
|
18
|
+
.PARAMETER SubscriptionId
|
|
19
|
+
Collect one subscription, including inherited policy, without MG discovery.
|
|
20
|
+
|
|
21
|
+
.PARAMETER OutputDir
|
|
22
|
+
Output directory for baseline files. Default: .github/data
|
|
23
|
+
|
|
24
|
+
.PARAMETER IncludeDefenderAuto
|
|
25
|
+
Switch. If set, retains Defender-for-Cloud auto-assignments.
|
|
26
|
+
|
|
27
|
+
.PARAMETER IncludeDescendants
|
|
28
|
+
Retain resource-group/resource assignments and scoped exemption evidence.
|
|
29
|
+
|
|
30
|
+
.PARAMETER MaxSubscriptions
|
|
31
|
+
Maximum subscriptions to process. Default: 100.
|
|
32
|
+
#>
|
|
33
|
+
[CmdletBinding(DefaultParameterSetName = "ManagementGroup")]
|
|
34
|
+
param(
|
|
35
|
+
[Parameter(Mandatory, ParameterSetName = "ManagementGroup")]
|
|
36
|
+
[ValidateNotNullOrEmpty()]
|
|
37
|
+
[ValidatePattern('\A[a-zA-Z0-9](?:[a-zA-Z0-9_().-]{0,88}[a-zA-Z0-9_()-])?\z')]
|
|
38
|
+
[string]$ManagementGroupId,
|
|
39
|
+
|
|
40
|
+
[Parameter(Mandatory, ParameterSetName = "Subscription")]
|
|
41
|
+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
|
|
42
|
+
[string]$SubscriptionId,
|
|
43
|
+
|
|
44
|
+
[string]$OutputDir = ".github/data",
|
|
45
|
+
|
|
46
|
+
[switch]$IncludeDefenderAuto,
|
|
47
|
+
[switch]$IncludeDescendants,
|
|
48
|
+
|
|
49
|
+
[ValidateRange(1, [int]::MaxValue)]
|
|
50
|
+
[int]$MaxSubscriptions = 100
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# StrictMode intentionally omitted — REST API response objects have dynamic
|
|
54
|
+
# properties that vary per endpoint, and strict property access causes false
|
|
55
|
+
# failures. Error handling is via try/catch + ErrorActionPreference Stop.
|
|
56
|
+
$ErrorActionPreference = "Stop"
|
|
57
|
+
|
|
58
|
+
# ─── Constants ─────────────────────────────────────────────────────────────
|
|
59
|
+
$ARM = "https://management.azure.com"
|
|
60
|
+
$API_ASSIGNMENTS = "2022-06-01"
|
|
61
|
+
$API_DEFINITIONS = "2021-06-01"
|
|
62
|
+
$API_EXEMPTIONS = "2022-07-01-preview"
|
|
63
|
+
$API_MG_DESCENDANTS = "2020-05-01"
|
|
64
|
+
|
|
65
|
+
$BLOCKER_EFFECTS = @("Deny")
|
|
66
|
+
$AUTO_REMEDIATE_EFFECTS = @("DeployIfNotExists", "Modify")
|
|
67
|
+
$RELEVANT_EFFECTS = $BLOCKER_EFFECTS + $AUTO_REMEDIATE_EFFECTS
|
|
68
|
+
$DEFENDER_ASSIGNED_BY = @("Security Center", "Microsoft Defender for Cloud")
|
|
69
|
+
|
|
70
|
+
# ─── ARM Token ───────────────────────────────────────────────────────────────
|
|
71
|
+
function Get-ArmToken {
|
|
72
|
+
$tokenJson = az account get-access-token --resource "$ARM" -o json 2>$null
|
|
73
|
+
if ($LASTEXITCODE -ne 0 -or -not $tokenJson) { throw "Failed to acquire ARM token via az account get-access-token" }
|
|
74
|
+
$token = $tokenJson | ConvertFrom-Json
|
|
75
|
+
if ([string]::IsNullOrWhiteSpace($token.accessToken)) { throw "ARM access token is empty" }
|
|
76
|
+
return $token.accessToken
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Invoke-ArmRest {
|
|
80
|
+
param([string]$Url, [string]$Token)
|
|
81
|
+
$headers = @{ Authorization = "Bearer $Token"; "Content-Type" = "application/json" }
|
|
82
|
+
$allValues = @()
|
|
83
|
+
$currentUrl = $Url
|
|
84
|
+
$visited = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
|
|
85
|
+
do {
|
|
86
|
+
$requestUri = $null
|
|
87
|
+
if (-not [uri]::TryCreate($currentUrl, [UriKind]::Absolute, [ref]$requestUri) -or
|
|
88
|
+
$requestUri.Scheme -ne "https" -or $requestUri.Authority -ne ([uri]$ARM).Authority -or
|
|
89
|
+
$requestUri.UserInfo -or $requestUri.Fragment) {
|
|
90
|
+
throw "Invalid ARM pagination URL: $currentUrl"
|
|
91
|
+
}
|
|
92
|
+
if (-not $visited.Add($requestUri.AbsoluteUri)) { throw "Repeated ARM pagination URL: $currentUrl" }
|
|
93
|
+
try {
|
|
94
|
+
$response = Invoke-RestMethod -Uri $currentUrl -Headers $headers -Method Get -ErrorAction Stop
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw "ARM REST call failed: $currentUrl - $_"
|
|
98
|
+
}
|
|
99
|
+
if ($null -eq $response -or $response.value -isnot [array]) {
|
|
100
|
+
throw "Invalid ARM list response: $currentUrl"
|
|
101
|
+
}
|
|
102
|
+
$allValues += $response.value
|
|
103
|
+
$nextLink = $response.nextLink
|
|
104
|
+
if ($null -ne $nextLink -and $nextLink -isnot [string]) {
|
|
105
|
+
throw "Invalid ARM nextLink: $currentUrl"
|
|
106
|
+
}
|
|
107
|
+
$currentUrl = $nextLink
|
|
108
|
+
} while ($currentUrl)
|
|
109
|
+
return $allValues
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function Invoke-ArmRestSingle {
|
|
113
|
+
param([string]$Url, [string]$Token)
|
|
114
|
+
$headers = @{ Authorization = "Bearer $Token"; "Content-Type" = "application/json" }
|
|
115
|
+
try {
|
|
116
|
+
return Invoke-RestMethod -Uri $Url -Headers $headers -Method Get -ErrorAction Stop
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw "ARM REST single fetch failed: $Url - $_"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function Get-ArmPolicyDefinition {
|
|
124
|
+
param([string]$Id, [hashtable]$Cache, [string]$ResourceType = "policyDefinitions")
|
|
125
|
+
|
|
126
|
+
$idPattern = "^(?:/subscriptions/[^/?#]+|/providers/Microsoft.Management/managementGroups/[^/?#]+)?/providers/Microsoft.Authorization/$ResourceType/[^/?#]+$"
|
|
127
|
+
if ($Id -notmatch $idPattern) { throw "Invalid $ResourceType resource ID: $Id" }
|
|
128
|
+
$cacheKey = $Id.ToLowerInvariant()
|
|
129
|
+
if (-not $Cache.ContainsKey($cacheKey)) {
|
|
130
|
+
$Cache[$cacheKey] = Invoke-ArmRestSingle -Url "$ARM${Id}?api-version=$API_DEFINITIONS" -Token $Token
|
|
131
|
+
}
|
|
132
|
+
$definition = $Cache[$cacheKey]
|
|
133
|
+
if ($null -eq $definition -or $definition.id -ine $Id -or $null -eq $definition.properties) {
|
|
134
|
+
throw "Invalid $ResourceType response for $Id"
|
|
135
|
+
}
|
|
136
|
+
if ($ResourceType -eq "policySetDefinitions") {
|
|
137
|
+
if ($definition.properties.policyDefinitions -isnot [array]) { throw "Invalid initiative members for $Id" }
|
|
138
|
+
}
|
|
139
|
+
elseif (-not $definition.properties.policyRule.then.effect) {
|
|
140
|
+
throw "Missing policy effect for $Id"
|
|
141
|
+
}
|
|
142
|
+
return $definition
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# ─── Classification ────────────────────────────────────────────────────────
|
|
146
|
+
function Get-EffectOf {
|
|
147
|
+
param($Defn, $Parameters, $Initiative, $Assignment, $MemberRefId)
|
|
148
|
+
$props = $Defn.properties
|
|
149
|
+
$eff = $props.policyRule.then.effect
|
|
150
|
+
$overridden = $false
|
|
151
|
+
$appliedOverride = $null
|
|
152
|
+
if ($null -ne $Assignment.properties.overrides -and $Assignment.properties.overrides -isnot [array]) {
|
|
153
|
+
throw "Invalid assignment overrides"
|
|
154
|
+
}
|
|
155
|
+
foreach ($override in $Assignment.properties.overrides) {
|
|
156
|
+
if ($override.kind -ne "policyEffect") { throw "Unsupported assignment override kind: $($override.kind)" }
|
|
157
|
+
if ($null -ne $override.selectors -and $override.selectors -isnot [array]) { throw "Invalid policy effect override selectors" }
|
|
158
|
+
$applies = $true
|
|
159
|
+
foreach ($selector in $override.selectors) {
|
|
160
|
+
if ($selector.kind -ne "policyDefinitionReferenceId" -or -not $Initiative) {
|
|
161
|
+
throw "Unsupported policy effect override selector: $($selector.kind)"
|
|
162
|
+
}
|
|
163
|
+
if (($null -ne $selector.in -and $selector.in -isnot [array]) -or
|
|
164
|
+
($null -ne $selector.notIn -and $selector.notIn -isnot [array]) -or
|
|
165
|
+
($null -eq $selector.in -and $null -eq $selector.notIn)) {
|
|
166
|
+
throw "Invalid policy effect override selector"
|
|
167
|
+
}
|
|
168
|
+
if ($null -ne $selector.in -and $MemberRefId -notin $selector.in) { $applies = $false }
|
|
169
|
+
if ($null -ne $selector.notIn -and $MemberRefId -in $selector.notIn) { $applies = $false }
|
|
170
|
+
}
|
|
171
|
+
if ($applies) {
|
|
172
|
+
if ($overridden) { throw "Overlapping policy effect overrides for $($Defn.id)" }
|
|
173
|
+
$eff = $override.value
|
|
174
|
+
$overridden = $true
|
|
175
|
+
$appliedOverride = $override
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (-not $overridden -and $eff -match "^\[parameters\('([^']+)'\)\]$") {
|
|
179
|
+
$paramName = $Matches[1]
|
|
180
|
+
$binding = if ($null -ne $Parameters) { $Parameters.PSObject.Properties[$paramName] } else { $null }
|
|
181
|
+
if ($null -ne $binding) {
|
|
182
|
+
$eff = $binding.Value.value
|
|
183
|
+
if ($Initiative -and $eff -is [string] -and $eff -match "^\[parameters\('([^']+)'\)\]$") {
|
|
184
|
+
$initiativeParamName = $Matches[1]
|
|
185
|
+
$assignmentParameters = $Assignment.properties.parameters
|
|
186
|
+
$assignmentBinding = if ($null -ne $assignmentParameters) { $assignmentParameters.PSObject.Properties[$initiativeParamName] } else { $null }
|
|
187
|
+
if ($null -ne $assignmentBinding) { $eff = $assignmentBinding.Value.value }
|
|
188
|
+
else { $eff = $Initiative.properties.parameters.$initiativeParamName.defaultValue }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else { $eff = $props.parameters.$paramName.defaultValue }
|
|
192
|
+
}
|
|
193
|
+
if ($eff -isnot [string] -or $eff -notin @("Deny", "DeployIfNotExists", "Modify", "Disabled", "Audit", "AuditIfNotExists", "Append", "Manual", "DenyAction", "Mutate")) {
|
|
194
|
+
throw "Unresolved or unsupported policy effect for $($Defn.id): $eff"
|
|
195
|
+
}
|
|
196
|
+
return @{ effect = [string]$eff; override = $appliedOverride }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function Get-Classification {
|
|
200
|
+
param([string]$Effect)
|
|
201
|
+
if ($BLOCKER_EFFECTS -contains $Effect) { return "blocker" }
|
|
202
|
+
if ($AUTO_REMEDIATE_EFFECTS -contains $Effect) { return "auto-remediate" }
|
|
203
|
+
return "informational"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function Get-EnforcementMode {
|
|
207
|
+
param($Assignment)
|
|
208
|
+
$property = $Assignment.properties.PSObject.Properties['enforcementMode']
|
|
209
|
+
if ($null -eq $property) { return "Default" }
|
|
210
|
+
$mode = $property.Value
|
|
211
|
+
if ($mode -isnot [string] -or $mode -cnotin @("Default", "DoNotEnforce")) {
|
|
212
|
+
throw "Unsupported assignment enforcementMode for $($Assignment.id)"
|
|
213
|
+
}
|
|
214
|
+
return $mode
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function Test-IsDefenderAuto {
|
|
218
|
+
param($Assignment)
|
|
219
|
+
$props = $Assignment.properties
|
|
220
|
+
if (-not $props) { return $false }
|
|
221
|
+
if (-not $props.PSObject.Properties['metadata']) { return $false }
|
|
222
|
+
$metadata = $props.metadata
|
|
223
|
+
if (-not $metadata -or -not $metadata.PSObject.Properties['assignedBy']) { return $false }
|
|
224
|
+
$assignedBy = $metadata.assignedBy
|
|
225
|
+
if (-not $assignedBy) { return $false }
|
|
226
|
+
return ($DEFENDER_ASSIGNED_BY -contains $assignedBy)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function Get-ResourceTypes {
|
|
230
|
+
param($Defn)
|
|
231
|
+
$types = @()
|
|
232
|
+
$rule = $Defn.properties.policyRule
|
|
233
|
+
if (-not $rule) { return $types }
|
|
234
|
+
$ruleJson = $rule | ConvertTo-Json -Depth 20 -Compress
|
|
235
|
+
$matches = [regex]::Matches($ruleJson, '"type"\s*:\s*"(Microsoft\.\w+/\w+(?:/\w+)?)"')
|
|
236
|
+
foreach ($m in $matches) {
|
|
237
|
+
$t = $m.Groups[1].Value
|
|
238
|
+
if ($types -notcontains $t) { $types += $t }
|
|
239
|
+
}
|
|
240
|
+
return $types
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function Get-RequiredValue {
|
|
244
|
+
param($Defn)
|
|
245
|
+
$rule = $Defn.properties.policyRule
|
|
246
|
+
if (-not $rule) { return $null }
|
|
247
|
+
$then = $rule.then
|
|
248
|
+
if (-not $then -or -not $then.details) { return $null }
|
|
249
|
+
$val = $then.details.value
|
|
250
|
+
if ($null -ne $val) { return $val }
|
|
251
|
+
$existCond = $then.details.existenceCondition
|
|
252
|
+
if ($existCond) {
|
|
253
|
+
$condJson = $existCond | ConvertTo-Json -Depth 10 -Compress
|
|
254
|
+
$valMatch = [regex]::Match($condJson, '"(?:equals|in)"\s*:\s*(\[.*?\]|"[^"]*"|\d+|true|false)')
|
|
255
|
+
if ($valMatch.Success) { return $valMatch.Groups[1].Value }
|
|
256
|
+
}
|
|
257
|
+
return $null
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function Get-PropertyPaths {
|
|
261
|
+
param($Defn, $ResourceTypes)
|
|
262
|
+
$result = @{ azurePropertyPath = $null; bicepPropertyPath = $null; pathSemantics = $null }
|
|
263
|
+
$rule = $Defn.properties.policyRule
|
|
264
|
+
if (-not $rule) { return $result }
|
|
265
|
+
$ruleJson = $rule | ConvertTo-Json -Depth 20 -Compress
|
|
266
|
+
# Look for field conditions
|
|
267
|
+
$fieldMatch = [regex]::Match($ruleJson, '"field"\s*:\s*"([^"]+)"')
|
|
268
|
+
if ($fieldMatch.Success) {
|
|
269
|
+
$field = $fieldMatch.Groups[1].Value
|
|
270
|
+
if ($field -match '^Microsoft\.' -or $field -match '^\[') { return $result }
|
|
271
|
+
if ($field -match 'tags\[') {
|
|
272
|
+
$result.pathSemantics = "tag-policy-non-property"
|
|
273
|
+
return $result
|
|
274
|
+
}
|
|
275
|
+
$result.azurePropertyPath = "properties.$field"
|
|
276
|
+
$bicep = $field -replace '\.', '.'
|
|
277
|
+
$result.bicepPropertyPath = $bicep
|
|
278
|
+
}
|
|
279
|
+
return $result
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function Get-TagsRequired {
|
|
283
|
+
param($Findings)
|
|
284
|
+
$seen = @{}
|
|
285
|
+
$tags = @()
|
|
286
|
+
foreach ($f in $Findings) {
|
|
287
|
+
$isTag = ($f.pathSemantics -eq "tag-policy-non-property") -or (($f.category -split '\s')[0] -ieq "Tags")
|
|
288
|
+
if (-not $isTag) { continue }
|
|
289
|
+
$params = $f.assignment_parameters
|
|
290
|
+
$tagKeys = @()
|
|
291
|
+
foreach ($pn in @("tagName", "tagname", "tag_name")) {
|
|
292
|
+
if ($params -and $params.$pn) {
|
|
293
|
+
$v = $params.$pn
|
|
294
|
+
if ($v -is [string]) { $tagKeys += $v }
|
|
295
|
+
elseif ($v -is [array]) { $tagKeys += $v }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
foreach ($pn in @("tagNames", "listOfTagNames", "tagnames")) {
|
|
299
|
+
if ($params -and $params.$pn -and $params.$pn -is [array]) {
|
|
300
|
+
$tagKeys += $params.$pn
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if ($tagKeys.Count -gt 0) {
|
|
304
|
+
foreach ($key in $tagKeys) {
|
|
305
|
+
$key = $key.Trim()
|
|
306
|
+
if ($key -and -not $seen.ContainsKey($key)) {
|
|
307
|
+
$seen[$key] = $true
|
|
308
|
+
$tags += @{ name = $key; source_policy = $f.policy_id; source_assignment = $f.assignment_display_name }
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
$policyName = ($f.display_name -split '\s')[0..99] -join ' '
|
|
314
|
+
if ($policyName -and -not $seen.ContainsKey($policyName)) {
|
|
315
|
+
$seen[$policyName] = $true
|
|
316
|
+
$tags += @{ name = "[unresolved: $policyName]"; source_policy = $f.policy_id; source_assignment = $f.assignment_display_name; unresolved = "true" }
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return $tags
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function Get-AllowedLocations {
|
|
324
|
+
param($Findings)
|
|
325
|
+
$locations = [System.Collections.Generic.HashSet[string]]::new()
|
|
326
|
+
foreach ($f in $Findings) {
|
|
327
|
+
$dn = ($f.display_name -replace '\s', ' ').ToLower()
|
|
328
|
+
$cat = ($f.category -replace '\s', ' ').ToLower()
|
|
329
|
+
$isLocation = ($dn -match 'location') -or ($dn -match 'region') -or ($cat -match 'location')
|
|
330
|
+
if ($isLocation) {
|
|
331
|
+
$rv = $f.required_value
|
|
332
|
+
if ($rv -is [array]) { foreach ($v in $rv) { [void]$locations.Add([string]$v) } }
|
|
333
|
+
elseif ($rv -is [string] -and $rv) { [void]$locations.Add($rv) }
|
|
334
|
+
}
|
|
335
|
+
$params = $f.assignment_parameters
|
|
336
|
+
if ($params) {
|
|
337
|
+
foreach ($pn in @("listOfAllowedLocations", "allowedLocations", "listofallowedlocations", "allowedlocations")) {
|
|
338
|
+
if ($params.$pn -is [array]) {
|
|
339
|
+
foreach ($v in $params.$pn) { if ($v) { [void]$locations.Add([string]$v) } }
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return ($locations | Sort-Object)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# ─── Process a single subscription ──────────────────────────────────────────
|
|
348
|
+
function Process-Subscription {
|
|
349
|
+
param([string]$SubId, [string]$Token)
|
|
350
|
+
|
|
351
|
+
$base = "$ARM/subscriptions/$SubId/providers/Microsoft.Authorization"
|
|
352
|
+
# Policy assignment and exemption names may contain inner spaces, for example "(ArcBox) Tag resources".
|
|
353
|
+
$POLICY_RESOURCE_NAME = '[^/?#%\s\p{Cc}](?:[^/?#%\p{Cc}]*[^/?#%\s\p{Cc}])?'
|
|
354
|
+
$scopePattern = if ($IncludeDescendants) { "(/subscriptions/$SubId(?:/resourceGroups/[^/?#%\s]+)?(?:/providers/[^/?#%\s]+(?:/[^/?#%\s]+/[^/?#%\s]+)+)*|/providers/Microsoft.Management/managementGroups/[^/?#%\s]+)" } else { '(/subscriptions/[0-9a-f-]{36}|/providers/Microsoft.Management/managementGroups/[^/?#\s]+)' }
|
|
355
|
+
|
|
356
|
+
$scopeFilter = if ($IncludeDescendants) { "" } else { '$filter=atScope()&' }
|
|
357
|
+
$assignments = Invoke-ArmRest -Url "$base/policyAssignments?${scopeFilter}api-version=$API_ASSIGNMENTS" -Token $Token
|
|
358
|
+
$subDefs = Invoke-ArmRest -Url "$base/policyDefinitions?api-version=$API_DEFINITIONS" -Token $Token
|
|
359
|
+
$subSets = Invoke-ArmRest -Url "$base/policySetDefinitions?api-version=$API_DEFINITIONS" -Token $Token
|
|
360
|
+
$exemptions = Invoke-ArmRest -Url "$base/policyExemptions?${scopeFilter}api-version=$API_EXEMPTIONS" -Token $Token
|
|
361
|
+
|
|
362
|
+
$defs = @{}
|
|
363
|
+
foreach ($d in $subDefs) { $defs[($d.id).ToLower()] = $d }
|
|
364
|
+
$sets = @{}
|
|
365
|
+
foreach ($s in $subSets) { $sets[($s.id).ToLower()] = $s }
|
|
366
|
+
|
|
367
|
+
# Build exemption map
|
|
368
|
+
$exemptionMap = @{}
|
|
369
|
+
$collectionTime = [DateTimeOffset]::UtcNow
|
|
370
|
+
$expiredExemptionCount = 0
|
|
371
|
+
foreach ($ex in $exemptions) {
|
|
372
|
+
$exProps = $ex.properties
|
|
373
|
+
if (-not $exProps -or -not $exProps.policyAssignmentId -or $exProps.exemptionCategory -notin @("Waiver", "Mitigated")) {
|
|
374
|
+
throw "Invalid policy exemption"
|
|
375
|
+
}
|
|
376
|
+
if ($ex.id -isnot [string] -or $ex.id -notmatch "^$scopePattern/providers/Microsoft.Authorization/policyExemptions/$POLICY_RESOURCE_NAME\z") {
|
|
377
|
+
throw "Invalid or unsupported policy exemption scope"
|
|
378
|
+
}
|
|
379
|
+
$exemptionScope = $Matches[1]
|
|
380
|
+
if ($exProps.policyAssignmentId -isnot [string] -or $exProps.policyAssignmentId -notmatch "^$scopePattern/providers/Microsoft.Authorization/policyAssignments/$POLICY_RESOURCE_NAME\z") {
|
|
381
|
+
throw "Invalid policy exemption assignment identity"
|
|
382
|
+
}
|
|
383
|
+
$assignmentScope = $Matches[1]
|
|
384
|
+
if ($IncludeDescendants -and $assignmentScope -notmatch '^/providers/Microsoft.Management/' -and
|
|
385
|
+
$exemptionScope -ine $assignmentScope -and -not $exemptionScope.StartsWith("$assignmentScope/", [StringComparison]::OrdinalIgnoreCase)) {
|
|
386
|
+
throw "Policy exemption scope is outside its assignment"
|
|
387
|
+
}
|
|
388
|
+
if (-not ($IncludeDescendants -and $exemptionScope.StartsWith("/subscriptions/$SubId/", [StringComparison]::OrdinalIgnoreCase)) -and $exemptionScope -ine "/subscriptions/$SubId" -and
|
|
389
|
+
($exemptionScope -notmatch '^/providers/Microsoft.Management/managementGroups/' -or $exemptionScope -ine $assignmentScope)) {
|
|
390
|
+
throw "Policy exemption scope does not cover the collected subscription"
|
|
391
|
+
}
|
|
392
|
+
if ($null -ne $exProps.expiresOn) {
|
|
393
|
+
$expiresAt = [DateTimeOffset]::MinValue
|
|
394
|
+
if ($exProps.expiresOn -is [datetime]) {
|
|
395
|
+
if ($exProps.expiresOn.Kind -eq [DateTimeKind]::Unspecified) { throw "Invalid policy exemption expiresOn timezone" }
|
|
396
|
+
$expiresAt = [DateTimeOffset]$exProps.expiresOn
|
|
397
|
+
}
|
|
398
|
+
elseif ($exProps.expiresOn -isnot [string] -or
|
|
399
|
+
$exProps.expiresOn -notmatch '^\d{4}-\d{2}-\d{2}T.+(?:Z|[+-]\d{2}:\d{2})$' -or
|
|
400
|
+
-not [DateTimeOffset]::TryParse($exProps.expiresOn, [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::None, [ref]$expiresAt)) {
|
|
401
|
+
throw "Invalid policy exemption expiresOn"
|
|
402
|
+
}
|
|
403
|
+
if ($expiresAt -le $collectionTime) { $expiredExemptionCount++; continue }
|
|
404
|
+
}
|
|
405
|
+
if ($exProps.resourceSelectors) { throw "Unsupported policy exemption resourceSelectors" }
|
|
406
|
+
if ($null -ne $exProps.policyDefinitionReferenceIds -and $exProps.policyDefinitionReferenceIds -isnot [array]) {
|
|
407
|
+
throw "Invalid policy exemption member references"
|
|
408
|
+
}
|
|
409
|
+
$asgId = $exProps.policyAssignmentId.ToLower()
|
|
410
|
+
$exemptionMap[$asgId] = @($exemptionMap[$asgId]) + @(@{
|
|
411
|
+
id = $ex.id
|
|
412
|
+
scope = $exemptionScope
|
|
413
|
+
expiresOn = $exProps.expiresOn
|
|
414
|
+
category = $exProps.exemptionCategory
|
|
415
|
+
policyDefinitionReferenceIds = $exProps.policyDefinitionReferenceIds
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
# Filter Defender auto-assignments
|
|
420
|
+
$filteredDefender = @()
|
|
421
|
+
$keptAssignments = @()
|
|
422
|
+
$notScopeExcludedCount = 0
|
|
423
|
+
$targetScope = "/subscriptions/$SubId"
|
|
424
|
+
foreach ($a in $assignments) {
|
|
425
|
+
if ($IncludeDescendants -and ($a.properties.scope -notmatch "^$scopePattern\z" -or $a.properties.scope -match '/\.{1,2}(?:/|$)')) {
|
|
426
|
+
throw "Invalid policy assignment scope"
|
|
427
|
+
}
|
|
428
|
+
if ($a.id -isnot [string] -or $a.id -notmatch "^(.+)/providers/Microsoft.Authorization/policyAssignments/$POLICY_RESOURCE_NAME\z" -or
|
|
429
|
+
$Matches[1] -ine $a.properties.scope) {
|
|
430
|
+
throw "Invalid policy assignment identity"
|
|
431
|
+
}
|
|
432
|
+
$notScopes = $a.properties.notScopes
|
|
433
|
+
if ($null -ne $notScopes -and $notScopes -isnot [array]) { throw "Invalid assignment notScopes" }
|
|
434
|
+
$normalizedNotScopes = @(
|
|
435
|
+
foreach ($excludedScope in $notScopes) {
|
|
436
|
+
if ($excludedScope -isnot [string] -or $excludedScope -notmatch '^/(?:subscriptions/[0-9a-f-]{36}(?:/[^?#]+)?|providers/Microsoft.Management/managementGroups/[^/?#]+?)/?\z') {
|
|
437
|
+
throw "Invalid assignment notScopes entry"
|
|
438
|
+
}
|
|
439
|
+
$excludedScope.TrimEnd('/')
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
if ($targetScope -in $normalizedNotScopes -or $a.properties.scope -in $normalizedNotScopes) {
|
|
443
|
+
$notScopeExcludedCount++
|
|
444
|
+
continue
|
|
445
|
+
}
|
|
446
|
+
foreach ($excludedScope in $normalizedNotScopes) {
|
|
447
|
+
if ($excludedScope.StartsWith("$targetScope/", [StringComparison]::OrdinalIgnoreCase)) {
|
|
448
|
+
throw "Partial subscription notScopes cannot be represented: $excludedScope"
|
|
449
|
+
}
|
|
450
|
+
if ($excludedScope -notmatch '^/subscriptions/') {
|
|
451
|
+
throw "Unresolved management group notScopes: $excludedScope"
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if ($a.properties.resourceSelectors) { throw "Unsupported assignment resourceSelectors" }
|
|
455
|
+
if ($null -ne $a.properties.PSObject.Properties['definitionVersion']) {
|
|
456
|
+
throw "Unsupported assignment definitionVersion"
|
|
457
|
+
}
|
|
458
|
+
$null = Get-EnforcementMode $a
|
|
459
|
+
if ((Test-IsDefenderAuto $a) -and -not $IncludeDefenderAuto -and -not $IncludeDescendants) {
|
|
460
|
+
$filteredDefender += ($a.properties.displayName ?? $a.name ?? $a.id ?? "<unknown>")
|
|
461
|
+
}
|
|
462
|
+
else { $keptAssignments += $a }
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
$assignmentInventory = @()
|
|
466
|
+
$findings = @()
|
|
467
|
+
$auditCount = 0
|
|
468
|
+
$disabledCount = 0
|
|
469
|
+
$classifiedPolicyCount = 0
|
|
470
|
+
$otherEffectCount = 0
|
|
471
|
+
|
|
472
|
+
foreach ($a in $keptAssignments) {
|
|
473
|
+
$props = $a.properties
|
|
474
|
+
$display = $props.displayName ?? $a.name ?? $a.id ?? "<unknown>"
|
|
475
|
+
$scope = $props.scope ?? ""
|
|
476
|
+
$policyDefId = ($props.policyDefinitionId ?? "").ToLower()
|
|
477
|
+
$assignmentId = ($a.id ?? "").ToLower()
|
|
478
|
+
$assignmentType = if ($scope.ToLower() -match '/providers/microsoft.management/managementgroups/') { "management-group" } else { "subscription" }
|
|
479
|
+
$enforcementMode = Get-EnforcementMode $a
|
|
480
|
+
|
|
481
|
+
$assignmentInventory += @{
|
|
482
|
+
assignmentId = $a.id
|
|
483
|
+
displayName = $display
|
|
484
|
+
scope = $scope
|
|
485
|
+
assignmentType = $assignmentType
|
|
486
|
+
policyDefinitionId = $policyDefId
|
|
487
|
+
enforcementMode = $enforcementMode
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (-not $policyDefId) { throw "Missing policyDefinitionId for assignment $assignmentId" }
|
|
491
|
+
|
|
492
|
+
# Resolve members
|
|
493
|
+
$members = @()
|
|
494
|
+
$policySet = $null
|
|
495
|
+
if ($policyDefId -match '/policysetdefinitions/') {
|
|
496
|
+
$policySet = Get-ArmPolicyDefinition -Id $policyDefId -Cache $sets -ResourceType "policySetDefinitions"
|
|
497
|
+
foreach ($setMember in $policySet.properties.policyDefinitions) {
|
|
498
|
+
if ($null -ne $setMember.PSObject.Properties['definitionVersion']) {
|
|
499
|
+
throw "Unsupported initiative member definitionVersion"
|
|
500
|
+
}
|
|
501
|
+
$memberDefinition = Get-ArmPolicyDefinition -Id $setMember.policyDefinitionId -Cache $defs
|
|
502
|
+
$members += @{ defn = $memberDefinition; memberRefId = $setMember.policyDefinitionReferenceId; parameters = $setMember.parameters }
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
$policyDefinition = Get-ArmPolicyDefinition -Id $policyDefId -Cache $defs
|
|
507
|
+
$members += @{ defn = $policyDefinition; memberRefId = $null; parameters = $props.parameters }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
foreach ($member in $members) {
|
|
511
|
+
$defn = $member.defn
|
|
512
|
+
$memberRefId = $member.memberRefId
|
|
513
|
+
$effectivePolicy = Get-EffectOf -Defn $defn -Parameters $member.parameters -Initiative $policySet -Assignment $a -MemberRefId $memberRefId
|
|
514
|
+
$eff = $effectivePolicy.effect
|
|
515
|
+
$classifiedPolicyCount++
|
|
516
|
+
if ($eff -eq "Disabled") { $disabledCount++; if (-not $IncludeDescendants) { continue } }
|
|
517
|
+
if ($eff -in @("Audit", "AuditIfNotExists")) { $auditCount++; if (-not $IncludeDescendants) { continue } }
|
|
518
|
+
if ($IncludeDescendants -and $eff -notin ($RELEVANT_EFFECTS + @("Disabled", "Audit", "AuditIfNotExists", "Append"))) { throw "Unsupported policy effect" }
|
|
519
|
+
if (-not $IncludeDescendants -and $eff -notin $RELEVANT_EFFECTS) { $otherEffectCount++; continue }
|
|
520
|
+
|
|
521
|
+
$rtypes = Get-ResourceTypes $defn
|
|
522
|
+
$paths = Get-PropertyPaths $defn $rtypes
|
|
523
|
+
$category = $defn.properties.metadata.category ?? "Uncategorized"
|
|
524
|
+
|
|
525
|
+
$exemption = $null
|
|
526
|
+
$reportedExemptions = @()
|
|
527
|
+
if ($exemptionMap.ContainsKey($assignmentId)) {
|
|
528
|
+
foreach ($candidate in $exemptionMap[$assignmentId]) {
|
|
529
|
+
if ($null -eq $candidate) { continue }
|
|
530
|
+
$refIds = $candidate.policyDefinitionReferenceIds
|
|
531
|
+
if (-not $refIds -or $memberRefId -in $refIds) {
|
|
532
|
+
$reportedExemptions += $candidate
|
|
533
|
+
if (-not $IncludeDescendants) {
|
|
534
|
+
$exemption = $candidate
|
|
535
|
+
}
|
|
536
|
+
if (-not $IncludeDescendants) { break }
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
$classification = Get-Classification $eff
|
|
542
|
+
if ($exemption) { $classification = "informational" }
|
|
543
|
+
|
|
544
|
+
$effectLower = $eff.Substring(0,1).ToLower() + $eff.Substring(1)
|
|
545
|
+
|
|
546
|
+
$finding = [ordered]@{
|
|
547
|
+
policy_id = $defn.id
|
|
548
|
+
display_name = $defn.properties.displayName ?? $defn.name ?? $defn.id
|
|
549
|
+
effect = $effectLower
|
|
550
|
+
enforcementMode = $enforcementMode
|
|
551
|
+
scope = $scope
|
|
552
|
+
assignment_display_name = $display
|
|
553
|
+
assignment_id = $a.id
|
|
554
|
+
classification = $classification
|
|
555
|
+
category = $category
|
|
556
|
+
resource_types = @($rtypes)
|
|
557
|
+
required_value = (Get-RequiredValue $defn)
|
|
558
|
+
azurePropertyPath = $paths.azurePropertyPath
|
|
559
|
+
bicepPropertyPath = $paths.bicepPropertyPath
|
|
560
|
+
exemption = $exemption
|
|
561
|
+
override = $effectivePolicy.override
|
|
562
|
+
}
|
|
563
|
+
if ($policySet) { $finding.policyDefinitionReferenceId = $memberRefId }
|
|
564
|
+
if ($IncludeDescendants) { $finding.reported_exemptions = @($reportedExemptions) }
|
|
565
|
+
|
|
566
|
+
# Assignment parameters
|
|
567
|
+
$assignmentParams = $props.parameters
|
|
568
|
+
$paramValues = [ordered]@{}
|
|
569
|
+
$parameterSource = if ($policySet) { $policySet } else { $defn }
|
|
570
|
+
foreach ($parameter in $parameterSource.properties.parameters.PSObject.Properties) {
|
|
571
|
+
if ($parameter.Value.PSObject.Properties['defaultValue']) {
|
|
572
|
+
$paramValues[$parameter.Name] = $parameter.Value.defaultValue
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
foreach ($parameter in $assignmentParams.PSObject.Properties) {
|
|
576
|
+
$paramValues[$parameter.Name] = $parameter.Value.value
|
|
577
|
+
}
|
|
578
|
+
if ($policySet) {
|
|
579
|
+
$memberValues = [ordered]@{}
|
|
580
|
+
foreach ($parameter in $defn.properties.parameters.PSObject.Properties) {
|
|
581
|
+
if ($parameter.Value.PSObject.Properties['defaultValue']) {
|
|
582
|
+
$memberValues[$parameter.Name] = $parameter.Value.defaultValue
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
foreach ($parameter in $member.parameters.PSObject.Properties) {
|
|
586
|
+
$val = $parameter.Value.value
|
|
587
|
+
if ($val -is [string] -and $val -match "^\[parameters\('([^']+)'\)\]$" -and $paramValues.Contains($Matches[1])) {
|
|
588
|
+
$val = $paramValues[$Matches[1]]
|
|
589
|
+
}
|
|
590
|
+
$memberValues[$parameter.Name] = $val
|
|
591
|
+
}
|
|
592
|
+
# A member's rule reads only its own parameters, already resolved from the initiative's values.
|
|
593
|
+
# Storing every initiative parameter on every member made baselines too large to import.
|
|
594
|
+
$paramValues = $memberValues
|
|
595
|
+
}
|
|
596
|
+
if ($paramValues.Count -gt 0) { $finding.assignment_parameters = $paramValues }
|
|
597
|
+
if ($paths.pathSemantics) { $finding.pathSemantics = $paths.pathSemantics }
|
|
598
|
+
$findings += $finding
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
$blockerCount = @($findings | Where-Object { $_.classification -eq "blocker" }).Count
|
|
603
|
+
$autoRemediateCount = @($findings | Where-Object { $_.classification -eq "auto-remediate" }).Count
|
|
604
|
+
$exemptedCount = @($findings | Where-Object { $null -ne $_.exemption }).Count
|
|
605
|
+
$infoCount = @($findings | Where-Object { $_.classification -eq "informational" }).Count
|
|
606
|
+
$subScopeCount = ($keptAssignments | Where-Object { $_.properties.scope -and $_.properties.scope.ToLower() -notmatch '/providers/microsoft.management/' }).Count
|
|
607
|
+
$mgInheritedCount = ($keptAssignments | Where-Object { $_.properties.scope -and $_.properties.scope.ToLower() -match '/providers/microsoft.management/' }).Count
|
|
608
|
+
|
|
609
|
+
$tagsRequired = Get-TagsRequired $findings
|
|
610
|
+
$allowedLocations = Get-AllowedLocations $findings
|
|
611
|
+
|
|
612
|
+
# L0 attestation envelope (plan-optimiseGovernanceAgent.prompt.md Phase 3b).
|
|
613
|
+
# We emit every field except `completeness_signature` here; the cached
|
|
614
|
+
# renderer recomputes the signature via the canonical Python helper so
|
|
615
|
+
# the cached path stays byte-identical to the live path for the same
|
|
616
|
+
# upstream findings (F2 decision — single signature algorithm).
|
|
617
|
+
$discoveredAtIso = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ" -AsUTC)
|
|
618
|
+
$managementGroups = @(
|
|
619
|
+
$keptAssignments
|
|
620
|
+
| Where-Object { $_.properties.scope -and ($_.properties.scope.ToLower() -match '/providers/microsoft.management/managementgroups/') }
|
|
621
|
+
| ForEach-Object {
|
|
622
|
+
$marker = '/providers/microsoft.management/managementgroups/'
|
|
623
|
+
$scope = $_.properties.scope.ToLower()
|
|
624
|
+
($scope -split [regex]::Escape($marker), 2)[1] -split '/', 2 | Select-Object -First 1
|
|
625
|
+
}
|
|
626
|
+
| Where-Object { $_ }
|
|
627
|
+
| Select-Object -Unique
|
|
628
|
+
)
|
|
629
|
+
$discoveryMetadata = [ordered]@{
|
|
630
|
+
discovery_status = "COMPLETE"
|
|
631
|
+
discovered_at = $discoveredAtIso
|
|
632
|
+
scope = [ordered]@{
|
|
633
|
+
subscription_id = $SubId
|
|
634
|
+
management_groups = @($managementGroups)
|
|
635
|
+
}
|
|
636
|
+
api_versions = [ordered]@{
|
|
637
|
+
policyAssignments = $API_ASSIGNMENTS
|
|
638
|
+
policyDefinitions = $API_DEFINITIONS
|
|
639
|
+
policyExemptions = $API_EXEMPTIONS
|
|
640
|
+
}
|
|
641
|
+
page_counts = [ordered]@{
|
|
642
|
+
policyAssignments = $assignments.Count
|
|
643
|
+
policyDefinitions = $defs.Count
|
|
644
|
+
policyExemptions = $exemptions.Count
|
|
645
|
+
}
|
|
646
|
+
# Left blank intentionally — render_cached_governance.py recomputes
|
|
647
|
+
# this via the canonical _completeness_signature helper.
|
|
648
|
+
completeness_signature = ""
|
|
649
|
+
ttl_days = 30
|
|
650
|
+
}
|
|
651
|
+
if ($IncludeDescendants) { $discoveryMetadata.scope.coverage = "subscription-and-descendants-v1" }
|
|
652
|
+
|
|
653
|
+
return [ordered]@{
|
|
654
|
+
schema_version = "governance-constraints-v1"
|
|
655
|
+
subscription_id = $SubId
|
|
656
|
+
discovered_at = $discoveredAtIso
|
|
657
|
+
source = "github-actions-baseline"
|
|
658
|
+
discovery_status = "COMPLETE"
|
|
659
|
+
discovery_metadata = $discoveryMetadata
|
|
660
|
+
discovery_summary = [ordered]@{
|
|
661
|
+
assignment_total = $assignments.Count
|
|
662
|
+
assignment_kept = $keptAssignments.Count
|
|
663
|
+
defender_auto_filtered = $filteredDefender.Count
|
|
664
|
+
not_scope_excluded = $notScopeExcludedCount
|
|
665
|
+
expired_exemption_count = $expiredExemptionCount
|
|
666
|
+
subscription_scope_count = $subScopeCount
|
|
667
|
+
management_group_inherited_count = $mgInheritedCount
|
|
668
|
+
blocker_count = $blockerCount
|
|
669
|
+
auto_remediate_count = $autoRemediateCount
|
|
670
|
+
informational_count = $infoCount
|
|
671
|
+
audit_count = $auditCount
|
|
672
|
+
disabled_count = $disabledCount
|
|
673
|
+
classified_policy_count = $classifiedPolicyCount
|
|
674
|
+
other_effect_count = $otherEffectCount
|
|
675
|
+
exempted_count = $exemptedCount
|
|
676
|
+
}
|
|
677
|
+
assignment_inventory = @($assignmentInventory)
|
|
678
|
+
findings = @($findings)
|
|
679
|
+
tags_required = @($tagsRequired)
|
|
680
|
+
allowed_locations = @($allowedLocations)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
# ─── Main ────────────────────────────────────────────────────────────────────
|
|
685
|
+
$standalone = $PSCmdlet.ParameterSetName -eq "Subscription"
|
|
686
|
+
Write-Host "Governance Baseline Collector: $(if ($standalone) { $SubscriptionId } else { $ManagementGroupId })"
|
|
687
|
+
|
|
688
|
+
$token = Get-ArmToken
|
|
689
|
+
Write-Host "ARM token acquired"
|
|
690
|
+
|
|
691
|
+
# Discover descendant subscriptions
|
|
692
|
+
if ($standalone) {
|
|
693
|
+
$allSubs = @([pscustomobject]@{ name = $SubscriptionId })
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
$descendantsUrl = "$ARM/providers/Microsoft.Management/managementGroups/$ManagementGroupId/descendants?api-version=$API_MG_DESCENDANTS"
|
|
697
|
+
$descendants = Invoke-ArmRest -Url $descendantsUrl -Token $token
|
|
698
|
+
$allSubs = @($descendants | Where-Object { $_.type -eq "Microsoft.Management/managementGroups/subscriptions" } | Sort-Object { $_.name })
|
|
699
|
+
Write-Host "Discovered $($allSubs.Count) subscriptions under MG: $ManagementGroupId"
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
# Exclusion checks
|
|
703
|
+
$subscriptionsExcluded = @()
|
|
704
|
+
$eligibleSubs = @()
|
|
705
|
+
foreach ($sub in $allSubs) {
|
|
706
|
+
$subId = $sub.name
|
|
707
|
+
try {
|
|
708
|
+
$subDetail = Invoke-RestMethod -Uri "$ARM/subscriptions/${subId}?api-version=2022-12-01" `
|
|
709
|
+
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
|
|
710
|
+
-Method Get -ErrorAction Stop
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
throw "Failed to read subscription $subId - $_"
|
|
714
|
+
}
|
|
715
|
+
$state = $subDetail.subscriptionPolicies.spendingLimit ?? $subDetail.state ?? "Unknown"
|
|
716
|
+
if ($standalone -and $subDetail.state -ne "Enabled") {
|
|
717
|
+
throw "Standalone subscription $subId is not enabled"
|
|
718
|
+
}
|
|
719
|
+
if ($subDetail.state -eq "Disabled") {
|
|
720
|
+
$subscriptionsExcluded += [ordered]@{ subscription_id = $subId; reason = "disabled" }
|
|
721
|
+
Write-Warning "Excluding $subId — disabled"
|
|
722
|
+
continue
|
|
723
|
+
}
|
|
724
|
+
$quotaId = $subDetail.subscriptionPolicies.quotaId ?? ""
|
|
725
|
+
if ($quotaId -like "AAD_*") {
|
|
726
|
+
if ($standalone) { throw "Standalone subscription $subId has AAD quota: $quotaId" }
|
|
727
|
+
$subscriptionsExcluded += [ordered]@{ subscription_id = $subId; reason = "AAD_quota"; quota_id = $quotaId }
|
|
728
|
+
Write-Warning "Excluding $subId — AAD quota: $quotaId"
|
|
729
|
+
continue
|
|
730
|
+
}
|
|
731
|
+
$eligibleSubs += $subId
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
# Apply cap
|
|
735
|
+
$subscriptionsSkipped = @()
|
|
736
|
+
$subsToProcess = $eligibleSubs
|
|
737
|
+
if ($eligibleSubs.Count -gt $MaxSubscriptions) {
|
|
738
|
+
$subsToProcess = $eligibleSubs[0..($MaxSubscriptions - 1)]
|
|
739
|
+
$subscriptionsSkipped = $eligibleSubs[$MaxSubscriptions..($eligibleSubs.Count - 1)]
|
|
740
|
+
Write-Warning "Cap applied: processing $MaxSubscriptions of $($eligibleSubs.Count) eligible. Skipping $($subscriptionsSkipped.Count)."
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
$coverageStatus = if ($subscriptionsSkipped.Count -gt 0) { "PARTIAL" } else { "COMPLETE" }
|
|
744
|
+
|
|
745
|
+
$baselineFile = Join-Path $OutputDir "governance-policy-baseline.json"
|
|
746
|
+
|
|
747
|
+
# Process subscriptions
|
|
748
|
+
$subscriptions = [ordered]@{}
|
|
749
|
+
$totalProcessed = 0
|
|
750
|
+
$subIndex = 0
|
|
751
|
+
foreach ($subId in $subsToProcess) {
|
|
752
|
+
$subIndex++
|
|
753
|
+
Write-Host "Processing subscription ${subIndex}/$($subsToProcess.Count): $subId"
|
|
754
|
+
try {
|
|
755
|
+
$envelope = Process-Subscription -SubId $subId -Token $token
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
throw "Failed to process subscription $subId - $_"
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
$subscriptions[$subId] = $envelope
|
|
762
|
+
$totalProcessed++
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
# Build summary
|
|
766
|
+
$totalBlockers = 0
|
|
767
|
+
$totalAutoRemediate = 0
|
|
768
|
+
$totalFindings = 0
|
|
769
|
+
foreach ($sub in $subscriptions.Values) {
|
|
770
|
+
$totalBlockers += $sub.discovery_summary.blocker_count
|
|
771
|
+
$totalAutoRemediate += $sub.discovery_summary.auto_remediate_count
|
|
772
|
+
$totalFindings += $sub.findings.Count
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
$baseline = [ordered]@{
|
|
776
|
+
schema_version = "governance-baseline-v2"
|
|
777
|
+
coverage_status = $coverageStatus
|
|
778
|
+
subscriptions_discovered = $allSubs.Count
|
|
779
|
+
subscriptions_processed = $totalProcessed
|
|
780
|
+
subscriptions_skipped = @($subscriptionsSkipped)
|
|
781
|
+
subscriptions_excluded = @($subscriptionsExcluded)
|
|
782
|
+
summary = [ordered]@{
|
|
783
|
+
total_findings = $totalFindings
|
|
784
|
+
total_blockers = $totalBlockers
|
|
785
|
+
total_auto_remediate = $totalAutoRemediate
|
|
786
|
+
subscriptions_complete = $totalProcessed
|
|
787
|
+
}
|
|
788
|
+
subscriptions = $subscriptions
|
|
789
|
+
}
|
|
790
|
+
if ($standalone) { $baseline.subscription_id = $SubscriptionId }
|
|
791
|
+
else { $baseline.management_group_id = $ManagementGroupId }
|
|
792
|
+
|
|
793
|
+
# Write output
|
|
794
|
+
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
|
795
|
+
$baselineFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($baselineFile)
|
|
796
|
+
$stagingFile = Join-Path ([System.IO.Path]::GetDirectoryName($baselineFile)) ".governance-policy-baseline.$([guid]::NewGuid().ToString('N')).tmp"
|
|
797
|
+
try {
|
|
798
|
+
$baselineJson = $baseline | ConvertTo-Json -Depth 50
|
|
799
|
+
$baselineJson | Set-Content -LiteralPath $stagingFile -NoNewline
|
|
800
|
+
[System.IO.File]::Move($stagingFile, $baselineFile, $true)
|
|
801
|
+
}
|
|
802
|
+
finally {
|
|
803
|
+
[System.IO.File]::Delete($stagingFile)
|
|
804
|
+
}
|
|
805
|
+
Write-Host "Wrote baseline: $baselineFile"
|
|
806
|
+
|
|
807
|
+
# Write raw debug file (gitignored — not committed)
|
|
808
|
+
$rawFile = Join-Path $OutputDir "governance-policy-raw.json"
|
|
809
|
+
try {
|
|
810
|
+
$baseline | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $rawFile -NoNewline
|
|
811
|
+
Write-Host "Wrote raw debug: $rawFile (gitignored)"
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
Write-Warning "Could not write optional raw debug file: $_" -WarningAction Continue
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
Write-Host "Done. Coverage: $coverageStatus | Processed: $totalProcessed | Excluded: $($subscriptionsExcluded.Count) | Skipped: $($subscriptionsSkipped.Count)"
|