# 3.8.5

**Release Date:** August 28, 2026

---

## 🐛 Bug Fixes

### Terraform Modules

#### Avoid A Depends_on Cycle In Saif-Application-Permissions 🔗

**Module:** `saif-application-permissions`

**PR:** [#1080](https://github.com/saif-corp/forge/pull/1080)

Fixes a Terraform `Error: Cycle` on `terraform apply`:

```text
Error: Cycle:
module.saif-appservices (close)
module.saif-appservices.module.external_identity (close)
module.application_permissions.local.application_name (expand)
...
```

`saif-application-permissions` looked up its target Entra application by display name via a data source, so callers added a coarse module-level `depends_on = [module.saif-appservices]` to sequence the apply. That forces Terraform to fully settle **every** resource inside `saif-appservices` — including unrelated ones, like `external-identity`'s Okta Key Vault secret role assignments — before touching anything in `application_permissions`. When resources in both module trees are being replaced (destroy+create) in the same apply, Terraform cannot linearize the graph across that coarse boundary and reports a cycle.

The module now accepts optional `application` (`{ id, client_id }`) and `service_principal` (`{ object_id, client_id }`) identity objects so callers can pass the identity directly (e.g. `module.saif-appservices.application_id`), creating a precise attribute-level dependency instead of the module-level `depends_on`, and skipping the display-name/service-principal lookups when provided. Identity is modeled as a single nullable object per concept, rather than separate string and boolean inputs, so `count` can safely branch on the object being `null` even when its attribute values are unknown at plan time — an object literal with unknown attributes is still a known, non-null value, avoiding an "Invalid count argument" error. A `validation` block on each variable rejects an incomplete identity (e.g. `{ id = null, client_id = null }`) with an actionable error instead of letting a `null` reach the nested `permissions` submodule.

The fix also closes an ID-format bug caught during review: the AzureAD data source's `.id` attribute is the Graph resource path `/applications/{object_id}`, not the bare object GUID (`.object_id`). Mixing the two formats between the fallback (data source) and passthrough (`var.application.id`) code paths would have changed the `uuidv5` seeds used to derive existing app role and scope GUIDs on the next apply, forcing `DeleteThenCreate` of every app role/scope and breaking anything pinned to the old GUIDs (e.g. role assignments, pre-authorizations).

`src/templates/saif-feature-api`, the `aspire-config` and `aspire-blobstorage` Foundry examples, and the `forge-v2-to-v3` migration guide now pass the new `application`/`service_principal` objects from `module.saif-appservices` and drop the `depends_on = [module.saif-appservices]` block.

**Action required:** the `application-permissions` module version constraint must be `>= 3.8.5, < 4.0.0` to use the new `application`/`service_principal` inputs. Projects with an older 3.x version of the module already initialized should run `terraform init -upgrade` after updating the constraint.

---

## 🔄 Breaking Changes

None in this release ✅

---

## 📋 Additional Notes

- Total commits: 1
- Contributors: Brian Sheridan, Emmitt Johnson

---

### Support

- 📧 Teams Support Channel: [Support](https://teams.microsoft.com/l/channel/19%3Acb611810fb0b42b080cfff5590bdd51c%40thread.tacv2/Support?groupId=514d2dac-2d62-48ce-bf99-0fa0ce39469c&tenantId=a86cb8ed-369b-4df5-ace5-43811f6e08cf)

---
