# saif-resources / api

Creates the standard Forge API compute stack: Linux web app, APIM registration (backend + API + policies), and Front Door route. APIM policies are pluggable — pass API-level XML and per-operation policy maps.

## Usage

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

  context  = module.environment.context
  identity = module.identity.identity

  resource_group_name     = module.resource_group.resource_group_name
  resource_group_location = var.resource_location

  api_name      = var.project_id
  api_type      = "Experience"
  open_api_spec = file("${path.module}/openapi/openapi.yaml")

  auth_config = {
    audience          = module.identity.identity.app_client_id
    tenant_id         = module.environment.context.tenant_id
    default_auth_type = "entra_user"
  }

  # Per-operation overrides (optional)
  operation_auth_policies = {
    postActivity = { auth_type = "bot_framework" }
    mcpRequest   = { streaming = true }
  }

  # Merge app settings from all resource modules
  app_settings = merge(
    module.identity.app_settings,
    module.cosmosdb.app_settings,
    { /* caller-specific settings */ }
  )
}
```

## Outputs

| Name                       | Description                          |
| -------------------------- | ------------------------------------ |
| `web_app_name`             | Web app name                         |
| `web_app_id`               | Web app resource ID                  |
| `web_app_default_hostname` | Web app default hostname             |
| `api_path`                 | APIM API path                        |
| `api_name`                 | APIM API name                        |
| `backend_name`             | APIM backend name                    |
| `bot_endpoint`             | Bot Framework messaging endpoint URL |
| `base_url`                 | Base URL via Front Door + APIM       |

<!-- BEGIN_TF_DOCS -->
## Providers

| Name | Version |
|------|---------|
| <a name="provider_azurerm"></a> [azurerm](#provider\_azurerm) | >= 4.0, < 5.0 |
| <a name="provider_azurerm.shared-services"></a> [azurerm.shared-services](#provider\_azurerm.shared-services) | >= 4.0, < 5.0 |
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.5.1, < 4.0.0 |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_api_name"></a> [api\_name](#input\_api\_name) | Application name for APIM naming (e.g. project\_id) | `string` | n/a | yes |
| <a name="input_api_protocol"></a> [api\_protocol](#input\_api\_protocol) | The API protocol type. Currently only 'rest' is supported (imports OpenAPI spec into APIM). | `string` | `"rest"` | no |
| <a name="input_api_type"></a> [api\_type](#input\_api\_type) | The API type for APIM naming: Experience, Process, or System | `string` | `"Experience"` | no |
| <a name="input_auth_config"></a> [auth\_config](#input\_auth\_config) | Auth configuration for governed APIM policies. Required when operation\_auth\_policies is non-empty.<br/>audience:              The application (client) ID for JWT validation.<br/>tenant\_id:             The Entra ID tenant ID for issuer/openid-config URLs.<br/>default\_auth\_type:     Default auth type for operations. "bot\_framework", "entra\_user" for simple<br/>                       per-operation policies; "standard" for dual Okta/Entra routing;<br/>                       "subscription\_key" for APIM subscription key auth.<br/>corp\_auth\_server\_url:  (standard) Entra corporate auth server URL for JWT validation.<br/>external\_auth:         (standard) External (Okta) auth config — server URL and audience.<br/>web\_app\_client\_id:     (standard/subscription\_key) App registration client ID for managed identity<br/>                       auth to the backend web app (api://{project\_id}-{environment}). | <pre>object({<br/>    audience          = string<br/>    tenant_id         = string<br/>    default_auth_type = optional(string)<br/>    # Standard auth extensions<br/>    corp_auth_server_url = optional(string)<br/>    external_auth = optional(object({<br/>      auth_server_url = string<br/>      audience        = string<br/>    }))<br/>    web_app_client_id = optional(string)<br/>  })</pre> | `null` | no |
| <a name="input_backends"></a> [backends](#input\_backends) | Named APIM backends to register. Key is the logical name used in policy references.<br/>url:         Backend URL (e.g. https://myapp.azurewebsites.net)<br/>resource\_id: ARM resource ID for Private Link validation. Null for external URLs.<br/>name:        APIM backend name override. Defaults to api\_name if not provided. | <pre>map(object({<br/>    url         = string<br/>    resource_id = optional(string)<br/>    name        = optional(string)<br/>  }))</pre> | n/a | yes |
| <a name="input_context"></a> [context](#input\_context) | Platform context from the environment module | `any` | n/a | yes |
| <a name="input_cookie_secret_reader_ids"></a> [cookie\_secret\_reader\_ids](#input\_cookie\_secret\_reader\_ids) | Map of principal IDs to grant Key Vault Secrets User role on the cookie secret. Keyed by a stable label (e.g. "webapp"). Use a static key to avoid for\_each errors when the principal ID is computed. | `map(string)` | `{}` | no |
| <a name="input_enable_auth_endpoints"></a> [enable\_auth\_endpoints](#input\_enable\_auth\_endpoints) | Whether to create /auth/* operations (login, me, callback, signedout, logout) for Experience APIs that use browser-based authentication. | `bool` | `false` | no |
| <a name="input_enable_cookie_secret"></a> [enable\_cookie\_secret](#input\_enable\_cookie\_secret) | Whether to create a Key Vault secret for cookie encryption and grant RBAC access to UAMI and APIM. Requires azurerm.shared-services provider. | `bool` | `false` | no |
| <a name="input_enable_frontdoor_route"></a> [enable\_frontdoor\_route](#input\_enable\_frontdoor\_route) | Whether to create a Front Door route | `bool` | `true` | no |
| <a name="input_enable_mocking_backend"></a> [enable\_mocking\_backend](#input\_enable\_mocking\_backend) | Whether to create a mocking backend from the OpenAPI spec x-mocking server for x-mocking header support. | `bool` | `false` | no |
| <a name="input_enable_service_discovery"></a> [enable\_service\_discovery](#input\_enable\_service\_discovery) | Whether to register service discovery entries in App Configuration. Requires azurerm.shared-services provider. | `bool` | `false` | no |
| <a name="input_filevine_config"></a> [filevine\_config](#input\_filevine\_config) | Filevine Identity Server configuration for webhook JWT validation.<br/>Required when auth\_config.default\_auth\_type = "filevine".<br/>openid\_config\_url: OIDC discovery endpoint (default: Filevine Identity Server).<br/>audience:          Expected JWT audience claim.<br/>issuer:            Expected JWT issuer claim.<br/>scope:             Required JWT scope claim. | <pre>object({<br/>    openid_config_url = optional(string, "https://identity.filevine.com/.well-known/openid-configuration")<br/>    audience          = optional(string, "filevine-v2-webhooks")<br/>    issuer            = optional(string, "https://identity.filevine.com")<br/>    scope             = optional(string, "filevine-v2-webhooks-access")<br/>  })</pre> | `null` | no |
| <a name="input_is_external"></a> [is\_external](#input\_is\_external) | Whether this is an external-facing API (uses external Front Door endpoint). | `bool` | `false` | no |
| <a name="input_open_api_spec"></a> [open\_api\_spec](#input\_open\_api\_spec) | OpenAPI spec content to import into APIM. Required when api\_protocol is 'rest'; must be non-empty. | `string` | `""` | no |
| <a name="input_operation_auth_policies"></a> [operation\_auth\_policies](#input\_operation\_auth\_policies) | Per-operation APIM auth policy overrides. Map of operation\_id to auth config.<br/>Only applies to simple auth modes (bot\_framework/entra\_user): all discovered operations<br/>get auth\_config.default\_auth\_type; use this to override specific operations to a different<br/>simple auth type. Standard/subscription\_key/filevine per-operation policies are<br/>auto-generated from OpenAPI spec scopes/roles/mocking and do not use this variable.<br/>auth\_type: "bot\_framework" or "entra\_user".<br/>          Optional — defaults to auth\_config.default\_auth\_type when omitted.<br/>streaming: true for SSE/streaming endpoints (disables response buffering, 120s timeout). | <pre>map(object({<br/>    auth_type = optional(string)<br/>    streaming = optional(bool, false)<br/>  }))</pre> | `{}` | no |
| <a name="input_service_discovery_config"></a> [service\_discovery\_config](#input\_service\_discovery\_config) | Service discovery configuration for App Configuration registration.<br/>Required when enable\_service\_discovery = true.<br/>external\_auth\_server\_url:      Okta auth server URL (for external tenant callers).<br/>external\_auth\_server\_audience: Okta auth server audience.<br/>corp\_auth\_server\_url:          Entra corp auth server URL.<br/>corp\_auth\_server\_audience:     Entra corp auth server audience (typically the app client ID).<br/><br/>enable\_external\_auth\_server:        Whether to register the external auth server URL key.<br/>enable\_corp\_auth\_server:            Whether to register the corp auth server URL key.<br/>enable\_external\_authserver\_audience: Whether to register the external auth server audience key.<br/>enable\_corp\_authserver\_audience:    Whether to register the corp auth server audience key.<br/><br/>These enable flags must be set to plan-time-known values (e.g. variables or literals).<br/>Do not derive them from resource outputs — Terraform cannot use unknown values in count. | <pre>object({<br/>    external_auth_server_url            = optional(string)<br/>    external_auth_server_audience       = optional(string)<br/>    corp_auth_server_url                = optional(string)<br/>    corp_auth_server_audience           = optional(string)<br/>    enable_external_auth_server         = optional(bool, false)<br/>    enable_corp_auth_server             = optional(bool, false)<br/>    enable_external_authserver_audience = optional(bool, false)<br/>    enable_corp_authserver_audience     = optional(bool, false)<br/>  })</pre> | `null` | no |
| <a name="input_subscription_primary_key"></a> [subscription\_primary\_key](#input\_subscription\_primary\_key) | User-provided primary subscription key. When null, APIM auto-generates the key. | `string` | `null` | no |
| <a name="input_subscription_required"></a> [subscription\_required](#input\_subscription\_required) | Whether APIM subscription key is required | `bool` | `false` | no |
| <a name="input_subscription_secondary_key"></a> [subscription\_secondary\_key](#input\_subscription\_secondary\_key) | User-provided secondary subscription key. When null, APIM auto-generates the key. | `string` | `null` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_api_name"></a> [api\_name](#output\_api\_name) | The APIM API name |
| <a name="output_api_path"></a> [api\_path](#output\_api\_path) | The APIM API path |
| <a name="output_backend_name"></a> [backend\_name](#output\_backend\_name) | The default APIM backend name |
| <a name="output_base_url"></a> [base\_url](#output\_base\_url) | Base URL via shared internal Front Door + APIM path |
| <a name="output_bot_endpoint"></a> [bot\_endpoint](#output\_bot\_endpoint) | Bot Framework messaging endpoint URL (base\_url + /api/messages) |
| <a name="output_cookie_secret_name"></a> [cookie\_secret\_name](#output\_cookie\_secret\_name) | The Key Vault secret name for the cookie encryption key. Null when cookie secret is not enabled. |
| <a name="output_cookie_secret_versionless_id"></a> [cookie\_secret\_versionless\_id](#output\_cookie\_secret\_versionless\_id) | The versionless ID of the cookie secret in Key Vault. Null when cookie secret is not enabled. |
| <a name="output_subscription_primary_key"></a> [subscription\_primary\_key](#output\_subscription\_primary\_key) | Primary key of the APIM subscription. Null when subscription is not enabled. |
| <a name="output_subscription_secondary_key"></a> [subscription\_secondary\_key](#output\_subscription\_secondary\_key) | Secondary key of the APIM subscription. Null when subscription is not enabled. |

## Resources


- resource.azurerm_api_management_api.main (/terraform-docs/modules/api/apimanagement.tf#76)
- resource.azurerm_api_management_api_operation.auth (/terraform-docs/modules/api/auth_endpoints.tf#45)
- resource.azurerm_api_management_api_operation_policy.auth (/terraform-docs/modules/api/auth_endpoints.tf#64)
- resource.azurerm_api_management_api_operation_policy.main (/terraform-docs/modules/api/apimanagement.tf#106)
- resource.azurerm_api_management_api_policy.main (/terraform-docs/modules/api/apimanagement.tf#97)
- resource.azurerm_api_management_backend.main (/terraform-docs/modules/api/apimanagement.tf#23)
- resource.azurerm_api_management_backend.mocking (/terraform-docs/modules/api/apimanagement.tf#50)
- resource.azurerm_api_management_named_value.backend (/terraform-docs/modules/api/apimanagement.tf#38)
- resource.azurerm_api_management_named_value.cookie_secret (/terraform-docs/modules/api/apimanagement.tf#122)
- resource.azurerm_api_management_named_value.mocking (/terraform-docs/modules/api/apimanagement.tf#64)
- resource.azurerm_api_management_subscription.main (/terraform-docs/modules/api/apimanagement.tf#142)
- resource.azurerm_app_configuration_key.service_corp_authserver (/terraform-docs/modules/api/service_discovery.tf#52)
- resource.azurerm_app_configuration_key.service_corp_authserveraudience (/terraform-docs/modules/api/service_discovery.tf#80)
- resource.azurerm_app_configuration_key.service_ext_authserver (/terraform-docs/modules/api/service_discovery.tf#38)
- resource.azurerm_app_configuration_key.service_ext_authserveraudience (/terraform-docs/modules/api/service_discovery.tf#66)
- resource.azurerm_app_configuration_key.service_path (/terraform-docs/modules/api/service_discovery.tf#24)
- resource.azurerm_app_configuration_key.service_url (/terraform-docs/modules/api/service_discovery.tf#10)
- resource.azurerm_cdn_frontdoor_custom_domain_association.main (/terraform-docs/modules/api/frontdoor.tf#29)
- resource.azurerm_cdn_frontdoor_route.main (/terraform-docs/modules/api/frontdoor.tf#5)
- resource.azurerm_key_vault_secret.cookie_secret (/terraform-docs/modules/api/cookie_secret.tf#18)
- resource.azurerm_role_assignment.cookie_secret_readers (/terraform-docs/modules/api/cookie_secret.tf#41)
- resource.azurerm_role_assignment.cookie_secret_user (/terraform-docs/modules/api/cookie_secret.tf#27)
- resource.random_id.cookie_secret (/terraform-docs/modules/api/cookie_secret.tf#9)    
<!-- END_TF_DOCS -->

---

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