Image Impressionist-style illustration of infrastructure-as-code migration from legacy alerting to unified on-call

Opsgenie Migration

New

Migrating from Opsgenie to All Quiet: A Full Terraform-First Guide

If your Opsgenie config already lives in Terraform, you can migrate methodically instead of clicking two consoles side by side. This guide translates users, teams, integrations, on-call schedules, escalations, and routing into All Quiet - complete with example HCL, migration checklist, and tips for running both tools in parallel before you switch.

Nikolas Köppl

By Nikolas Köppl · Go-to-market at All Quiet

Updated: Tuesday, 12 May 2026

Published: Tuesday, 12 May 2026

With the recent changes in the Atlassian ecosystem, many SRE and DevOps teams are finding themselves at a crossroads: adapt to the increasing complexity of Jira Service Management (JSM) or move to a leaner, more focused incident management platform.

At All Quiet, we believe incident management should stay close to the code. That's why our platform is built to be managed via Terraform from day one. In this guide, we'll walk through a complete technical migration from Opsgenie Terraform resources to All Quiet, resource by resource, with real HCL on both sides.

If you are still weighing vendors before you change tooling, start with our overview of All Quiet as an Opsgenie alternative, then use this article for the Terraform resource mapping and cutover checklist.

Why a Terraform-First Migration?

If you're already managing Opsgenie via Terraform, you have an advantage: your entire on-call configuration is already codified. Rather than clicking through two UIs in parallel, you can translate your .tf files directly from one provider to the other, terraform plan the result, and cut over with confidence.

The Strategy: "Logic-First" Migration

In Opsgenie, configuration is fragmented across six or more resource types: opsgenie_user, opsgenie_team, opsgenie_api_integration, opsgenie_schedule, opsgenie_schedule_rotation, and opsgenie_escalation. All Quiet centralizes this logic into fewer, more cohesive resources, most notably allquiet_team_escalations, which unifies schedules, rotations, and escalation policies into a single resource that can't get out of sync.

Here's the full resource mapping at a glance:

Opsgenie Resource All Quiet Resource Notes
opsgenie_user allquiet_user Standalone identity
opsgenie_team allquiet_team + allquiet_team_membership One membership resource per user–team pair
opsgenie_api_integration allquiet_integration Team-owned, strongly typed
opsgenie_schedule allquiet_team_escalations Merged into unified resource
opsgenie_schedule_rotation allquiet_team_escalations Rotations live inside escalation tiers
opsgenie_escalation allquiet_team_escalations Rules become escalation tiers
(routing within integration) allquiet_routing Explicit routing resource

1. Setting Up the Providers

First, initialize your environment. You'll need your All Quiet API Key, which you can generate in Organization Settings > API Keys (requires Owner or Administrator role). See our Terraform setup docs for the full walkthrough.

  
terraform {
  required_providers {
    allquiet = {
      source  = "AllQuietApp/allquiet"
      version = ">= 3.0.0"
    }
  }
}

provider "allquiet" {
  api_key    = var.allquiet_api_key
  api_region = "eu" # or "us" — must match your organization's data region
}

variable "allquiet_api_key" {
  type      = string
  sensitive = true
}
  

Tip: We recommend creating your organization with a shared admin account (e.g., admin@company.com) rather than a personal email. This way, every "real" on-call user can be provisioned via Terraform, and you won't have a chicken-and-egg problem with the account that created the org.

2. Teams, Users, and Memberships

In Opsgenie, users are standalone resources with roles, and team membership is defined inline within the team. This creates tight coupling, changing a user's team membership means editing the team resource.

All Quiet separates these concerns into three distinct resources: the team, the user identity, and the membership link between them. Each membership is its own resource (allquiet_team_membership), one resource per user–team pair. This allows for cleaner state management: adding or removing a single member doesn't trigger a plan change on the team or on any other member.

The Opsgenie Way:

  
resource "opsgenie_user" "sre_lead" {
  username  = "alex@company.com"
  full_name = "Alex Rivera"
  role      = "user"
  timezone  = "Europe/Berlin"
}

resource "opsgenie_team" "devops" {
  name        = "DevOps"
  description = "Core DevOps and SRE team"

  member {
    id   = opsgenie_user.sre_lead.id
    role = "admin"
  }

  member {
    id   = opsgenie_user.backend_eng.id
    role = "user"
  }
}
  

