Skip to content

RELOPS-2520: locked-down on-demand VMs for Desktop Integrations - #324

Merged
markcor merged 1 commit into
masterfrom
RELOPS-2520-desktop-integrations-vms
Aug 24, 2026
Merged

RELOPS-2520: locked-down on-demand VMs for Desktop Integrations#324
markcor merged 1 commit into
masterfrom
RELOPS-2520-desktop-integrations-vms

Conversation

@markcor

@markcor markcor commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

RELOPS-2520 — Desktop Integrations need to spin up short-lived VMs to reproduce bugs across OSes.

Other VM-using teams hold Contributor at subscription scope. This confines DI to one resource group instead.

Resource Scope
rg-west-us-desktop-integrations FF Non-CI — the only place the group can create anything
fx-nonci-desktop-integrations-vm-operator that RG — full VM lifecycle
fx-nonci-desktop-integrations-browse subscription — 7 read-only actions, so the portal renders

Group: Desktop Integrations VMsiholmes@, aborondo@, cdupuis@, ahabibi@ (manager), markco_test@. All RBAC binds to the group, never to individuals, so membership changes are one line in terraform.tfvars.

Apply order matters: azure_fx_nonci first, then azure_ad. A role definition can't be scoped to an RG that doesn't exist yet.

Review notes

  • Setting the role's scope to the RG pins assignable_scopes there — it can't be assigned elsewhere.
  • Escalation blocked by omission: no Microsoft.Authorization writes, no ManagedIdentity/*/assign/action, no virtualNetworkPeerings/write.
  • DI build from Marketplace to match real-world environments, so no RelOps gallery access. Marketplace terms are read-only — paid/term-gated offers need RelOps to accept.
  • Includes networkSecurityGroups/write, so DI can open RDP/SSH to 0.0.0.0/0 inside their own RG.
  • Subscription-scope grants still inherit into this RG; accepted deliberately, tracked in RELOPS-2529.

Verified: validate + fmt clean, all 49 action strings checked against the live Azure providerOperations API, no name collisions, plans run against real state.

terraform planazure_fx_nonci: 1 to add, 0 to change, 0 to destroy
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.rg-west-us-desktop-integrations will be created
  + resource "azurerm_resource_group" "rg-west-us-desktop-integrations" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "rg-west-us-desktop-integrations"
      + tags     = {
          + "Name"             = "rg-west-us-desktop-integrations"
          + "owner_email"      = "mcornmesser@mozilla.com"
          + "production_state" = "production"
          + "project_name"     = "azure_fx_nonci"
          + "source_repo_url"  = "https://github.com/mozilla-platform-ops/relops_infra_as_code"
          + "terraform"        = "true"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
terraform planazure_ad: 10 to add, 0 to change, 0 to destroy
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azuread_group.desktop_integrations_vms will be created
  + resource "azuread_group" "desktop_integrations_vms" {
      + auto_subscribe_new_members     = (known after apply)
      + description                    = "Managed by RelOps — on-demand test VM access in rg-west-us-desktop-integrations (FF Non-CI)"
      + display_name                   = "Desktop Integrations VMs"
      + external_senders_allowed       = (known after apply)
      + hide_from_address_lists        = (known after apply)
      + hide_from_outlook_clients      = (known after apply)
      + id                             = (known after apply)
      + mail                           = (known after apply)
      + mail_enabled                   = false
      + mail_nickname                  = (known after apply)
      + members                        = (known after apply)
      + object_id                      = (known after apply)
      + onpremises_domain_name         = (known after apply)
      + onpremises_group_type          = (known after apply)
      + onpremises_netbios_name        = (known after apply)
      + onpremises_sam_account_name    = (known after apply)
      + onpremises_security_identifier = (known after apply)
      + onpremises_sync_enabled        = (known after apply)
      + owners                         = (known after apply)
      + preferred_language             = (known after apply)
      + prevent_duplicate_names        = false
      + proxy_addresses                = (known after apply)
      + security_enabled               = true
      + visibility                     = (known after apply)
      + writeback_enabled              = false
    }

  # azuread_group_member.desktop_integrations_vms_membership["aborondo@mozilla.com"] will be created
  + resource "azuread_group_member" "desktop_integrations_vms_membership" {
      + group_object_id  = (known after apply)
      + id               = (known after apply)
      + member_object_id = "b617daaa-e8c3-42ec-9ca2-f0b180af8aee"
    }

  # azuread_group_member.desktop_integrations_vms_membership["ahabibi@mozilla.com"] will be created
  + resource "azuread_group_member" "desktop_integrations_vms_membership" {
      + group_object_id  = (known after apply)
      + id               = (known after apply)
      + member_object_id = "480ada98-6795-47bf-bc9f-b7069a63e699"
    }

  # azuread_group_member.desktop_integrations_vms_membership["cdupuis@mozilla.com"] will be created
  + resource "azuread_group_member" "desktop_integrations_vms_membership" {
      + group_object_id  = (known after apply)
      + id               = (known after apply)
      + member_object_id = "b1886be7-cebc-4c8d-bf55-cd0482f3b064"
    }

  # azuread_group_member.desktop_integrations_vms_membership["iholmes@mozilla.com"] will be created
  + resource "azuread_group_member" "desktop_integrations_vms_membership" {
      + group_object_id  = (known after apply)
      + id               = (known after apply)
      + member_object_id = "31cbf9d1-ddcc-4084-8357-eb4284d46b39"
    }

  # azuread_group_member.desktop_integrations_vms_membership["markco_test@mozilla.com"] will be created
  + resource "azuread_group_member" "desktop_integrations_vms_membership" {
      + group_object_id  = (known after apply)
      + id               = (known after apply)
      + member_object_id = "e759a696-07ea-48ec-b64d-6b09417d3db2"
    }

  # azurerm_role_assignment.desktop_integrations_browse will be created
  + resource "azurerm_role_assignment" "desktop_integrations_browse" {
      + condition_version                = (known after apply)
      + id                               = (known after apply)
      + name                             = (known after apply)
      + principal_id                     = (known after apply)
      + principal_type                   = (known after apply)
      + role_definition_id               = (known after apply)
      + role_definition_name             = (known after apply)
      + scope                            = "/subscriptions/0a420ff9-bc77-4475-befc-a05071fc92ec"
      + skip_service_principal_aad_check = (known after apply)
    }

  # azurerm_role_assignment.desktop_integrations_vm_operator will be created
  + resource "azurerm_role_assignment" "desktop_integrations_vm_operator" {
      + condition_version                = (known after apply)
      + id                               = (known after apply)
      + name                             = (known after apply)
      + principal_id                     = (known after apply)
      + principal_type                   = (known after apply)
      + role_definition_id               = (known after apply)
      + role_definition_name             = (known after apply)
      + scope                            = "/subscriptions/0a420ff9-bc77-4475-befc-a05071fc92ec/resourceGroups/rg-west-us-desktop-integrations"
      + skip_service_principal_aad_check = (known after apply)
    }

  # azurerm_role_definition.fx_nonci_desktop_integrations_browse will be created
  + resource "azurerm_role_definition" "fx_nonci_desktop_integrations_browse" {
      + assignable_scopes           = (known after apply)
      + description                 = "Minimum subscription-level reads required for the Azure portal to render the FF Non-CI subscription and VM size/quota/image pickers."
      + id                          = (known after apply)
      + name                        = "fx-nonci-desktop-integrations-browse"
      + role_definition_id          = (known after apply)
      + role_definition_resource_id = (known after apply)
      + scope                       = "/subscriptions/0a420ff9-bc77-4475-befc-a05071fc92ec"

      + permissions {
          + actions = [
              + "Microsoft.Resources/subscriptions/read",
              + "Microsoft.Resources/subscriptions/resourceGroups/read",
              + "Microsoft.Compute/locations/usages/read",
              + "Microsoft.Compute/locations/vmSizes/read",
              + "Microsoft.Compute/skus/read",
              + "Microsoft.Network/locations/*/read",
              + "Microsoft.MarketplaceOrdering/agreements/offers/plans/read",
            ]
        }
    }

  # azurerm_role_definition.fx_nonci_desktop_integrations_vm_operator will be created
  + resource "azurerm_role_definition" "fx_nonci_desktop_integrations_vm_operator" {
      + assignable_scopes           = (known after apply)
      + description                 = "Create, manage and delete VMs within the Desktop Integrations resource group. No RBAC, no identity assignment, no cross-RG reach."
      + id                          = (known after apply)
      + name                        = "fx-nonci-desktop-integrations-vm-operator"
      + role_definition_id          = (known after apply)
      + role_definition_resource_id = (known after apply)
      + scope                       = "/subscriptions/0a420ff9-bc77-4475-befc-a05071fc92ec/resourceGroups/rg-west-us-desktop-integrations"

      + permissions {
          + actions     = [
              + "Microsoft.Authorization/*/read",
              + "Microsoft.Compute/*/read",
              + "Microsoft.Insights/*/read",
              + "Microsoft.Network/*/read",
              + "Microsoft.ResourceHealth/availabilityStatuses/read",
              + "Microsoft.Resources/subscriptions/resourceGroups/read",
              + "Microsoft.Resources/subscriptions/resourceGroups/resources/read",
              + "Microsoft.Storage/*/read",
              + "Microsoft.Resources/deployments/*",
              + "Microsoft.Compute/virtualMachines/write",
              + "Microsoft.Compute/virtualMachines/delete",
              + "Microsoft.Compute/virtualMachines/start/action",
              + "Microsoft.Compute/virtualMachines/restart/action",
              + "Microsoft.Compute/virtualMachines/powerOff/action",
              + "Microsoft.Compute/virtualMachines/deallocate/action",
              + "Microsoft.Compute/virtualMachines/redeploy/action",
              + "Microsoft.Compute/virtualMachines/reimage/action",
              + "Microsoft.Compute/virtualMachines/runCommand/action",
              + "Microsoft.Compute/virtualMachines/extensions/*",
              + "Microsoft.Compute/disks/write",
              + "Microsoft.Compute/disks/delete",
              + "Microsoft.Compute/disks/beginGetAccess/action",
              + "Microsoft.Compute/disks/endGetAccess/action",
              + "Microsoft.Compute/snapshots/write",
              + "Microsoft.Compute/snapshots/delete",
              + "Microsoft.Compute/snapshots/beginGetAccess/action",
              + "Microsoft.Compute/snapshots/endGetAccess/action",
              + "Microsoft.Network/networkInterfaces/write",
              + "Microsoft.Network/networkInterfaces/delete",
              + "Microsoft.Network/networkInterfaces/join/action",
              + "Microsoft.Network/networkSecurityGroups/write",
              + "Microsoft.Network/networkSecurityGroups/delete",
              + "Microsoft.Network/networkSecurityGroups/join/action",
              + "Microsoft.Network/publicIPAddresses/write",
              + "Microsoft.Network/publicIPAddresses/delete",
              + "Microsoft.Network/publicIPAddresses/join/action",
              + "Microsoft.Network/virtualNetworks/write",
              + "Microsoft.Network/virtualNetworks/delete",
              + "Microsoft.Network/virtualNetworks/subnets/join/action",
            ]
          + not_actions = [
              + "Microsoft.Authorization/*/Delete",
              + "Microsoft.Authorization/*/Write",
              + "Microsoft.Authorization/elevateAccess/Action",
            ]
        }
    }

