# Governance Constraints - pci-dss-gw

> This document captures the governance constraints and Azure Policy requirements that must be addressed in the Bicep implementation for the PCI-DSS Level 1 payment gateway.

- Skill: `tools-only/governance-constraints-pci-dss-gw` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/governance-constraints-pci-dss-gw`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/governance-constraints-pci-dss-gw/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-29
- Page: https://skillmd.com/skills/tools-only/governance-constraints-pci-dss-gw

---

# Governance Constraints - pci-dss-gw

> Generated by bicep-plan agent | 2026-02-11 | Full REST API discovery

> [!NOTE]
> 📚 See [SKILL.md](../../.github/skills/azure-artifacts/SKILL.md) for visual standards.

This document captures the governance constraints and Azure Policy requirements
that must be addressed in the Bicep implementation for the PCI-DSS Level 1 payment gateway.


## Discovery Source

> [!IMPORTANT]
> Governance constraints discovered via **REST API** including management group-inherited policies.
> Previous `az policy assignment list` discovery missed 16 of 21 policies.

| Query              | Result                       | Timestamp            |
| ------------------ | ---------------------------- | -------------------- |
| REST API Total     | **21 assignments**           | 2026-02-11T12:00:00Z |
| Subscription-scope | 5 direct assignments         | 2026-02-11T12:00:00Z |
| RG-scoped          | 7 resource group assignments | 2026-02-11T12:00:00Z |
| MG-inherited       | 9 inherited policies         | 2026-02-11T12:00:00Z |
| Deny-effect        | 3 blockers found             | 2026-02-11T12:00:00Z |
| Tag Policies       | 9 tags required (Deny)       | 2026-02-11T12:00:00Z |

**Discovery Method**: REST API
(`GET /subscriptions/{id}/providers/Microsoft.Authorization/policyAssignments?api-version=2022-06-01`)

**Subscription**: noalz (`00858ffc-dded-4f0f-8bbf-e17fff0d47d9`)

**Tenant**: `2d04cb4c-999b-4e60-a3a7-e8993edc768b`

**Scope**: All effective (subscription + resource group + management group inherited)

> [!CAUTION]
> Previous discovery using `az policy assignment list` only returned 5 subscription-scoped policies.
> REST API revealed 16 additional policies including **critical Deny blockers**.

---


## Azure Policy Compliance

| Category | Constraint | Source | Implementation |
| --- | --- | --- | --- |
| **Tags (Deny)** | 9 lowercase tags required on RGs | JV-Enforce RG Tags v3 | All Bicep RG deployments include all 9 |
| **Tags (Modify)** | 9 tags auto-inherited to children | JV-Inherit Tags | No Bicep action — policy handles it |
| **VM SKUs (Deny)** | H, M, N series blocked | MCAPSGov Deny | Using D-series — compliant |
| **AKS Pools (Deny)** | Max 10 agent pool profiles | MCAPSGov Deny | Using 2 pools — compliant |
| **Classic (Deny)** | All Classic resource types blocked | MCAPSGov Deny + Block ARM | Using ARM — compliant |
| **SQL Auth (Deny)** | Azure SQL requires AAD-only | MCAPSGov Deny | Using PostgreSQL — N/A |
| **HSM Purge (Deny)** | Purge protection required | MCAPSGov Deny | Enable purge protection in Bicep |
| **Compliance (Audit)** | PCI DSS v4 (269 controls) | Subscription policy | Architecture designed for PCI-DSS L1 |
| **Compliance (Audit)** | GDPR (285 controls) | Subscription policy | EU regions: swedencentral/germanywestcentral |
| **Security (Audit)** | Azure Security Baseline (224) | MG policy | Cloud security benchmark posture |
| **Security (Audit)** | MCAPSGov Audit (44) | MG policy | Tenant baseline compliance |
| **MFA (Audit)** | MFA for write/delete operations | MG policy (×2) | Deploying user must have MFA enabled |
| **Naming** | No naming policy discovered | — | Follow CAF conventions from azure-defaults |
| **Location** | No location restriction found | — | EU regions: swedencentral/germanywestcentral |

---


### Complete Policy Inventory


### Management Group-Inherited Policies (9)

| # | Display Name | Type | Effect | Sub-Policies | Impact |
| - | --- | --- | --- | --- | --- |
| 1 | **JV-Enforce Resource Group Tags v3** | Policy | **Deny** | 1 | 🚫 BLOCKER — 9 lowercase tags required |
| 2 | **MCAPSGov Deny Policies** | Initiative | **Deny** | 11 | ⚠️ VM SKU + AKS pool + HSM constraints |
| 3 | **Block Azure RM Resource Creation** | Policy | **Deny** | 1 | ✅ Classic resources only — no impact |
| 4 | MCAPSGov Deploy and Modify Policies | Initiative | DeployIfNotExists/Modify | 27 | Auto-deploys security agents |
| 5 | MCAPSGov Audit Policies | Initiative | Audit | 44 | Compliance reporting only |
| 6 | Azure Security Baseline | Initiative | Audit | 224 | Microsoft cloud security benchmark |
| 7 | JV - Inherit Multiple Tags from Resource Group | Policy | Modify | 1 | Auto-inherits 9 tags from RG to children |
| 8 | MFA Enforcement for Resource Write Actions | Policy | Audit | 1 | MFA required for write operations |
| 9 | MFA Enforcement for Resource Delete Actions | Policy | Audit | 1 | MFA required for delete operations |

### Subscription-Scoped Policies (5)

| # | Display Name | Type | Effect | Impact |
| - | --- | --- | --- | --- |
| 10 | PCI DSS v4 | Initiative (269 controls) | Audit | Architecture must align with PCI DSS |
| 11 | EU GDPR 2016/679 | Initiative (285 controls) | Audit | EU data residency validation |
| 12 | ASC DataProtection | Initiative | DeployIfNotExists | Auto-deploys data protection monitoring |
| 13 | ASC OpenSourceRelationalDatabasesProtection | Initiative | DeployIfNotExists | Auto-deploys Defender for PostgreSQL |
| 14 | Defender for SQL Servers on Machines | Initiative | DeployIfNotExists | SQL Defender (not applicable — PaaS only) |

### Resource Group-Scoped Policies (7)

All scoped to `rg-arcbox-swc01` — a different resource group. **No impact on this project.**

| # | Display Name | Scope |
| - | --- | --- |
| 15 | (ArcBox) Enable SSH Posture Control audit | rg-arcbox-swc01 |
| 16 | (ArcBox) Enable Azure Update Manager for Arc-enabled Windows machines | rg-arcbox-swc01 |
| 17 | (ArcBox) Tag resources (unnamed) | rg-arcbox-swc01 |
| 18 | (ArcBox) Azure Monitor (unnamed) | rg-arcbox-swc01 |
| 19 | (ArcBox) Enable Azure Update Manager for Arc-enabled Linux machines | rg-arcbox-swc01 |
| 20 | (ArcBox) Enable Azure Update Manager for Azure Windows machines | rg-arcbox-swc01 |
| 21 | (ArcBox) Enable Azure Update Manager for Azure Linux machines | rg-arcbox-swc01 |

---


## Plan Adaptations Based on Policies

### Tag Schema (Updated — CRITICAL)

```bicep
// Resource Group tags — ALL 9 REQUIRED by JV-Enforce Resource Group Tags v3 (Deny)
// Case-sensitive: ALL lowercase
@description('Deployment environment')
@allowed(['dev', 'staging', 'prod'])
param environment string

@description('Team or individual owner')
param owner string

@description('Cost center code')
param costCenter string

@description('Technical contact email')
param technicalContact string

var requiredRgTags = {
  environment: environment
  owner: owner
  costcenter: costCenter
  application: 'pci-dss-gw'
  workload: 'payment-gateway'
  sla: '99.99'
  'backup-policy': 'daily'
  'maint-window': 'Sun:02:00-06:00'
  'technical-contact': technicalContact
}

// Additional project-standard tags (not policy-enforced but recommended)
var allTags = union(requiredRgTags, {
  ManagedBy: 'Bicep'
  Project: 'pci-dss-gw'
})
```

> [!IMPORTANT]
> Resource groups require ALL 9 tags from `JV-Enforce Resource Group Tags v3`.
> Child resources auto-inherit these 9 tags via `JV - Inherit Multiple Tags from Resource Group`
> (Modify effect). Additional tags (ManagedBy, Project) can be appended but are NOT required.

### Architectural Constraints

| Original Design | Blocking Policy | Effect | Required Adaptation |
| --- | --- | --- | --- |
| 4 PascalCase tags | JV-Enforce RG Tags v3 | Deny | **9 lowercase tags** on all resource groups |
| Key Vault Premium HSM | MCAPSGov Deny (HSM purge) | Deny | `enablePurgeProtection: true` required |
| AKS 2 node pools | MCAPSGov Deny (AKS pool limit) | Deny | ✅ Compliant (2 < 10 limit) — document constraint |
| D8s_v5 / D4s_v5 VMs | MCAPSGov Deny (VM SKU) | Deny | ✅ Compliant — D-series not blocked |

### Auto-Applied Configurations

| Policy | Effect | What Gets Auto-Applied |
| --- | --- | --- |
| JV - Inherit Multiple Tags from Resource Group | Modify | 9 RG tags auto-copied to all child resources |
| MCAPSGov Deploy and Modify Policies (27 sub-policies) | DeployIfNotExists/Modify | Security agents, diagnostic settings |
| ASC DataProtection | DeployIfNotExists | Data protection monitoring agents |
| ASC OpenSourceRelationalDatabasesProtection | DeployIfNotExists | Defender for PostgreSQL monitoring |
| Defender for SQL on Machines | DeployIfNotExists | SQL Defender agents (N/A — PaaS) |

---


## Deployment Blockers

> [!CAUTION]
> **CRITICAL**: Policies that BLOCK deployment. Resolution is REQUIRED before proceeding.

### Blocker 1: JV-Enforce Resource Group Tags v3 (DENY)

**Assignment**: `b1ad1a690a5148ec8707ff17`

**Scope**: Management Group (Tenant Root `2d04cb4c-999b-4e60-a3a7-e8993edc768b`)

**Enforcement Mode**: Default (enabled)

**Effect**: Deny

**Policy Definition**: `27833bcf-5909-4a37-891c-16a3cb06856d`

**Policy Rule**:

- Applies to: `Microsoft.Resources/subscriptions/resourceGroups`
- Excludes RG names matching: `AzureBackupRG*`, `ResourceMover*`, `databricks-rg*`,
  `NetworkWatcherRG`, `microsoft-network`, `LogAnalyticsDefaultResources`, `rg-amba-*`,
  `DynamicsDeployments*`, `MC_myResourceGroup*`
- **Denies creation if ANY of 9 tags are missing** (case-sensitive, all lowercase)

| # | Tag Name             | Required | Case      |
| - | -------------------- | -------- | --------- |
| 1 | `environment`        | Yes      | lowercase |
| 2 | `owner`              | Yes      | lowercase |
| 3 | `costcenter`         | Yes      | lowercase |
| 4 | `application`        | Yes      | lowercase |
| 5 | `workload`           | Yes      | lowercase |
| 6 | `sla`                | Yes      | lowercase |
| 7 | `backup-policy`      | Yes      | lowercase |
| 8 | `maint-window`       | Yes      | lowercase |
| 9 | `technical-contact`  | Yes      | lowercase |

> [!WARNING]
> **Our original plan only had 4 tags (Environment, ManagedBy, Project, Owner) with PascalCase.**
> This policy requires **9 tags** with **lowercase** names. Resource group creation will be
> **DENIED** without all 9 tags present.

**Resolution**: Update all Bicep resource group deployments to include all 9 required tags
with correct lowercase casing. The `ManagedBy` and `Project` tags from our defaults are
supplementary but NOT required by policy.

### Blocker 2: MCAPSGov Deny Policies (11 sub-policies)

**Assignment**: `MCAPSGovDenyPolicies`

**Scope**: Management Group (Tenant Root `2d04cb4c-999b-4e60-a3a7-e8993edc768b`)

**Enforcement Mode**: Default (enabled)

**Effect**: Deny (initiative with 11 policies)

| # | Reference ID | Policy Definition | Effect | Impact on This Project |
| - | --- | --- | --- | --- |
| 1 | BlockVMSKUs_H | VirtualMachine_SKU_Deny | Deny | ✅ Safe — we use D-series, not H-series (17 blocked SKUs) |
| 2 | BlockVMSKUs_M | VirtualMachine_SKU_Deny | Deny | ✅ Safe — we use D-series, not M-series (44 blocked SKUs) |
| 3 | BlockVMSKUs_N | VirtualMachine_SKU_Deny | Deny | ✅ Safe — we use D-series, not N-series (64 blocked SKUs) |
| 4 | AKS_LimitNodeCount | AKS_LimitNodeCount_Deny | Deny | ✅ Safe — our plan uses 2 pools (system + CDE), limit is 10 |
| 5 | VMSS_LimitNodesCount | VMSS_LimitNodesCount_Deny | Deny | ⚠️ Verify — AKS uses VMSS internally, check node count limit |
| 6 | OpenAI_BlockProvisionedCapacity | AzureOpenAI_ProvisionedCapacity_Deny | Deny | ✅ N/A — not in our architecture |
| 7 | Sentinel_Commitment_Deny | Sentinel_Commitment_Deny | Deny | ✅ N/A — not in our architecture |
| 8 | AzureSQL_WithoutAzureADOnlyAuthentication_Deny | AzureSQL_WithoutAzureADOnlyAuthentication_Deny | Deny | ✅ N/A — we use PostgreSQL, not Azure SQL |
| 9 | AzureSQLMI_WithoutAzureADOnlyAuthentication_Deny | AzureSQLMI_WithoutAzureADOnlyAuthentication_Deny | Deny | ✅ N/A — not in our architecture |
| 10 | NotAllowedResourceTypes | 6c112d4e-5bc7-47ae-a041-ea2d9dccd749 | Deny | ✅ Safe — blocks 57 Classic (ASM) resource types only |
| 11 | KeyVaultManagedHSM_PurgeProtectionEnabled | KeyVaultManagedHSM_PurgeProtectionEnabled_Deny | Deny | ⚠️ Ensure — Key Vault HSM must have purge protection enabled |

**Resolutions**:

1. **AKS agent pool limit**: Architecture uses 2 pools — compliant. Document constraint.
2. **VMSS node count**: Verify the exact limit parameter and ensure AKS autoscaler max
   does not exceed it.
3. **Key Vault HSM purge protection**: Set `enablePurgeProtection: true` on Key Vault.
4. **VM SKUs**: Only D-series VMs used — compliant. Document blocked families.

### Non-Blocker: Block Azure RM Resource Creation (DENY)

**Scope**: Management Group (Tenant Root)

**Assessment**: This policy ONLY blocks Classic (ASM) resource types:
`Microsoft.ClassicCompute/*`, `Microsoft.ClassicStorage/*`, `Microsoft.ClassicNetwork/*`.

**Impact**: None — our Bicep templates use ARM resources exclusively.

---


## Required Tags

### Resource Group Tags (MANDATORY — Deny enforced)

```bicep
// These 9 tags are REQUIRED by JV-Enforce Resource Group Tags v3 (Deny)
// ALL lowercase, case-sensitive — deployment will FAIL without them
var requiredRgTags = {
  environment: environment          // 'dev' | 'staging' | 'prod'
  owner: owner                      // Team or individual
  costcenter: costCenter            // Cost center code
  application: 'pci-dss-gw'          // Application name
  workload: 'payment-gateway'      // Workload type
  sla: '99.99'                     // SLA target
  'backup-policy': 'daily'         // Backup policy
  'maint-window': 'Sun:02:00-06:00' // Maintenance window
  'technical-contact': techContact  // Technical contact email
}
```

### Resource Tags (Project standard + auto-inherited)

```bicep
// Child resources auto-inherit 9 RG tags via JV-Inherit policy (Modify)
// These additional tags are project convention, not policy-enforced
var additionalTags = {
  ManagedBy: 'Bicep'
  Project: 'pci-dss-gw'
}

var allTags = union(requiredRgTags, additionalTags)
```

---


## Security Policies

| Area | Requirement | Source |
| --- | --- | --- |
| HTTPS Only | TLS 1.2+ on all services | PCI Req 4 (proactive) |
| Public Access | Disabled via private endpoints | PCI Req 1 (proactive) |
| Managed Identity | All service-to-service auth | PCI Req 8 (proactive) |
| Key Vault HSM | Purge protection required | MCAPSGov Deny + PCI Req 3 |
| AAD-Only Auth | Required for all databases | MCAPSGov Deny (SQL) + PCI Req 8 |
| Network Segmentation | Hub-spoke with Firewall IDPS | PCI Req 1 (proactive) |
| Container Security | Defender auto-enabled | MCAPSGov Deploy (auto) |
| Logging | 1-year retention, tamper-proof | PCI Req 10 (proactive) |
| Defender for PostgreSQL | Auto-enabled | ASC policy (auto) |
| Defender for Cloud | Enhanced posture | PCI Req 11 (proactive) |
| MFA Enforcement | Write/delete operations | MG policy (Audit) |
| Security Baseline | 224 controls | MG policy (Audit) |

---


## Cost Policies

| Constraint | Source | Impact |
| --- | --- | --- |
| H/M/N VM SKUs blocked | MCAPSGov Deny (3 policies) | Prevents expensive HPC/GPU VMs |
| OpenAI provisioned capacity blocked | MCAPSGov Deny | Prevents AI spend |
| Sentinel commitment tier blocked | MCAPSGov Deny | Prevents commitment over-provisioning |
| No budget policy found | — | Recommend Azure Cost Management alert at $20,000/mo |
| No reservations policy | — | Recommend 1-year RI for AKS + PostgreSQL (~35% savings) |

---


## Network Policies

| Policy | Constraint | Impact |
| --- | --- | --- |
| No location restriction | No Deny policy on allowed locations | Using EU regions: swedencentral/germanywestcentral |
| No naming policy | No Deny policy on naming conventions | Following CAF conventions from azure-defaults skill |
| NSG flow logs | MCAPSGov Deploy auto-enables | Auto-applied via DeployIfNotExists |

> [!NOTE]
> No network-specific Deny policies found. Architecture follows PCI-DSS network
> segmentation requirements proactively (hub-spoke, private endpoints, Azure Firewall IDPS).


## References

| Topic | Link |
| --- | --- |
| Azure Policy effects | [Policy effects](https://learn.microsoft.com/azure/governance/policy/concepts/effects) |
| Tag enforcement patterns | [Tag policies](https://learn.microsoft.com/azure/azure-resource-manager/management/tag-policies) |
| PCI-DSS on Azure | [PCI compliance](https://learn.microsoft.com/azure/compliance/offerings/offering-pci-dss) |
| Azure Policy exemptions | [Exemption structure](https://learn.microsoft.com/azure/governance/policy/concepts/exemption-structure) |
| Tag inheritance | [Inherit a tag from RG](https://learn.microsoft.com/azure/governance/policy/samples/built-in-policies#tags) |
| MCAPSGov policies | Tenant Root management group (internal) |
| REST API discovery | [Policy assignments API](https://learn.microsoft.com/rest/api/policy/policy-assignments/list) |

---

*Governance constraints discovered via REST API on subscription `noalz` (`00858ffc-dded-4f0f-8bbf-e17fff0d47d9`).*
*All 21 assignments verified including 9 management group-inherited policies.*
*See [governance-discovery.instructions.md](../../.github/instructions/governance-discovery.instructions.md) for discovery methodology.*

