# saif-resources / environment

Resolves the full platform context from foundation modules, Azure client configuration, networking constants, and naming conventions. No `tfe_outputs` remote-state reads remain (#845). All downstream `saif-resources` modules accept a `context` input produced by this module.

## Usage

```hcl
module "environment" {
  source  = "app.terraform.io/SAIFCorp/resources/saif//modules/environment"
  version = "~> 1.0.0"

  environment       = var.environment
  tenant            = var.tenant
  owner             = var.owner
  project_id        = var.project_id
  is_production     = var.is_production
  resource_location = var.resource_location
  tags              = var.tags
}
```

## Outputs

| Name      | Description                                             |
| --------- | ------------------------------------------------------- |
| `context` | Structured object with all resolved platform references |

### Context Object Shape

```
context.environment / context.environment_short_name / context.tenant
context.organization_name / context.is_production / context.resource_location
context.tenant_id / context.subscription_id / context.caller_object_id
context.tags (merged common + custom)
context.diagnostic_settings_config (eventhub_name / eventhub_authorization_rule_id / log_analytics_workspace_id; iac-azure-modules v5 diagnostic settings destinations)
context.naming.resource_group_name / context.naming.internal_fd_hostname
context.team_services.service_plan_id
context.team_services.function_app_service_plan_id (null until the team enables the opt-in -functions plan; gate on != null before use)
context.org_services.apim_v2_name / context.org_services.ai_hub_id / ...
context.shared_services.acr_id / context.shared_services.key_vault_id / ...
context.networking.api_app_subnet_id / context.networking.services_subnet_id / ...
context.names (full naming module for downstream namers)
```

## Required Providers

- `azurerm` (>= 4.0, < 5.0) — **plus a `shared-services` provider alias supplied by the caller** (see below)
- `azuread` (>= 3.0, < 4.0)
- `azapi` (>= 2.0, < 3.0) — required for the Front Door origin-group/custom-domain existence pre-checks (see below)

### `azurerm.shared-services` provider alias required

As of the #845 Track B decoupling, this module resolves platform coordinates via
the published `SAIFCorp/foundation/saif` module (v1.2.0) instead of `tfe_outputs`
remote-state reads or hand-rolled data sources: `//modules/environment` owns the
org-scoped naming-convention lookups (APIM, Front Door, AI Hub/Search, Service
Bus, org Log Analytics), `//modules/team` owns the team-scoped lookups (team
resource group, App Service Plan, and the opt-in `-functions` Elastic Premium
plan — surfaced on `context.team_services.function_app_service_plan_id` as
`null` when the team hasn't enabled it, so consumers must gate on `!= null`
before use), and
`//modules/shared` owns the shared-services singletons
(ACR, Key Vault, App Configuration, the OTEL exporter contract). The
shared-services reads run against the shared-services
subscription, so callers declare a second, aliased `azurerm` provider themselves and
pass it through — the same pattern `saif-api-service` has always used for its
identity modules:

```hcl
provider "azurerm" {
  alias           = "shared-services"
  subscription_id = "9a7b2f1c-ab7b-44b2-a254-817c6a75e958" # shared-services subscription
  features {}
}

provider "azapi" {}

module "environment" {
  source  = "app.terraform.io/SAIFCorp/resources/saif//modules/environment"
  version = "~> 0.0.0" # pin to the concrete release that ships the alias — not yet published

  providers = {
    azurerm.shared-services = azurerm.shared-services
    azapi                   = azapi
  }

  environment       = var.environment
  tenant            = var.tenant
  owner             = var.owner
  project_id        = var.project_id
  is_production     = var.is_production
  resource_location = var.resource_location
  tags              = var.tags
}
```

Terraform requires **every** instantiation of a module to satisfy its
`configuration_aliases` — not just the callers that consume `shared_services`
fields — so adopters add the
`providers = { azurerm.shared-services = azurerm.shared-services, azapi = azapi }`
map on their `module "environment"` call when they bump to the release that
ships the alias. `saif-api-service`, `saif-web-service`, and
`saif-event-subscriber-service` already declare an `azurerm.shared-services`
alias for their `identity`/`external-identity` module calls, so they reuse the
same root-level provider block; `saif-event-service` and `saif-staticsite-service`
add it fresh. All five already pass a `providers = { azurerm.shared-services =
azurerm.shared-services }` map to `module "environment"` ahead of the version
bump — see the `NOTE` above each of those module blocks. That map is a no-op
against the still-`~> 3.8.0` registry pin (which doesn't declare the alias
yet) and only exists so Forge CI's local-module swap, which resolves the
source to this in-repo module for validation, has the alias configured; the
map does **not** need — and must not add — entries for the default
(unaliased) `azurerm`/`azuread`/`azapi` providers, which continue to be
inherited implicitly. Concrete version pins are still bumped at release time,
independent of this pre-wired map.

This is **not a breaking change** for anything pinned to a published version:
the alias requirement ships in the next minor release, which existing `~>`
pins never resolve to. Existing workspaces keep planning against their current
minor untouched; a consumer opts in at release time by bumping its pin and adding
the two provider lines above at the same time. That bump also picks up the
`organization_services`/`team_services` swaps, which land in the same release.

### `tfe_outputs` elimination (#845)

As of this PR, this module has **no** `data "tfe_outputs"` reads.
`organization_services` and `team_services` were the last two: every field
that previously came from remote state now resolves via the
`SAIFCorp/foundation/saif` module, a native `azurerm_cdn_frontdoor_*` data
source (origin IDs are constructed as `${origin_group_id}/origins/${name}`,
since AzureRM exposes no origin data source), or — for the one field with no
ARM-visible equivalent, `ai_search_docs_index_name` — a hardcoded literal
matching both the producer's own hardcoded value and this repo's existing
consumer default (see `outputs.tf`). `custom_subdomains` moved out of
`team_services` entirely into a native per-slug lookup in the
`custom-subdomain` module itself. `shared_services` was already fully native
before this PR. See `saif-resources/modules/external-identity/README.md` for
the equivalent note on `okta_app`.

<!-- BEGIN_TF_DOCS -->
## Providers

| Name | Version |
|------|---------|
| <a name="provider_azapi"></a> [azapi](#provider\_azapi) | >= 2.0, < 3.0 |
| <a name="provider_azuread"></a> [azuread](#provider\_azuread) | >= 3.0, < 4.0 |
| <a name="provider_azurerm"></a> [azurerm](#provider\_azurerm) | >= 4.0, < 5.0 |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_environment"></a> [environment](#input\_environment) | The environment name (e.g. Test, QA, UAT, Prod) | `string` | n/a | yes |
| <a name="input_environment_short_name"></a> [environment\_short\_name](#input\_environment\_short\_name) | Optional override for the environment short name. When set, takes precedence over the value derived from the naming module. | `string` | `null` | no |
| <a name="input_is_production"></a> [is\_production](#input\_is\_production) | Whether this is a production environment | `bool` | `false` | no |
| <a name="input_owner"></a> [owner](#input\_owner) | The team that owns the resources | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | The project identifier used for resource naming | `string` | n/a | yes |
| <a name="input_resource_location"></a> [resource\_location](#input\_resource\_location) | The Azure region for resource deployment | `string` | `"westus2"` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Tags to apply to all resources (merged with common tags) | `map(string)` | `{}` | no |
| <a name="input_tenant"></a> [tenant](#input\_tenant) | Deprecated — the Azure infra tenant is now fixed to Corporate (local.azure\_tenant) for resource naming and workspace lookups. Accepted for backward compatibility with existing callers only; the value is ignored. | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_context"></a> [context](#output\_context) | Resolved platform context — environment, networking, naming, TFC outputs, and Azure client config |

## Resources


- data source.azapi_resource_list.frontdoor_custom_domains (/terraform-docs/modules/environment/main.tf#126)
- data source.azapi_resource_list.frontdoor_origin_groups (/terraform-docs/modules/environment/main.tf#120)
- data source.azuread_client_config.current (/terraform-docs/modules/environment/main.tf#159)
- data source.azuread_service_principal.apim_to_webapp (/terraform-docs/modules/environment/main.tf#172)
- data source.azuread_service_principal.azuredevops (/terraform-docs/modules/environment/main.tf#178)
- data source.azurerm_cdn_frontdoor_custom_domain.external_app (/terraform-docs/modules/environment/main.tf#148)
- data source.azurerm_cdn_frontdoor_custom_domain.internal_app (/terraform-docs/modules/environment/main.tf#140)
- data source.azurerm_cdn_frontdoor_origin_group.external_api_v2 (/terraform-docs/modules/environment/main.tf#132)
- data source.azurerm_client_config.current (/terraform-docs/modules/environment/main.tf#158)    
<!-- END_TF_DOCS -->

---

[View source on GitHub](https://github.com/saif-corp/forge/blob/main/src/terraform/saif-resources/modules/environment/README.md)