Plan: 10 to add, 0 to change, 0 to destroy.

Rebased onto 337986a after #325 removed splunk.tf; this branch is now pure additions.

🤖 Generated with Claude Code

@markcor
markcor force-pushed the RELOPS-2520-desktop-integrations-vms branch 2 times, most recently from 8e28566 to 20df79d Compare August 24, 2026 17:40
Desktop Integrations need to spin up short-lived VMs to reproduce bugs and
validate patches across operating systems. Rather than the subscription-scope
Contributor that other VM-using teams hold, this confines them to a single
resource group in FF Non-CI.

- rg-west-us-desktop-integrations, the only place the group can create anything
- fx-nonci-desktop-integrations-vm-operator, an RG-scoped custom role covering
  the full VM lifecycle. Setting scope to the RG pins assignable_scopes there,
  so the role cannot be assigned elsewhere.
- fx-nonci-desktop-integrations-browse, seven subscription-scope reads so the
  portal can render the subscription. Narrower than built-in Reader.

DI build from Marketplace images to match real-world environments, so no access
to the RelOps gallery is granted. Marketplace terms are read-only: term-gated or
paid offers need RelOps to accept terms for that offer.

Escalation is blocked by omission: no Microsoft.Authorization writes, no
ManagedIdentity/*/assign/action, no virtualNetworkPeerings/write. All RBAC is
bound to the group, never to individual users.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@markcor
markcor force-pushed the RELOPS-2520-desktop-integrations-vms branch from 20df79d to 258150e Compare August 24, 2026 17:45
@markcor
markcor merged commit 62d7f91 into master Aug 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant