Skip to content

powerplatform_environment_disaster_recovery resource: Enable/Disable Disaster Recovery for a Power Platform Environment #946

@mattdot

Description

@mattdot

Summary

Add a new Terraform resource powerplatform_environment_disaster_recovery that enables (and, if supported, disables) Disaster Recovery for an existing Power Platform environment using the Business App Platform (BAP) Admin API. The provider should issue the DR PATCH, capture the lifecycle operation ID from a response header, and poll the lifecycle operation until it completes. This should not be part of the base environment resource since the environment must exist and be a managed environment to enable DR. Since managed env is a separate resource, DR can't be applied on an environment create, this should be a separate resource that can be applied after managed env.

Preconditions (platform behavior):

  • Environment must be Production
  • Environment must be a Managed Environment
  • The paired/secondary region is predetermined by the environment configuration (not user-selectable in this flow)

Proposed UX

resource "powerplatform_environment_disaster_recovery" "prod_dr" {
  environment_id = powerplatform_environment.prod.id

  enabled = true
}

Behavior

  • Create: Enable DR via PATCH; capture operation ID from response headers; poll lifecycle operation to Succeeded.
  • Read: Report current DR state (enabled/disabled) and last lifecycle metadata.
  • Update: Flip enabled and repeat PATCH + poll.
  • Delete: If the platform supports disabling via PATCH, send the disable PATCH and poll; otherwise document as a no-op.

API Contract (observed)

Enable DR (PATCH)

PATCH https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/scopes/admin/environments/{environmentId}?api-version=2021-04-01
Content-Type: application/json
Authorization: Bearer <token>

{
  "properties": {
    "states": {
      "disasterRecovery": { "id": "Enabled" }
    }
  }
}

Response: 202 Accepted

Headers (observed/expected):

  • An operation reference header (e.g., Location and/or Azure-AsyncOperation) that yields or includes the lifecycle operation ID
  • Optionally Retry-After for polling cadence

Poll lifecycle operation (GET)

GET https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/lifecycleOperations/{operationId}?api-version=2021-04-01

Terminal success example (real response captured):

{
  "stages": [
    {"id":"Validate","name":"Validate","state":{"id":"Succeeded"},"firstActionDateTime":"2025-10-03T21:30:42.268922Z","lastActionDateTime":"2025-10-03T21:30:42.268922Z"},
    {"id":"Prepare","name":"Prepare","state":{"id":"Succeeded"},"firstActionDateTime":"2025-10-03T21:30:42.2845463Z","lastActionDateTime":"2025-10-03T21:30:42.2845463Z"},
    {"id":"Run","name":"Run","state":{"id":"Succeeded"},"firstActionDateTime":"2025-10-03T21:30:42.300174Z","lastActionDateTime":"2025-10-03T21:32:46.6722115Z"},
    {"id":"Finalize","name":"Finalize","state":{"id":"Succeeded"},"firstActionDateTime":"2025-10-03T21:32:47.0784649Z","lastActionDateTime":"2025-10-03T21:32:47.6409748Z"}
  ],
  "id":"519e32e9-9e86-453d-a45d-b90d390a9623",
  "links":{
    "self":{"path":"/providers/Microsoft.BusinessAppPlatform/lifecycleOperations/519e32e9-9e86-453d-a45d-b90d390a9623"},
    "environment":{"path":"/providers/Microsoft.BusinessAppPlatform/environments/ba87dcb8-f946-e87a-b4c6-8aafaa5c081d"}
  },
  "type":{"id":"Edit"},
  "typeDisplayName":"Edit",
  "state":{"id":"Succeeded"},
  "createdDateTime":"2025-10-03T21:30:40.0422098Z",
  "lastActionDateTime":"2025-10-03T21:32:47.6409748Z",
  "requestedBy":{"id":"68429daf-e401-4e49-9354-31eacc594b56","displayName":"System Administrator","type":"User"}
}

Terminal condition: state.id == "Succeeded" (provider should also surface failed/canceled stage details on error).

Disable DR (PATCH) — to be verified

PATCH .../environments/{environmentId}?api-version=2021-04-01
{
  "properties": {
    "states": {
      "disasterRecovery": { "id": "Disabled" }
    }
  }
}

Assumption: same 202 + lifecycle operation behavior.


Provider Implementation Notes

  • Schema

    • environment_id (Required, string)

    • enabled (Optional, bool; default true if resource is created)

    • Computed:

      • status (e.g., "Enabled" / "Disabled")
      • last_operation_id
      • last_action_time
      • requested_by (if helpful for audit)
  • Validation

    • Before PATCH, GET environment; if not Production or not Managed, return a clear, actionable error.
  • Lifecycle Operation Handling

    • Read operation ID from response headers (e.g., parse Location or Azure-AsyncOperation to extract {operationId}).
    • Poll lifecycleOperations/{operationId} until terminal (Succeeded/Failed/Canceled), honoring Retry-After if present; otherwise capped exponential backoff (e.g., 5s → 10s → … → 60s).
    • Respect timeouts and surface stage-by-stage progress in debug logs.
  • Idempotency

    • If DR already enabled, treat create/update as no-op.
    • Plan should be stable after success.
  • Delete semantics

    • If disable is supported: on terraform destroy, send the disable PATCH and wait for terminal success.
    • If not supported: document as a no-op and require manual disable outside Terraform.
  • Import

    • terraform import powerplatform_environment_disaster_recovery.this {environmentId}
  • Testing

    • Acceptance tests behind an opt-in env flag (DR has tenant-level implications).
    • Test create → read (no drift) → optional disable (if supported).

Acceptance Criteria

  • New resource powerplatform_environment_disaster_recovery
  • Create/Update performs PATCH → reads operation ID from response header → polls lifecycle op to Succeeded
  • Pre-validation for Production + Managed
  • Stable plan after success; terraform refresh reflects DR state
  • Delete either disables DR (if supported) with the same LRO flow, or is clearly documented as a no-op
  • Timeouts + helpful error messages that include failing stage
  • Docs + example HCL
  • Opt-in acceptance tests

Open Items for Maintainers

  1. Header contract: confirm the exact header carrying the lifecycle operation link/ID (Location, Azure-AsyncOperation, etc.).
  2. Disable flow: confirm that setting "disasterRecovery": {"id": "Disabled"} via PATCH is supported and follows the same LRO pattern.
  3. RBAC: confirm the minimum role(s) required at tenant/environment scope for DR enable/disable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    communityRequest or issue originated from a customer or community requestenhancementNew feature or requestresourceterraform resource

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions