Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions quickstart/101-chaos-studio-workspace-azapi/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Chaos Studio v2 - Quickstart 101: a minimal AzAPI workspace
# =============================================================================
# This quickstart provisions the smallest useful Chaos Studio v2 footprint:
#
# * a resource group (with a random, human-readable name suffix),
# * a user-assigned managed identity for the workspace,
# * a Reader role assignment so that identity can discover in-scope resources,
# * a Microsoft.Chaos/workspaces resource, scoped to the resource group.
#
# It deliberately stops there: it does NOT author scenarios, scenario
# configurations, or run any fault actions. It is safe to `terraform apply` and
# `terraform destroy` repeatedly - nothing here injects a fault. Its job is to
# teach the AzAPI type/api-version idiom and the prerequisite identity + RBAC
# wiring that every Chaos workspace needs.
#
# Why AzAPI and not the azurerm provider? Microsoft.Chaos v2 is a *preview*
# api-version. The azurerm provider has no typed resource for it (and cannot,
# on the preview timeline), so we express the workspace as a raw ARM resource
# through the AzAPI provider, which can target any type + api-version. See
# https://learn.microsoft.com/en-us/azure/developer/terraform/overview-azapi-provider.
#
# For the full end-to-end workflow - a real fault target, a scenario
# configuration, and the discover -> evaluate -> validate -> fixResourcePermissions
# -> execute chain - see the companion quickstart
# `201-chaos-studio-scenario-run-azapi`.
# =============================================================================

# BEGIN chaos-codegen:api-versions
# Machine-managed Microsoft.Chaos api-version pins (api-version 2026-08-01-preview).
# Do NOT edit inside these markers by hand - they are regenerated from the
# Microsoft.Chaos ARM specification. Only this locals block is machine-managed;
# every other block in this file is hand-authored and never touched.
locals {
chaos = {
workspace = "Microsoft.Chaos/workspaces@2026-08-01-preview"
scenario = "Microsoft.Chaos/workspaces/scenarios@2026-08-01-preview"
scenario_configuration = "Microsoft.Chaos/workspaces/scenarios/configurations@2026-08-01-preview"
scenario_run = "Microsoft.Chaos/workspaces/scenarios/runs@2026-08-01-preview"
discovered_resource = "Microsoft.Chaos/workspaces/discoveredResources@2026-08-01-preview"
latest_validation = "Microsoft.Chaos/workspaces/scenarios/configurations/validations@2026-08-01-preview"
latest_permission_fix = "Microsoft.Chaos/workspaces/scenarios/configurations/fixResourcePermissions@2026-08-01-preview"
}
}
# END chaos-codegen:api-versions

# The subscription the deployment runs in, used to build the built-in role
# definition resource id below.
data "azurerm_subscription" "current" {}

# A random, readable suffix so repeated applies in the same subscription do not
# collide on resource names.
resource "random_pet" "this" {
length = 2
separator = "-"
}

locals {
resource_group_name = "${var.name_prefix}-${random_pet.this.id}"

# Reader (built-in role). Chosen over Owner/Contributor: discovery only needs
# read access to enumerate in-scope resources; per-scenario action roles are
# granted separately by the fixResourcePermissions action (see the 201
# quickstart), never by a broad standing grant here.
#
# GUID verified against the Azure built-in role catalog:
# https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/general#reader
reader_role_definition_id = "/subscriptions/${data.azurerm_subscription.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7"
}

resource "azurerm_resource_group" "this" {
name = local.resource_group_name
location = var.location
tags = var.tags
}

# User-assigned (not system-assigned) managed identity for the workspace. Using
# a user-assigned identity lets the Reader assignment below be created and
# destroyed on its own lifecycle, independently of the workspace resource.
resource "azurerm_user_assigned_identity" "chaos" {
name = "${local.resource_group_name}-id"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
tags = var.tags
}

# Grant the workspace identity Reader over the scope the workspace targets, so a
# later discovery can enumerate the in-scope resources.
resource "azurerm_role_assignment" "reader" {
scope = azurerm_resource_group.this.id
role_definition_id = local.reader_role_definition_id
principal_id = azurerm_user_assigned_identity.chaos.principal_id
}

# The Chaos Studio workspace, expressed as a raw ARM resource via AzAPI. `type`
# references the codegen-owned pin in local.chaos (above), so a spec/api-version
# bump is a one-line regeneration, never a hand edit of this block.
resource "azapi_resource" "workspace" {
type = local.chaos.workspace
name = "${random_pet.this.id}-chaos"
parent_id = azurerm_resource_group.this.id
location = azurerm_resource_group.this.location
tags = var.tags

identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.chaos.id]
}

body = {
properties = {
# `scopes` is the only spec-required workspace property: the ARM ids the
# workspace's child scenarios are allowed to target. Here it is the
# resource group created above.
scopes = [azurerm_resource_group.this.id]
}
}

# The Reader assignment must exist before the workspace so the identity is
# usable for discovery. Explicit because the dependency flows through
# principal_id, which Terraform cannot infer as a resource-id reference.
depends_on = [azurerm_role_assignment.reader]
}
14 changes: 14 additions & 0 deletions quickstart/101-chaos-studio-workspace-azapi/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "workspace_id" {
description = "The fully-qualified ARM id of the Chaos Studio workspace."
value = azapi_resource.workspace.id
}

output "workspace_identity_principal_id" {
description = "The principal id of the user-assigned identity the workspace uses for discovery. Grant this identity any additional roles a scenario requires."
value = azurerm_user_assigned_identity.chaos.principal_id
}

output "resource_group_name" {
description = "The resource group that holds the workspace and is its default scope."
value = azurerm_resource_group.this.name
}
33 changes: 33 additions & 0 deletions quickstart/101-chaos-studio-workspace-azapi/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
terraform {
required_version = ">= 1.5"

required_providers {
azapi = {
source = "Azure/azapi"
# >= 2.11.0: earlier azapi versions set azapi_resource_action output
# incorrectly while an action was still executing
# (Azure/terraform-provider-azapi GH-1168, fixed in 2.11.0). The 101 uses
# no actions, but both quickstarts pin the provider identically so the
# cross-example contract stays uniform.
version = ">= 2.11.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}

provider "azapi" {}

provider "azurerm" {
# azurerm v4 requires an explicit subscription id. When var.subscription_id is
# null the provider falls back to the ARM_SUBSCRIPTION_ID environment variable,
# so `az account set` / CI-provided credentials work without editing this file.
subscription_id = var.subscription_id
features {}
}
100 changes: 100 additions & 0 deletions quickstart/101-chaos-studio-workspace-azapi/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Quickstart 101 - Chaos Studio v2 workspace (AzAPI, minimal)

Provision the smallest useful Azure Chaos Studio v2 footprint with Terraform and
the [AzAPI provider](https://learn.microsoft.com/en-us/azure/developer/terraform/overview-azapi-provider):
a resource group, a user-assigned managed identity, a `Reader` role assignment,
and a `Microsoft.Chaos/workspaces` resource scoped to the resource group.

This example is intentionally minimal. It does **not** create scenarios, scenario
configurations, or run any fault actions, so it is safe to `apply` and `destroy`
repeatedly - nothing here injects a fault. For the full end-to-end workflow (a
real target, a scenario configuration, and the
`discover -> evaluate -> validate -> fixResourcePermissions -> execute` chain),
see the companion quickstart
[`201-chaos-studio-scenario-run-azapi`](../201-chaos-studio-scenario-run-azapi).

## Why AzAPI instead of the azurerm provider?

`Microsoft.Chaos` v2 is a **preview** api-version (`2026-08-01-preview`). The
`azurerm` provider has no typed resource for it and cannot offer one on the
preview timeline, so the workspace is expressed as a raw ARM resource through the
generic AzAPI provider, which can target any resource type and api-version. This
is the documented pattern for preview Azure services. Native `azurerm` support is
being delivered separately and is additive - these AzAPI examples remain the way
to reach preview api-versions.

> **Preview caveat.** Because this targets a preview api-version, the resource
> shape and behavior can change between preview releases, and the feature must be
> enabled for your subscription and available in your chosen region. Pin the
> api-version (this example does, via the codegen-owned `local.chaos` block in
> `main.tf`) and re-validate after any spec bump.

## Prerequisites

- Terraform `>= 1.5`.
- An Azure subscription with the Chaos Studio v2 preview enabled, and a region
where it is available (set `location`).
- **A target subscription.** Set `ARM_SUBSCRIPTION_ID` (the value `az account set`
already exports) or pass `-var 'subscription_id=<id>'`. The azurerm v4 provider
requires a subscription id; the example leaves `subscription_id` null so it is
sourced from the environment by default.
- **Permission to create role assignments on the target scope.** This example
assigns `Reader` to the workspace identity, which requires the deploying
principal to hold a role with `Microsoft.Authorization/roleAssignments/write`
on the scope - for example **Owner** or **User Access Administrator** on the
subscription or resource group. A principal with only Contributor cannot create
the role assignment and the apply will fail.

## Usage

```bash
export ARM_SUBSCRIPTION_ID="$(az account show --query id -o tsv)"
terraform init
terraform apply
```

Override the defaults as needed:

```bash
terraform apply -var 'location=westus2' -var 'name_prefix=demo-chaos' -var 'subscription_id=00000000-0000-0000-0000-000000000000'
```

Outputs:

| Output | Description |
|--------|-------------|
| `workspace_id` | ARM id of the created Chaos Studio workspace. |
| `workspace_identity_principal_id` | Principal id of the workspace's user-assigned identity; grant it any additional roles a scenario needs. |
| `resource_group_name` | The resource group holding the workspace (its default scope). |

## AzAPI action lifecycle caveats

This minimal quickstart runs no actions, but the same AzAPI behaviors apply to
the `201` quickstart and to any workflow you build on top of this one, so they
are called out here as well:

- **Actions fire on create/replace only.** An `azapi_resource_action` runs when
it is first created (or replaced); it does **not** re-fire on a subsequent
unchanged `terraform apply`. To re-run an action, taint or replace it
(`terraform apply -replace=<address>`).
- **`terraform destroy` performs no action** unless the action block sets
`when = "destroy"`. Destroying does not automatically undo an action's
server-side effect.
- **`fixResourcePermissions` with `whatIf = false` creates real role
assignments** that Terraform does not track in state and will not remove on
destroy. Use `whatIf = true` to preview the required assignments first, and
clean up any created assignments out of band.

## What this creates

| Resource | Purpose |
|----------|---------|
| `random_pet.this` | Readable, unique name suffix. |
| `azurerm_resource_group.this` | Holds the workspace; also its default scope. |
| `azurerm_user_assigned_identity.chaos` | Identity the workspace uses for discovery. |
| `azurerm_role_assignment.reader` | Grants that identity `Reader` on the scope. |
| `azapi_resource.workspace` | The `Microsoft.Chaos/workspaces` resource. |

The `Reader` role definition GUID (`acdd72a7-3385-48ef-bd42-f606fba81ae7`) is
verified against the
[Azure built-in role catalog](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/general#reader).
28 changes: 28 additions & 0 deletions quickstart/101-chaos-studio-workspace-azapi/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
variable "subscription_id" {
description = "Azure subscription ID to deploy into. The azurerm v4 provider requires a subscription id; when this is left null it is sourced from the ARM_SUBSCRIPTION_ID environment variable (which `az account set` and most CI set automatically). Set it explicitly to target a specific subscription. (The Reader role-definition id in main.tf is derived separately from the data.azurerm_subscription.current data source, not from this variable.)"
type = string
default = null
}

variable "location" {
description = "Azure region for the resource group and workspace. Must be a region where the Microsoft.Chaos v2 preview is enabled."
type = string
default = "eastus"
}

variable "name_prefix" {
description = "Prefix for generated resource names. A random pet suffix is appended for uniqueness."
type = string
default = "chaos-quickstart"

validation {
condition = can(regex("^[a-z][a-z0-9-]{1,20}$", var.name_prefix))
error_message = "name_prefix must be 2-21 characters: lowercase letters, digits, or hyphens, starting with a letter."
}
}

variable "tags" {
description = "Tags applied to every resource this quickstart creates."
type = map(string)
default = {}
}
Loading
Loading