> For the complete documentation index, see [llms.txt](https://docs.forestall.io/forestall/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/forestall/edges/azure/az_cap_duplicate_of.md).

# AZ\_CAP\_DUPLICATE\_OF

## Summary

|                               |                                                             |
| ----------------------------- | ----------------------------------------------------------- |
| **Forestall ACL Alias**       | AZ\_CAP\_DUPLICATE\_OF                                      |
| **Entra ID (Azure AD) Alias** | Conditional Access Policy - Exact Duplicate                 |
| **Affected Object Types**     | Conditional Access Policy <-> Conditional Access Policy     |
| **Edge Direction**            | Symmetric (one edge per unordered pair, queried undirected) |
| **Exploitation Certainty**    | Informational                                               |

## Description

`AZ_CAP_DUPLICATE_OF` connects two **Conditional Access Policies** (`AZConditionalAccessPolicy`) in the same tenant that are **exact duplicates** of each other: identical conditions, grant controls, and session controls after normalization. Policy name, state (enabled / disabled / report-only), and timestamps are deliberately ignored, so a report-only or disabled clone of an enforced policy still counts as a duplicate. That is the most common real-world case: a policy cloned for testing and never removed.

The edge is **symmetric**. One edge is written per unordered pair (the collector writes it from the ordinally smaller policy Guid to the larger) and it is meant to be queried undirected. Each policy in a duplicate group also carries `DuplicateCount`, `DuplicateGroupId` (the first eight characters of the group's canonical hash), and `DuplicateOf` (the peer Guids).

This edge is informational and hygiene-focused, not an attack primitive. Duplicate policies inflate the policy set, make Conditional Access harder to reason about, and cause drift when one copy is updated and the others are not, which can quietly weaken enforcement.

## Identification

### PowerShell (Microsoft Graph)

Duplicate detection compares the full condition and control set. A practical approximation is to hash the comparable fields and group by the hash:

```powershell
Connect-MgGraph -Scopes "Policy.Read.All"

Get-MgIdentityConditionalAccessPolicy -All | ForEach-Object {
    $key = ($_ | Select-Object -ExpandProperty Conditions | ConvertTo-Json -Depth 10) +
           ($_ | Select-Object -ExpandProperty GrantControls | ConvertTo-Json -Depth 10) +
           ($_ | Select-Object -ExpandProperty SessionControls | ConvertTo-Json -Depth 10)
    [PSCustomObject]@{ Name = $_.DisplayName; State = $_.State; Key = $key }
} | Group-Object Key | Where-Object Count -gt 1 |
  ForEach-Object { $_.Group | Select-Object Name, State }
```

### Azure Portal

1. Open **Microsoft Entra admin center** -> **Protection** -> **Conditional Access** -> **Policies**.
2. Compare policies with the same assignments, conditions, and controls; look for clones left in report-only or disabled state.

## Exploitation

There is no exploit for this edge; it flags redundant Conditional Access configuration. Its value is operational hygiene: duplicates should be consolidated so there is one authoritative policy per intent, reducing the chance that an update to one copy leaves the others enforcing stale rules.

## Mitigation

* Consolidate exact duplicates into a single authoritative policy and remove the clones.
* Remove test clones once testing is complete rather than leaving them disabled or in report-only.
* Adopt a naming and lifecycle convention for Conditional Access policies to prevent accidental duplication.

## Detection

Use the Microsoft Entra admin center:

1. Open **Microsoft Entra ID** -> **Audit logs** and filter for Conditional Access policy creation.
2. Review newly created policies against existing ones for identical conditions and controls.
3. Periodically review the full policy set for redundant entries, especially disabled or report-only clones.

## References

* <https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview>
* <https://learn.microsoft.com/en-us/graph/api/resources/conditionalaccesspolicy>
* <https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-report-only>