The All Quiet Way:

  
# 1. Define the team
resource "allquiet_team" "devops" {
  display_name = "DevOps"
  time_zone_id = "Europe/Berlin"
}

# 2. Define user identities
resource "allquiet_user" "sre_lead" {
  email        = "alex@company.com"
  display_name = "Alex Rivera"
}

resource "allquiet_user" "backend_eng" {
  email        = "jordan@company.com"
  display_name = "Jordan Lee"
}

# 3. Link each user to the team via a dedicated membership resource (one per user–team pair)
resource "allquiet_team_membership" "devops_sre_lead" {
  team_id = allquiet_team.devops.id
  user_id = allquiet_user.sre_lead.id
  role    = "Administrator" # "Administrator" or "Member"
}

resource "allquiet_team_membership" "devops_backend_eng" {
  team_id = allquiet_team.devops.id
  user_id = allquiet_user.backend_eng.id
  role    = "Member"
}
  

What changed:

  • Opsgenie's admin / user team roles map to All Quiet's Administrator / Member.
  • Each membership is its own resource (allquiet_team_membership), so adding or removing a single team member is a targeted change, no cascading diffs on the team or other members.
  • Users provisioned via Terraform receive an invite to set their password. If you use SSO (OIDC, Google, or Microsoft), configure that first, see the SSO docs.

3. Integrations: Team-Owned and Strongly Typed

Opsgenie requires separate resources for API integrations and their subsequent notification or routing actions. The integration itself is often a loose endpoint that you wire to teams via responders blocks.

All Quiet treats integrations as team-owned endpoints with strongly typed integration types, so there's no guesswork about payload format.

The Opsgenie Way:

  
resource "opsgenie_api_integration" "grafana" {
  name              = "Grafana-Alerts"
  type              = "Grafana"
  owner_team_id     = opsgenie_team.devops.id
  enabled           = true
  allow_write_access = true

  responders {
    type = "team"
    id   = opsgenie_team.devops.id
  }
}
  

The All Quiet Way:

  
resource "allquiet_integration" "grafana" {
  team_id      = allquiet_team.devops.id
  display_name = "Grafana Production"
  type         = "Grafana"
}
  

That's it, no responders block, no allow_write_access flag. The integration belongs to the team, and the team's escalation policy handles notification logic.

Common type mappings:

Opsgenie type All Quiet type
Datadog Datadog
Prometheus Prometheus
Grafana Grafana
CloudWatch AmazonCloudWatch
API / Webhook Webhook

The full list of supported integration types is available at: https://allquiet.app/api/public/v1/inbound-integration/types

Important: Treat the integration type and team assignment as fixed once created, changing them may require destroying and re-creating the resource, which generates a new webhook URL. Plan these carefully. After terraform apply, the new webhook URL will be available. For each supported inbound integration type you can download a default Terraform snippet for payload mapping: use https://allquiet.app/api/integrations/terraform/default/<Type>.tf where <Type> is the exact integration type identifier (see the inbound integration types list above). For example, Datadog is https://allquiet.app/api/integrations/terraform/default/Datadog.tf; Grafana, Webhook, AmazonCloudWatch, and every other supported type follow the same URL pattern with their own type name.

If you need to customize how payloads map to incidents (e.g., extracting severity from a specific JSON field), use the allquiet_integration_mapping resource. If you don't define one, All Quiet uses sensible defaults for each integration type. The mapping supports JSONPath, XPath, regex, and static values and every incident maps to three key attributes: Status (Open/Resolved), Severity (Minor/Warning/Critical), and an optional Title.

Migration tip: You can run both Opsgenie and All Quiet integrations in parallel during the transition period. Point your monitoring tools at both webhook URLs until you're confident in the All Quiet setup.

4. On-Call Schedules and Escalations: The Unified Resource

This is the most significant architectural difference between the two providers, and the biggest win in your Terraform-first migration.

In Opsgenie, on-call configuration is spread across three separate resources that reference each other by ID. In All Quiet, it's all one resource: allquiet_team_escalations. This resource follows a clear hierarchy: Escalation Tiers → Schedules → Rotations, which mirrors how on-call actually works: you have layers of people to notify (tiers), each layer has time-based coverage windows (schedules), and people rotate through those windows (rotations).

The Opsgenie Way (3 resources, fragile cross-references):

  
resource "opsgenie_schedule" "devops_oncall" {
  name          = "DevOps On-Call"
  timezone      = "Europe/Berlin"
  enabled       = true
  owner_team_id = opsgenie_team.devops.id
}

resource "opsgenie_schedule_rotation" "devops_weekly" {
  schedule_id = opsgenie_schedule.devops_oncall.id
  name        = "Weekly Rotation"
  type        = "weekly"
  length      = 1
  start_date  = "2024-01-01T09:00:00Z"

  participant {
    type = "user"
    id   = opsgenie_user.sre_lead.id
  }

  participant {
    type = "user"
    id   = opsgenie_user.backend_eng.id
  }
}

resource "opsgenie_escalation" "devops_escalation" {
  name          = "DevOps Escalation"
  owner_team_id = opsgenie_team.devops.id

  rules {
    condition   = "if-not-acked"
    notify_type = "default"
    delay       = 5

    recipient {
      type = "schedule"
      id   = opsgenie_schedule.devops_oncall.id
    }
  }

  rules {
    condition   = "if-not-acked"
    notify_type = "default"
    delay       = 15

    recipient {
      type = "user"
      id   = opsgenie_user.manager.id
    }
  }

  repeat {
    wait_interval          = 30
    count                  = 3
    reset_recipient_states = true
  }
}
  

That's 3 resources, 50+ lines, with IDs threaded between them. Delete the schedule without updating the escalation and you get a dangling reference.

The All Quiet Way (1 resource, self-contained):

  
resource "allquiet_team_escalations" "devops_oncall" {
  team_id = allquiet_team.devops.id

  escalation_tiers {
    # TIER 1: On-call rotation — alert the person on duty
    repeats               = 3
    repeats_after_minutes = 5

    schedules {
      display_name = "DevOps Weekly Rotation"

      rotation_settings {
        rotation_mode      = "auto"
        auto_rotation_size = 1         # One person on-call at a time
        repeats            = "weekly"
      }

      rotations {
        members {
          team_membership_id = allquiet_team_membership.devops_sre_lead.id
        }
        members {
          team_membership_id = allquiet_team_membership.devops_backend_eng.id
        }
      }
    }
  }

  escalation_tiers {
    # TIER 2: If Tier 1 exhausts its repeats, escalate to the manager
    repeats = 1

    schedules {
      display_name = "Manager Escalation"

      rotations {
        members {
          team_membership_id = allquiet_team_membership.devops_manager.id
        }
      }
    }
  }
}
  

Everything that was spread across opsgenie_schedule, opsgenie_schedule_rotation, and opsgenie_escalation is now a single allquiet_team_escalations resource. Schedules and rotations live inside escalation tiers, so there's no way for them to become orphaned. Every person, whether they're in a rotating schedule or a single-person escalation target, is referenced via their team_membership_id, which keeps the dependency graph clean.

Key mapping:

Opsgenie concept All Quiet equivalent
Escalation rule with delay escalation_tiers with repeats_after_minutes
opsgenie_schedule (time windows) schedules block within a tier, define on-call times (e.g., weekdays 08:00–18:00)
opsgenie_schedule_rotation rotation_settings within a schedules block, auto or explicit mode
Rotation participants rotationsmembersteam_membership_id (always via membership)
Multiple schedules for follow-the-sun Multiple schedules blocks in the same tier, each covering different hours/days
repeat block on escalation repeats on the relevant tier
Recipient: schedule Tier with a schedules block containing rotations
Recipient: user Tier with one schedule, one rotation, one member

Advanced patterns: Round-robin alerting distributes incidents evenly when multiple people are on-call simultaneously. On-call overrides, both personal and team-level, can be managed via the allquiet_on_call_override Terraform resource without touching the escalation config. See the escalation docs for the full set of options.

Note on complexity: Opsgenie's delay/repeat model and All Quiet's tier-level repeats / repeats_after_minutes / auto_escalation_after_minutes don't map one-to-one in every case. Simple escalations translate cleanly, but complex multi-rule Opsgenie policies may need case-by-case tuning. We recommend testing each escalation path with a synthetic incident before cutting over.

Note on time restrictions: Opsgenie's time_restriction blocks on rotations (time-of-day and weekday-and-time-of-day) map to All Quiet's schedule on-call times. In All Quiet, each schedule defines its active hours and days directly (e.g., "Monday–Friday, 09:00–17:00"), which is more intuitive than Opsgenie's separate restriction blocks. Review these during migration.

5. Routing: The Incident Traffic Controller

In Opsgenie, routing logic is often buried inside the integration itself (via responders blocks) or handled by notification policies. In All Quiet, routing is an explicit, first-class resource with a powerful rules engine. Each rule has three components: Conditions (when to trigger), Actions (what to do), and Channels (how to notify).

  
resource "allquiet_routing" "prod_alerts" {
  team_id      = allquiet_team.devops.id
  display_name = "Production Alert Routing"

  rules = [
    {
      # Mute Slack for test environment alerts, only send email
      conditions = {
        statuses   = ["Open"]
        attributes = [{ name = "Environment", operator = "=", value = "Test" }]
      }
      actions = {
        change_severity = "Minor"
      }
      channels = {
        notification_channels = ["Email"]
      }
    },
    {
      # For escalated critical incidents, also trigger the PagerDuty outbound webhook
      conditions = {
        statuses   = ["Open"]
        severities = ["Critical"]
        intents    = ["Escalated"]
      }
      channels = {
        outbound_integrations = [allquiet_outbound_integration.pagerduty_webhook.id]
      }
    }
  ]
}
  

Valid notification_channels values are "Email", "Push", "SMS", and "VoiceCall".

Routing conditions can filter on severity, status, specific integrations, incident intents (created, escalated, resolved), custom payload attributes, and even time-of-day restrictions. Actions include discarding incidents, changing severity, assigning to other teams (within an Organization), adding interactions, and delaying execution. This replaces Opsgenie's scattered notification policies with a single, auditable, version-controlled resource.

Pro tip: For multi-team setups, you can create a "root team" that owns your integrations and uses routing rules to fan out incidents to the appropriate team based on payload attributes. See the routing docs for detailed examples.

Migration Checklist

Here's the step-by-step order we recommend. The key dependency is that allquiet_team_membership requires both the team and user to exist, and allquiet_team_escalations references membership IDs, so teams, users, and memberships must all be in place before you build escalation tiers.

  1. Set up the All Quiet Organization and generate your API key.
  2. Create teams (allquiet_team) and provision users (allquiet_user). These two have no dependency on each other and can be created in any order or in parallel.
  3. Link users to teams (allquiet_team_membership), one resource per user–team pair. This requires both the team and user to exist.
  4. Create integrations (allquiet_integration) for each monitoring source. Note the new webhook URLs.
  5. Customize payload mappings (allquiet_integration_mapping) if the defaults don't fit your payload structure.
  6. Configure notification preferences (allquiet_user_incident_notification_settings), this controls how each user gets alerted (push, SMS, voice call, email) and with what delay.
  7. Build escalation policies (allquiet_team_escalations) by merging your Opsgenie schedules, rotations, and escalation rules into unified tiers. Rotations reference team_membership_id, so memberships must exist first.
  8. Set up routing rules (allquiet_routing) for any advanced alert routing.
  9. Set up outbound integrations (allquiet_outbound_integration) for Slack, Microsoft Teams, or webhook notifications.
  10. Run both systems in parallel, point your monitoring tools at both Opsgenie and All Quiet webhook URLs for a burn-in period. Trigger test incidents to verify the full notification chain.
  11. Cut over, update webhook URLs to point only to All Quiet, then terraform destroy the Opsgenie resources.

Why Switch to All Quiet?

  • Bootstrapped and independent. We aren't beholden to Private Equity, Venture Capital firms or enterprise conglomerates. We build for SREs, not for quarterly earnings.
  • Infrastructure as Code, natively. Our Terraform provider isn't an afterthought, it's built to be the primary way you manage your on-call setup. Resources provisioned via Terraform are locked in the web app to prevent drift.
  • Cost efficiency. Stop paying the "Atlassian Tax." All Quiet provides the same high-availability alerting at a fraction of the cost, with a transparent pricing model.
  • Less fragmentation, less drift. Opsgenie spreads on-call logic across separate schedule, rotation, and escalation resources that reference each other by ID. All Quiet collapses that into a single allquiet_team_escalations resource, fewer cross-references means fewer ways for your Terraform state to diverge from reality.

Ready to simplify your stack? Check out our Terraform Provider documentation and start your migration today.

Nikolas Köppl

Author

Nikolas Köppl

Go-to-market at All Quiet

Builds go-to-market and customer-first growth for teams adopting calmer, clearer incident communication.