Tuesday, May 24, 2022

A review of CSP Programs Users, Roles, Groups and how they relate to Azure AD and customers CSP subscriptions

One of the items I had on my to-do list was to create material that I could use to walk my colleagues through how our CSP tenant relates to our customers’ tenants and one of examples I wanted to include in the material was how to grant our CSP foreign principal permissions to a customer’s subscription as describe in my previous post:

Granting a CSP Foreign Principal the Reader or Owner role onto an Azure Subscription with PowerShell

What I quickly noticed while testing the script was that it no longer worked today (May 2022) because the DisplayName for the CSP foreign principal provided by the output was now blank. What this means is that my script, which looks for a entries where the DisplayName matches Foreign Principal*, will now return zero records:


*Note that the warning ( is referring to a problem where service principals are not returned by Get-AzAdGroupMember and does not appear to affect the role assignments we’re looking for.

If I remove the filter, it will return the CSP foreign principal with a blank DisplayName as shown in the following screenshot:


For reference, here is a screenshot of the foreign principal as displayed as a Azure RBAC role in the Access control (IAM) blade:


Here is a screenshot in the Microsoft 365 admin center portal of the foreign principal:


I’ve checked three tenants to confirm this is the same across them but since I’m not sure if this is temporary, I will leave the previous post as is with the script unchanged and will provide the updated set of cmdlets in this post that will be focused on discussing the roles from the CSP tenant, how it maps to the tenant’s Azure AD, and how they are used to grant permissions to a customer’s tenant.

Before I begin, the following post provides great information about the CSP identity and rights management even though it is very old: I highly encourage anyone learning about the CSP program to go through the blog entry.

The Microsoft Partner Center Portal

Those who have worked at a Microsoft partner would be familiar with the partner portal located at: where they can sign in by clicking on the Partner Center link:


Microsoft’s CSP program currently supports three main types of transactional relationships:

  • Indirect providers
  • Indirect resellers
  • Direct-bill partners

More information can be found at the following Microsoft documentation:

Depending on the type of relationship you’ll be presented with different navigation menus depending on the type of partner and membership (left is the Direct-bill while the right is a Indirect reseller):


For the purpose of this post, we will focus on the Direct-bill partners (Tier 1) who are able to directly provision Azure CSP subscriptions to their customers and are required to use their identity (CSP Provider) to open tickets because customers would no longer be able to from within

CSP Program User, Roles, Groups and how they relate to Azure AD

The following is a diagram I mapped out of how the CSP program users and roles relate to the Azure AD:


Let’s break down the diagram by mapping the various components out in the Partner Center.

Navigating in the portal to User Management, we are able to create accounts and assign predefined roles:


These accounts in the Microsoft Partner Center are user accounts in the CSP Azure AD tenant:


The type of roles and groups we are able to assign accounts are listed under the drop down list Manages your organization’s account as, while the groups we can add the accounts to are listed under Assist your customer as:


The Business Profile admin and Manages your organization’s referrals provide these roles:


Detailed information about these roles can be found in the following Microsoft documentation:

Azure AD tenant roles and non-Azure AD roles

To summarize, some of these roles and groups are mapped to the Azure AD tenant while the others are not.

Azure AD Tenant Roles

The following is a mapping between the roles listed under: Manages your organization’s account as and the Azure AD tenant roles:

Microsoft Partner Central Role

Azure AD Role

Global admin

Global administrator

Billing admin

Billing administrator

User management admin

User administrator



Assigning a user in the Microsoft Partner Center the role of a Global admin will place this identity that lives in Azure AD into the Global administrator role. This is the same for Billing admin > Billing administrator and User management admin > User administrator.


Azure AD Tenant Groups

The roles that are provided under Assist your customer as are mapped as these Azure AD groups:

Microsoft Partner Central Role

Azure AD Groups

Admin Agent


Sales Agent


Helpdesk Agents




Assigning a user in the Microsoft Partner Center the role of a Admin Agent will place this identity that lives in Azure AD into the AdminAgents Azure AD group. This is the same for Sales Agents > SalesAgents and Helpdesk Agents > HelpdeskAgents.


Non-Azure AD Tenant Roles

The remaining list of roles are non-Azure AD tenant roles:

  • business profile admin
  • referral admin
  • incentive admin
  • incentive user
  • MPN (Microsoft Partner Network) partner admin

More information about these non-Azure AD tenant roles:


CSP Admin Agent, Sales Agent and Helpdesk Agent Azure AD Groups to Customer Subscription Azure RBAC Roles Mappings

One of the most important mappings that should be understood is how the CSP Azure AD groups are mapped to the customers’ subscriptions as Foreign Principal Azure RBAC roles. As described earlier, the following two groups assigned within Partner Center:

  1. Admin Agent
  2. Sales Agent
  3. Helpdesk Agent

… are mapped to the CSP tenant’s Azure AD group:

  1. AdminAgent
  2. SalesAgent
  3. HelpDeskAgent

These CSP tenant Azure AD groups can then be granted Azure RBAC roles to the customer’s subscriptions as foreign identities as shown in the screenshot below:

  1. TenantAdmins
  2. SalesAdmins
  3. HelpdeskAdmins

Assigning the CSP tenant’s Azure AD Groups to customers’ subscriptions can only be performed through Azure CLI or PowerShell and cannot be performed through the GUI. To demonstrate this process, I will use the scenario for granting Helpdesk Admin role permissions to open tickets.

The diagram and the beginning of this walkthrough outlines how the foreign principal mapping are assigned and note that the foreign principals in the diagram can be granted any Azure RBAC rules on the subscription:


Granting CSP Provider Accounts Permissions to Open Tickets

Let’s take the scenario where a Microsoft CSP partner wants to set up an a group of support representatives who simply opens up an Azure support ticket with Microsoft when requested by the customer. These representatives do not need elevated permissions such as Owner, which is automatically granted to the Admin Agents role when a CSP subscription is created, on the subscriptions as they should not have the ability to perform any changes to the subscription and resources. For this scenario, we use the Helpdesk Agent role that is mapped to the HelpdeskAgent Azure AD group, to assign the Support Request Contributor Azure RBAC role onto the customer’s subscription. The following diagram depicts the assignments and how the identities are mapped:


As mentioned earlier, it is not possible to simply sign in as a Owner on the desired customer CSP subscription, navigate to the Access control (IAM) blade, then assign the foreign principal as a Support Request Contributor as shown in the screenshot below because Foreign Principals are not presented from within the GUI:



The following are instructions on how to assign the HelpdeskAgents Azure AD group in the CSP Azure AD tenant onto a customer’s subscription as a Azure RBAC Support Request Contributor.

Begin by obtaining the object ID of the HelpdeskAgents Azure AD group in the CSP Azure AD tenant by either navigating to the groups blade:


Or alternatively, use the PowerShell cmdlet Get-AzADGroup to list the Object ID:

Connect-AzAccount ### Log in with CSP partner credentials
Get-AzADGroup | Select-Object DisplayName,Id


With the HelpdeskAgents Azure AD group ObjectId, proceed to use the following PowerShell New-AzRoleAssignment cmdlet to assign the Azure RBAC role to the desired subscription:

Connect-AzAccount ### Log in with customer global admin credentials

$foreignPrincipalObjectID = "<Azure AD Group Object ID>"

$subscriptionID = "/subscriptions/<Subscription ID>"

New-AzRoleAssignment -ObjectId $foreignPrincipalObjectID -Scope $subscriptionID -RoleDefinitionName "Support Request Contributor" -ObjectType "ForeignGroup"


With the above cmdlet executed, the HelpdeskAdmin foreign principal identity will be displayed as a Support Request Contributor on the subscription and users in this group able to open support tickets:


Note that you do not need to grant additional permissions such as reader to the foreign principal if the users only need to open tickets. It is a common misconception that the users need Reader access to the subscription because prior to graning the Support Request Contributor role, trying to open a new service request will display the following:


Restricted Tenant

You do not have access to any subscriptions or resources in this tenant. Click ‘I acknowledge’ to continue or ‘Sign out’ to sign out of this tenant.


It is not necessary to grant Reader and Support Request Contributor to the subscription.

Granting CSP Provider Accounts Permissions to Read Subscription

Another potential scenario is if the CSP partner would like to provide support where representatives will only view and provide guidance for troubleshooting without making any changes. Assuming we want to use the HelpdeskAgents Azure AD Group, we can use the same cmdlet as shown above to assign the Reader Azure RBAC role to the subscription. Providing this permission will allow the users in this group to view all the resources in the subscription (there are some restrictions such as various configuration parameters in app services) but unable to make any changes such as provisioning, editing, or deleting.


It is also worth noting that having Reader access to a subscription does not permit the user to open support tickets as an attempt to do so will display the following message during the ticket creation process:

You don’t have permission to create a support request

To get permission, ask your subscription administrator or owner to assign you ‘Support Request Contributor’ role for the selected subscription.



I hope this post helps anyone who may be trying to learn more about how being a CSP can manage their customer’s tenant. The design isn’t overly complex but requires a bit of time to dissect the components and understand how they all interact with each other.

Friday, May 13, 2022

Let's learn IaC using Terraform with GitHub Actions to deploy into Microsoft Azure

It has been a busy start to the year and I regret that I haven’t been able to dedicate more time to blogging so I intend on catching up on my backlog in the following months. One of the topics I’ve been meaning to write about is how to use Terraform with GitHub actions to deploy infrastructure in Azure. Both Terraform (IaC) and GitHub Actions (orchestration engine) are technologies I’ve been self-training over the past few months and I am excited to continue building the knowledge I’ve acquired. Those who may not be familiar with IaC can refer to one of my previous posts here:

Infrastructure as Code in 15 Minutes PowerPoint Presentation

My journey through learning these two technologies has been challenging but very fulfilling, and the purpose of this post is to share the various features I came across and what I’ve been able put together to demo all of them. I have to admit that I am not an expert and there may be better approaches so please feel free to comment on this post.

A few of my colleagues have indicated that they feel it would be beneficial to include more diagrams in my posts rather than just writing so I have taken the time to create a series of diagrams to better illustrate the workflow and the Terraform code that used.

What I’m trying to Demo

The components and features I’m trying to demo are the following:

  1. How to use Terraform for IaC
  2. How to execute terraform init, format, validate, plan, apply, destroy in a workflow
  3. How to use different .tfvars variables to deploy different environments: dev, stg, prod
  4. How to use GitHub Actions as an orchestration engine for pipelines
  5. How to initiate a workflow manually, on push, on pull request, on complete of another workflow
  6. How to have the workflow store the terrafirn .tfstate file to an Azure Storage Account Container
  7. How to set a dependency on a previous step
  8. How to use and reference custom self-written Terraform modules stored in a different registry and in subfolders (some may not know about the // when accessing a module in a sub directory)
  9. How to use and reference a Terraform Registry module
  10. How to use the Super-Linter to scan the code
  11. How to get a branch name
  12. How to pass a branch name a different step
  13. How to store and use secrets in GitHub
  14. How to configure different environments in GitHub
  15. How to configure a protection rule for a GitHub environment
  16. How to require an approval before executing a workflow
  17. How to configure an Azure Storage Account Container to store the .tfstate file

GitHub Repositories

Let me begin by providing the links to the GitHub repositories I will be using for the demonstration.

GitHub Repository that contains the GitHub Actions workflows, Terraform code for deploying dev, stg, and production environments:

GitHub Repository that contains the Terraform modules that are referenced and used for the deployment of Azure Kubernetes Service, Azure Container Registry, and PostgreSQL server and database:

I’ve added as many comments into the code to explain the purpose in hopes that whoever is reading it will understand the function. Feel free to fork them to your repo and test or modify as you see fit.

GitHub Repository Branches

There will be 3 branches in the GitHub repo:

  • Dev
  • Stg
  • Prod

The Terraform code and workflows will be directly pushed to the dev branch to test, then merged into stg and production.

Terraform and GitHub Actions Code

The GitHub Actions YAML files will be stored in the mandatory .github/workflows directory of the repository.

The Terraform code will be split as follows.

terraform-k8s-acr-psql-vms-demo repository

  • The,,, and files are stored in the root
  • The .tfvars files containing the variable values for dev, stg, and prod environments are stored in the subfolder Variables
  • The references modules that are stored outside of its repository:
    • Another GitHub public repository named terraform-modules
    • A Terraform Registry module

terraform-modules repository

  • This repository contains 3 modules that are used to deploy:
    • Azure Container Registry
    • Azure Kubernetes Service
    • PostgreSQL Server and Database

What we are deploying with Terraform

The resources that will be deployed are the following:

  1. Azure Container Registry
  2. Azure Kubernetes Service
  3. PostgreSQL Server and Database
  4. Linux Virtual Machine
  5. Windows Virtual Machine
  6. VNet with subnets
  7. Management lock for the Azure Container Registry

Workflow: terraform-plan.yml and terraform-apply-dev.yml

The terraform-plan.yml and terraform-apply-dev.yml workflows are dispatched whenever there is a push to the dev branch in the GitHub repo. I have included a diagram below that walks through the process and will also list the flow here:

  1. User updates Terraform or GitHub Action YAML code and pushes it to the dev branch of the GitHub repo
  2. The terraform-plan.yml workflow is started as it is configured to start on push to dev
  3. Two steps are now executed in parallel:
    1. Get-Branch-Name to determine what branch this was pushed on
    2. The download and use of the Super-Linter is ran in parallel to scan the code
  4. Several Terraform commands are executed:
    1. fmt is ran on the Terraform code to ensure it is formatted properly
    2. validate is ran on the Terraform code
    3. init is ran to initialize and configure an Azure Storage Account Container to store the .tfstate file
    4. plan is ran to generate a plan with the appropriate terraform-dev.tfvars file
  5. Once the terraform-plan.yml workflow is complete, initiate the terraform-apply-dev.yml workflow
  6. Get-Branch-Name will start to obtain the previous workflow run conclusion
  7. If previous workflow was not successful then end the workflow, if it was successful then get the branch path that is currently being worked on
  8. If the branch path is not dev then end the workflow, if it is the dev branch then set the environment to GitHub dev-deploy and go to the next step
  9. The dev-deploy GitHub environment has a protection rule configured that requires review and approval so the reviewer will receive an email to approve or reject
  10. If the reviewer has approved then proceed with the deploy where the following are executed:
    1. fmt is ran on the Terraform code to ensure it is formatted properly
    2. validate is ran on the Terraform code
    3. init is ran to initialize and configure an Azure Storage Account Container to store the .tfstate file
    4. plan is ran to generate a plan with the appropriate terraform-dev.tfvars file and the -out switch is used to create the plan.tfdata file
    5. apply with -auto-approve using the plan.tfdata file is executed
  11. Resources will not get deployed to Azure

The following is a screenshot of the jobs in the workflows and the process during the deployment:


What a pending review looks like:


The email a reviewer would receive:


The review prompt in GitHub:


The apply output when deploying infrastructure:


A successfully deployment (note that the duration of 20h 13m 31s is because I left the review pending over a day):


Workflow: terraform-apply.yml

The terraform-apply.yml workflow is executed upon completing a pull request for stg and prod. It is much less complex so I will simply include the two diagrams to describe the process:





Workflow: terraform-destroy.yml

The terraform-destroy.yml workflow is dispatched manually when we want to remove the environment. The following are a few screenshots of manually dispatching the workflow:


The output during a destroy of the infrastructure:


Successfully destroying the infrastructure:


Setting up Azure Storage Account Container and Resource Group

With the walkthrough of the Terraform and GitHub Actions completed, I would like to provide the steps required to set up the Azure Storage Account Container that will be used to store the terraform .tfstate file as none of this would work without it.

We’ll be using the Azure CLI to configure this:

# Log into Azure

az login



# Define variables for subscription ID, Azure Region, storage account, container

subscriptionID = "xxxxxxxx-71c2-40f2-b3d4-xxxxxxxxxx"

resourceGroup = "ca-cn-dev-demo-rg"

azureRegion = "canadacentral"

storageAccount = "cacndevdemost"

containerName = "terraform-state"


# List available subscriptions

az account list


# Specify the subscription to use

az account set -s $subscriptionID

# Create a App Registration and corresponding Enterprise Application / Service Principal and assign it contributor role to the subscription – Ref:

az ad sp create-for-rbac --name $servicePrincipalName --role Contributor --scopes /subscriptions/$subscriptionID --sdk-auth

Copy the clientId, clientSecret, tenantId values.


Note that the following App Registration will be configured along with a secret in Azure AD:


The corresponding Enterprise application (Service Principal) will be created:


We’ll need to grant permissions to the Service Principal to the subscription that Terraform will deploy resources to. Contributor typically sufficient but there are some configurations such as Resource Locks that require Owner:


# Create resource group that will store storage account for saving the Terraform State

az group create -g $resourceGroup -l $azureRegion


# Create a new storage account and place it in the resource group

az storage account create -n $storageAccount -g $resourceGroup -l $azureRegion --sku Standard_LRS


The following storage account will be created:


# Create a container in the storage account to store the terraform state

az storage container create -n $containerName --account-name $storageAccount


The following Container will be created and when used a .tfstate will be stored here:


Setting up GitHub Secrets

Various parameters such as service principal attributes, secrets, storage account access keys should not be stored directly in the Terraform .tfvars files and should be stored in the GitHub secrets vault for retrieval.

Proceed to navigate to the previously configured Storage Account’s Access Keys and copy the key1 as we’ll need to configure it in GitHub secrets:


For the purpose of this example, the dev environment will require the following secrets configured as they are referenced in the workflows and terraform code:


Note that you will not be able to view the values of these secrets once they are configured in GitHub.

In addition to the DEV secrets, the STG and PROD secrets will also need to be configured for the other branches.


Setting up GitHub Environments

The last requirement for this demo is to set up the different environments in GitHub for the branches. It’s important to note that Environments are NOT available in private repositories for free accounts so you’ll need to use a public repo for it. This demo has the following environments configured:

  • dev-deploy
  • prod
  • dev
  • stg

The additional dev-deploy environment is really just a way for me to execute the plan step to verify the code is free of errors and then requiring a review and approve to initiate the deployment of the resources. This method likely isn’t best practice but I thought I’d use this to demonstrate how to set the environment in the workflow to force a review or reject.


With the environments setup, the dev-deploy is configured with the Required reviewers protection rule:


… and that’s it. I hope this was beneficial for anyone who may be trying to learn Terraform and GitHub actions. There are plenty of blog posts available but I’ve noticed that some were not very clear on the steps and I’ve spent countless hours troubleshooting the code from start to finish. The process can be very frustrating at times but it’s also very satisfying when everything starts to work.

I’ll be working on another new project to incorporate an actual application in the future and will be sharing it.