Preflight check: detect Azure Policy blocking local authentication#7179
Preflight check: detect Azure Policy blocking local authentication#7179
Conversation
Add a local preflight check that detects Azure Policy assignments which deny resources with local authentication enabled (disableLocalAuth != true). The check: - Lists policy assignments on the target subscription via armpolicy SDK - Fetches policy definitions and inspects policyRule for disableLocalAuth field conditions with deny effect - Handles parameterized effects, policy sets (initiatives), and nested allOf/anyOf conditions - Cross-references affected resource types against the Bicep snapshot - Reports a warning (not error) when template resources would be blocked Also handles storage accounts which use allowSharedKeyAccess (inverted logic) instead of disableLocalAuth. Fixes #7177 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new local (client-side) preflight check in azd provision that warns when Azure Policy assignments on the target subscription are likely to deny deployments of resources that have local authentication enabled, helping users avoid late RequestDisallowedByPolicy failures.
Changes:
- Introduces
PolicyService(Azure Policy assignments/definitions enumeration + rule parsing) to detect deny policies related todisableLocalAuth/allowSharedKeyAccess. - Adds a Bicep preflight check that compares detected deny policies against
bicep snapshotpredicted resources and emits a warning when violations are found. - Registers the new service in the IoC container and adds the
armpolicySDK dependency + cspell overrides.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go |
Registers and implements the new local preflight check that warns based on snapshot resources and detected deny policies. |
cli/azd/pkg/azapi/policy_service.go |
New Azure Policy service to list assignments, fetch definitions (including initiatives), and parse policy rules for local-auth deny patterns. |
cli/azd/pkg/azapi/policy_service_test.go |
Unit tests for effect resolution, rule parsing helpers, and resource property evaluation. |
cli/azd/cmd/container.go |
Adds IoC registration for PolicyService. |
cli/azd/go.mod |
Adds armpolicy dependency (currently marked indirect). |
cli/azd/go.sum |
Adds checksums for the new armpolicy dependency. |
cli/azd/.vscode/cspell.yaml |
Adds spelling overrides for policy-related identifiers/terms. |
You can also share your feedback on Copilot code review. Take the survey.
- Fix extractParameterReference whitespace bug: use trimmed string for both prefix check and inner extraction - Remove localAuthEnabled from isLocalAuthField to avoid false positives (ResourceHasLocalAuthDisabled doesn't handle it) - Handle multiple resource types in policy 'in' array (was only taking first entry) - Thread resolved resourceType through recursive findInCompoundCondition calls so nested conditions inherit parent's resource type - Use []LocalAuthDenyPolicy per resource type to avoid overwriting when multiple deny policies target the same type - Add in-memory cache for policy definition/set definition fetches to avoid duplicate API calls across assignments - Improve warning message to mention both disableLocalAuth and allowSharedKeyAccess for storage accounts - Add tests for whitespace parameter extraction, multi-type 'in' arrays, and nested condition resource type inheritance - Run go mod tidy to move armpolicy to direct dependency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds log.Printf statements at key decision points so the flow can be traced with --debug to diagnose why the check might not fire: - Number of policy assignments found - Policy rule parse failures - Number of deny policies found - Per-resource type matching and localAuthDisabled status - Effect type mismatches Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The subscription-level API lists inherited policy assignments from
management groups, but the definition IDs reference management group
scope (e.g. /providers/Microsoft.Management/managementGroups/{mgId}/...).
Previously, getPolicyDefinitionByID and getPolicySetDefinitionByID only
handled built-in and subscription-scoped definitions, silently failing
to fetch management-group-scoped ones. This caused the check to miss
policies like 'Storage Accounts - Safe Secrets Standard' which are
typically assigned at the management group level.
Fix: detect management group scope from the definition ID and use
GetAtManagementGroup SDK method. Also add diagnostic logging throughout
the check pipeline to aid debugging.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove subscription ID (redundant, already shown in context) - Remove unresolved ARM expression resource names (not helpful) - Aggregate by resource type instead of per-resource - Deduplicate policy names - Shorter, cleaner message format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
wbreza
left a comment
There was a problem hiding this comment.
Code Review - PR #7179
What Looks Good
- Clean integration with the existing preflight framework - follows the same
PreflightCheckFnpattern ascheckRoleAssignmentPermissions - Well-structured
PolicyServicewith proper separation of concerns - ~90% of new code is inpkg/azapi/policy_service.go, reusable outside of Bicep - Excellent test coverage for policy rule parsing - 15+ test cases covering deny literals, parameterized effects, nested conditions, multi-type arrays, resource type inheritance, and edge cases
- Definition caching prevents redundant API calls; management-group scope handling is a nice touch for enterprise subs
- Modern Go patterns throughout -
maps.Copy,slices.Sorted(maps.Keys(...)),t.Parallel(),strings.Cut - All 11 copilot review issues addressed thoroughly
Summary
| Priority | Count |
|---|---|
| Medium | 3 |
| Low | 1 |
| Total | 4 |
Overall Assessment: Approve. Well-implemented feature with strong test coverage and clean integration. The main item worth considering is including policy names in the warning to make it more actionable.
Review performed with GitHub Copilot CLI
| "an Azure Policy on this subscription denies resources with local authentication enabled. "+ | ||
| "The following resource types in this deployment may be blocked:\n%s\n"+ | ||
| "Disable local authentication on these resources or request a policy exemption.", | ||
| strings.Join(lines, "\n"), |
There was a problem hiding this comment.
[Medium] Warning message omits policy names - less actionable for users
The policiesByType map stores policy names per resource type, but the warning only lists affected resource types. Users can't easily identify which policy is blocking them.
Consider including policy names in the output to make the warning more actionable:
for _, t := range typeList {
policies := policiesByType[strings.ToLower(t)]
names := make([]string, 0, len(policies))
for _, p := range policies {
names = append(names, p.PolicyName)
}
lines = append(lines, fmt.Sprintf(" - %s (policy: %s)", t, strings.Join(names, ", ")))
}|
|
||
| // Deploys the specified Bicep module and parameters with the selected provisioning scope (subscription vs resource group) | ||
| func (p *BicepProvider) deployModule( | ||
| ctx context.Context, |
There was a problem hiding this comment.
[Medium] No unit tests for checkLocalAuthPolicy integration logic
PolicyService parsing is well-tested (15+ cases), but this integration function has no test coverage. Untested paths: empty SnapshotResources exit, PolicyService resolution failure, API error fallback, no matching resource types, and warning message formatting.
Consider adding tests with mocked PolicyService and SnapshotResources.
|
|
||
| // Register the local auth policy check. This detects Azure Policy assignments | ||
| // that deny resources with local authentication enabled (disableLocalAuth != true) | ||
| // and warns if the template contains affected resources. |
There was a problem hiding this comment.
[Medium] Merge conflict expected with PR #7174
Both PRs modify bicep_provider.go in adjacent areas. This PR adds checkLocalAuthPolicy after checkRoleAssignmentPermissions, while #7174 modifies checkRoleAssignmentPermissions and adds resolveResourceTenantPrincipalId. The changes are semantically independent - the merge should be straightforward, but the person merging second needs to be aware.
| continue | ||
| } | ||
|
|
||
| if !azapi.ResourceHasLocalAuthDisabled(resource.Type, resource.Properties) { |
There was a problem hiding this comment.
[Low] Duplicate iteration over SnapshotResources
SnapshotResources is iterated twice - once for diagnostic logging (lines 2313-2318) and once for collecting affected types (lines 2322-2331). These could be merged into a single loop.

Overview
Adds a local preflight check that detects Azure Policy assignments which deny resources with local authentication enabled (
disableLocalAuth != true), preventing the crypticRequestDisallowedByPolicydeployment failure.Problem
When a subscription has an Azure Policy requiring
disableLocalAuth: true, deployments fail late with:Solution
A new preflight check that runs before deployment:
armpolicyAzure SDKpolicyRulefordisableLocalAuthfield conditions withdenyeffect[parameters('effect')]), policy sets (initiatives), nestedallOf/anyOfconditionsAlso handles
allowSharedKeyAccessfor storage accounts (inverted logic equivalent ofdisableLocalAuth).Files Changed
pkg/azapi/policy_service.goPolicyServicewith policy assignment listing, definition fetching, and rule parsingpkg/azapi/policy_service_test.gopkg/infra/provisioning/bicep/bicep_provider.gocheckLocalAuthPolicypreflight check functioncmd/container.goPolicyServicego.mod/go.sumarmpolicySDK dependency.vscode/cspell.yamlLimitations (acceptable for a warning-level check)
[if(...)]) indisableLocalAuthvalues can't be evaluated locallyFixes #7177