The methods here are still valid for a scenario of delivering under a single tenant, but the Contino requirement required a custom setup, covering the implementation with greater detail in a separate document HERE

Overview

With the recent transition fro the EA portal into the Azuer Portal for managing and delivering subscriptions, Microsoft has thrown a spanner in the works if you’re operational staff were using the EA Portal to manage and track subscriptions for your org - with the less than fully transitioned process now documented as some azcli, some REST commands and some portal - so here we’ve bundled together each step in the process so you can finally automate this mundane task - and deliver Azure Subscription vending at the EA level

Azure Portal

Enterprise Accounts

  • Azure Portal :azure:

    • Azure Cost Management :costman:

      • Billing Scopes :costman:

        • Departments :dept:

          • Accounts :accts:

            • Subscriptions :subscription:

Preparation

Delivery

  • Confirm EA Account Access
  • Confirm EA Account Role Assignment :role:
  • Create an automation Service Principal :sp:
    • Assign Enrolment Role to SP :role:
    • Confirm Billing & Enrolment Account Access
  • Create a subscription :subscription:
    • within an enrolment account :costman:
  • Grant access to the requesting user :AD:
  • Grant access to other users at various role assignments :role:
  • Align Subscription Maintenance
    • Budgets & Alerts :costman:
    • Policy Assignment :policy:
    • management Groups :mangrp:

These steps ensure that we, and the automation account we will use are assigned correctly and have the access to create subscriptions

In steps, we deliver a subscription and then add layers of function as desired


In order to create subscriptions under your Microsoft EA Agreement, your account - or more specifically the account that will be used to provision the resources will require certain access and role assignments

  • EA Account
    • Enrolment Account Owner Role
    • Enrolment Account Subscription Creator Role


EA - Billing & Enrolment Accounts :costman:

  • Verified your permissions to setup SP
  • Verified your permissions to assign EA roles
  • Obtain Billing Account Name
  • Obtain Enrolment Account Name

After verifying that we have an account that is valid for EA Billing - we need to find out what entities we have access to - so that we can assign the access to our service principal when ready

EA Portal Deprecation

The traditional portal for management of EA accounts is via https://ea.azure.com/ - and you may still have access to this - but Microsoft are migrating away from this portal in favour of portal.azure.com and (I think in the interim) some direct REST calls for the permissions

az cli
az billing account list

terraform

There’s no provider resource or data source to directly get the data fpr the billing we need - so you can use the http data source and simply call the REST APIs

Billing Accounts

data "http" "get_billing" {
  url    = "https://management.azure.com/providers/Microsoft.Billing/billingaccounts/?api-version=2020-05-01"
  method = "GET"

 request_headers = {
    Authorization = "Bearer <token removed>"
  }
}

output "billing_accounts" {
value = jsondecode(data.http.get_billing.response_body).value.*.name
}
data.http.get_billing: Reading...
data.http.get_billing: Read complete after 1s [id=https://management.azure.com/providers/Microsoft.Billing/billingaccounts/?api-version=2020-05-01]

Changes to Outputs:
  + billing_accounts = [
      + "87561154",
      + "911f9cbb-b0d9-4ac1-854e-fd46a654629e",
    ]

Enrolment Accounts

data "http" "get_enrollment" {
  url    = "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts?api-version=2019-10-01-preview"
  method = "GET"

 request_headers = {
    Authorization = "Bearer <token removed>"
  }
}

output "billing_accounts" {
value = jsondecode(data.http.get_billing.response_body).value.*.name
}
Changes to Outputs:
  + enrolment_accounts = {
      + id         = "/providers/Microsoft.Billing/billingAccounts/87561154/enrollmentAccounts/283828"
      + name       = "283828"
      + properties = {
          + accountName = "Royal London Engineering"
          + costCenter  = "RLG"
          + displayName = "Royal London Engineering"
          + endDate     = "2023-11-30T00:00:00.0000000Z"
          + startDate   = "2021-04-20T00:00:00.0000000Z"
          + status      = "Active"
        }
      + type       = "Microsoft.Billing/billingAccounts/EnrollmentAccounts"
    }

REST API
GET https://management.azure.com/providers/Microsoft.Billing/billingaccounts/?api-version=2020-05-01
GET https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts?api-version=2019-10-01-preview

Create a Service Principal :sp:

  • Verified your permissions to setup SP
  • Verified your permissions to assign EA roles
  • Obtain Billing Account Name
  • Obtain Enrolment Account Name
  • Create a new RBAC enabled SP
  • Set the SP Role to allow creation of Azure Subscriptions
links

v bcxbxc

Now we know we have the correct access to proceed, we can create just the service principal account - quite simply as an unassigned definition at first - we can set it up properly at a later step

azcli
az ad sp create-for-rbac -n "SubVend" --role contributor --scope /subscriptions/{SubID}
terraform
variable "service_principal_display_name" { default = ""}
variable "service_principal_owner" { }
variable "inital_role_assignments" { }

data "azuread_user" "owner" { user_principal_name = local.service_principal_owner }

# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application#:~:text=Resource%3A%20azuread_application
resource "azuread_application" "sp" {

  display_name = local.service_principal_display_name
  owners       = [data.azuread_user.owner.object_id]
}

# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal#:~:text=Resource%3A%20azuread_service_principal
resource "azuread_service_principal" "sp" {

  application_id               = azuread_application.sp.application_id
  app_role_assignment_required = false
  owners                       = [data.azuread_user.owner.object_id]

  depends_on = [ azuread_application.sp ]
}

EA Portal Role Assignment :role:

  • Verified your permissions to setup SP
  • Verified your permissions to assign EA roles
  • Obtain Billing Account Name
  • Obtain Enrolment Account Name
  • Create a new RBAC enabled SP
  • Set the SP Role to allow creation of Azure Subscriptions
  • Obtain Enrolment Account roles available to assign
  • Assign Enrolment Account roles to SP

The final preparation stage - assigning the correct roles from our assigned Billing/Enrolment account to the Service Principal so that they can create subscriptions…

azcli

terraform
data "http" "get_enrolment_roles" {
  url    = "GET https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts/{enrollmentAccountName}/billingRoleDefinitions?api-version=2019-10-01-preview"
  method = "GET"

 request_headers = {
    Authorization = "Bearer <token removed>"
  }
}

output "enrolment_roles" {
value = jsondecode(data.http.get_billing.response_body).value
}
 + enrolment_accounts = [
      + {
          + id         = "/providers/Microsoft.Billing/billingAccounts/87561154/enrollmentAccounts/283828/billingRoleDefinitions/c15c22c0-9faf-424c-9b7e-bd91c06a240b"
          + name       = "c15c22c0-9faf-424c-9b7e-bd91c06a240b"
          + properties = {
              + description = "The account owner role gives the user read/write permissions to an Enrollment Account."
              + permissions = [
                  + {
                      + actions = [
                          + "Microsoft.Billing/billingAccounts/enrollmentAccounts/read",
                          + "Microsoft.Billing/billingAccounts/enrollmentAccounts/write",
                          + "Microsoft.Billing/billingAccounts/enrollmentAccounts/billingSubscriptions/read",
                          + "Microsoft.Billing/billingAccounts/enrollmentAccounts/billingSubscriptions/write",
                          + "Microsoft.Subscription/subscriptions/write",
                        ]
                    },
                ]
              + roleName    = "Enrollment account owner"
            }
          + type       = "Microsoft.Billing/billingAccounts/enrollmentAccounts/billingRoleDefinitions"
        },
      + {
          + id         = "/providers/Microsoft.Billing/billingAccounts/87561154/enrollmentAccounts/283828/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71"
          + name       = "a0bcee42-bf30-4d1b-926a-48d21664ef71"
          + properties = {
              + description = "The enrollent account subscription creator role gives the user permissions to create a subscription under the enrollment account."
              + permissions = [
                  + {
                      + actions = [
                          + "Microsoft.Billing/billingAccounts/enrollmentAccounts/read",
                          + "Microsoft.Subscription/subscriptions/write",
                        ]
                    },
                ]
              + roleName    = "Enrollment account subscription creator"
            }
          + type       = "Microsoft.Billing/billingAccounts/enrollmentAccounts/billingRoleDefinitions"
        },
    ]

data "http" "set_role" {
  url    = "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts/{enrollmentAccountName}/billingRoleDefinitions?api-version=2019-10-01-preview"
  method = "POST"

 request_headers = {
    Authorization = "Bearer <token removed>"
  }
  
  request_body = {
  "properties": {
    "principalId": "99a1a759-30dd-42c2-828c-db398826bb67",
    "principalTenantId": "7ca289b9-c32d-4f01-8566-7ff93261d76f",
    "roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/87561154/enrollmentAccounts/225314/billingRoleDefinitions/c15c22c0-9faf-424c-9b7e-bd91c06a240b"
  }
}
}
REST API
GET https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts/{enrollmentAccountName}/billingRoleDefinitions?api-version=2019-10-01-preview
PUT https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{billingAccountName}/enrollmentAccounts/{enrollmentAccountName}/billingRoleAssignments/{billingRoleAssignmentName}?api-version=2019-10-01-preview

{
  "properties": {
    "principalId": "99a1a759-30dd-42c2-828c-db398826bb67",
    "principalTenantId": "7ca289b9-c32d-4f01-8566-7ff93261d76f",
    "roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/7898901/enrollmentAccounts/283828/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71"
  }
}

Creation of Azure Subscriptions in an Organisation - if you have the correct permissions - is REALLY EASY.. and can get out of control if you start like many companies do with wide access and manual process.

It’s important to think about the Organisation setup - how you will be billing - cross charging and managing the subscriptions and setting up Azure Blueprints, Policies and LifeCycle Management for resources

Consider

  • Automated vending of subscriptions under specific, organisation-model structures for billing

  • Categorisation and assignment of defaults and policies based on how the subscription will be used - and aligning across the org

  • Sensible Policy restrictions on resource types - and grow as the requests come in

  • Set default budget alerts for the Owners - and over-spend notifications to a wider audience such as Cost Code Owner

  • Set Anti-patterns for usage and spending.. if subscriptions are being left doing nothing we should housekeep

  • Consider whether certain resources can be shared services - in dev you want to keep persistent resources going but ensure your IaC deployments are up only when needed

  • Look at auto-power off and expiry.. you can set a default 3 week expiry and delete expired resources - allowing users to over-ride with a tag…

Basic Setup

Create a subscription - no frills

providing the details for the Billing and Enrolment accounts, we request creation of a Subscription

  • just the billing details and permissions required

  • standard subscription created owned by the creating user/sp

  • Billing and Enrolment Details Required
  • Azure Subscription created
    • Owned by the creating user/SP
  •  
links

AZ CLI
az login --use-device-code
az extension add --name subscription
az billing enrollment-account list
az account create \ 
    --enrollment-account-name 
    --offer-type {MS-AZR-0017P, MS-AZR-0148P}
    --display-name
    --owner-object-id
    --owner-spn
    --owner-upn

Terraform
variable "billing_account" { default = "" }
variable "enrollment_account" { default = "" }
variable "owner" { default = "" }
data "azurerm_billing_enrollment_account_scope" "billing_account" {
  billing_account_name    = var.billing_account
  enrollment_account_name = var.enrollment_account
}

data "azurerm_subscription" "current" { }
data "azurerm_client_config" "current" { }
data "azuread_user" "owner" {
  user_principal_name = var.owner
}
resource "azurerm_subscription" "create" {
  
  subscription_name = local.subscription_display_name
  billing_scope_id  =  data.azurerm_billing_enrollment_account_scope.billing_account.id

  lifecycle {
      ignore_changes = [
        tags, alias
      ]
    }
}
REST
POST https://management.azure.com/providers/Microsoft.Billing/enrollmentAccounts/{enrollmentAccountName}/providers/Microsoft.Subscription/createSubscription?api-version=2018-03-01-preview

Standard Setup

Create subscription and deliver for use

  • Billing and Enrolment Details Required
  • Azure Subscription created
    • Owned by the creating user/SP
  • Add requesting user into subscription RBAC
  • Add subscription budget and alerting
links

Advanced Setup

Create Subscription & Deliver to Multiple Users
Align with ORG Management & Policy

  • Billing and Enrolment Details Required
  • Azure Subscription created
    • Owned by the creating user/SP
  • Add requesting user into subscription RBAC
  • Add subscription budget and alerting
  • Assign into an existing management group
  • Add additional Owner role assignments
  • Add additional Contributor role assignments
  • Add additional Reader role assignments

TL/DR; !RTFM

Align with ORG Management & Policy

    _                         ____ _     ___ 
   / \    _____   _ _ __ ___ / ___| |   |_ _|
  / _ \  |_  / | | | '__/ _ \ |   | |    | | 
 / ___ \  / /| |_| | | |  __/ |___| |___ | | 
/_/   \_\/___|\__,_|_|  \___|\____|_____|___|
 _____                    __                      
|_   _|__ _ __ _ __ __ _ / _| ___  _ __ _ __ ___  
  | |/ _ \ '__| '__/ _` | |_ / _ \| '__| '_ ` _ \ 
  | |  __/ |  | | | (_| |  _| (_) | |  | | | | | |
  |_|\___|_|  |_|  \__,_|_|  \___/|_|  |_| |_| |_|
    _    ____ ___      __  ____  ____  
   / \  |  _ \_ _|    / / |  _ \/ ___| 
  / _ \ | |_) | |    / /  | |_) \___ \ 
 / ___ \|  __/| |   / /   |  __/ ___) |
/_/   \_\_|  |___| /_/    |_|   |____/ 

Check the :gh: Repository for complete scripts for the steps above and some examples

Check the :gh: Repository for basic to advanced terraform configurations - going from a simple run-local deployment up to remote state, service principal creation, subscription creation and integration plus some enhanced notifications options to ensure new subscription creation visibility

:terraform: azurerm/azurerm_ea_subscription

Check the :gh: and (blue star) for using the REST APIs - either calling directly or scripted