How to manage resources in the external tenant in Azure?
Tags: cloud, azure, bicep, management
Published: 2021-04-19 11:52:18

# The case

You want to manage resources in other tenants without additional workload around that like accounts creation for you and your teammates in the destination Azure tenant or disabling those accounts if someone leaves the team. Is there any solution for that?

The answer is: yes! And it is called **Azure Lighthouse**.

From docs:

> Azure Lighthouse enables cross- and multi-tenant management, allowing for higher automation, scalability, and enhanced governance across resources and tenants. ~[What is Azure Lighthouse?]([https://docs.microsoft.com/en-us/azure/lighthouse/overview](https://docs.microsoft.com/en-us/azure/lighthouse/overview))

# How does it work?

Azure Lighthouse enables managing resources in another tenant directly from your 'home' tenant. You can define with that which users and/or Active Directory **Security** groups should have access to which resources and with which roles.

# Requirements

To set up the Azure Lighthouse solution, you have to fulfill some requirements and gather a bunch of information. The full list you can find here:

- Your tenant ID
- Customer's tenant ID
- List of subscription IDs and/or resource group IDs to which you have to have access
- List of users and/or Active Directory groups which you want to assign to manage customer environments - **IMPORTANT**: AD groups have to be **Security** groups!
- List of roles and permissions (with their IDs) which you want to assign
- Someone with non-guest account in customer's tenant and `Microsoft.Authorization/roleAssignments/write` permission - it is required to create assignment ([Deploy the Azure Resource Manager templates]([https://docs.microsoft.com/en-us/azure/lighthouse/how-to/onboard-customer#deploy-the-azure-resource-manager-templates](https://docs.microsoft.com/en-us/azure/lighthouse/how-to/onboard-customer#deploy-the-azure-resource-manager-templates)))
- Something called `mspOfferName` and `mspOfferDescription` - the first one defines the connection between both tenants and their 
resources. **IMPORTANT: You can't have multiple assignments at the same scope with the same `mspOfferName`.**

# How to onboard the customer?

If you gathered all of the above requirements, on [Github]([https://github.com/Azure/Azure-Lighthouse-samples](https://github.com/Azure/Azure-Lighthouse-samples)) you can find example ARM templates that allow you to onboard. You have to deploy prepared and fulfilled templates to each subscription separately. 

# Example

For preparing example I will use the template from the official [docs]([https://docs.microsoft.com/en-us/azure/lighthouse/how-to/onboard-customer](https://docs.microsoft.com/en-us/azure/lighthouse/how-to/onboard-customer)):

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "mspOfferName": {
            "type": "string",
            "metadata": {
                "description": "Specify a unique name for your offer"
            },
            "defaultValue": "<to be filled out by MSP> Specify a title for your offer"

        },
        "mspOfferDescription": {
            "type": "string",
            "metadata": {
                "description": "Name of the Managed Service Provider offering"
            },
            "defaultValue": "<to be filled out by MSP> Provide a brief description of your offer"
        },
        "managedByTenantId": {
            "type": "string",
            "metadata": {
                "description": "Specify the tenant id of the Managed Service Provider"
            },
            "defaultValue": "<to be filled out by MSP> Provide your tenant id"
        },
        "authorizations": {
            "type": "array",
            "metadata": {
                "description": "Specify an array of objects, containing tuples of Azure Active Directory principalId, a Azure roleDefinitionId, and an optional principalIdDisplayName. The roleDefinition specified is granted to the principalId in the provider's Active Directory and the principalIdDisplayName is visible to customers."
            }
        }              
    },
    "variables": {
        "mspRegistrationName": "[guid(parameters('mspOfferName'))]",
        "mspAssignmentName": "[guid(parameters('mspOfferName'))]"
    },
    "resources": [
        {
            "type": "Microsoft.ManagedServices/registrationDefinitions",
            "apiVersion": "2019-09-01",
            "name": "[variables('mspRegistrationName')]",
            "properties": {
                "registrationDefinitionName": "[parameters('mspOfferName')]",
                "description": "[parameters('mspOfferDescription')]",
                "managedByTenantId": "[parameters('managedByTenantId')]",
                "authorizations": "[parameters('authorizations')]"
            }
        },
        {
            "type": "Microsoft.ManagedServices/registrationAssignments",
            "apiVersion": "2019-09-01",
            "name": "[variables('mspAssignmentName')]",
            "dependsOn": [
                "[resourceId('Microsoft.ManagedServices/registrationDefinitions/', variables('mspRegistrationName'))]"
            ],
            "properties": {
                "registrationDefinitionId": "[resourceId('Microsoft.ManagedServices/registrationDefinitions/', variables('mspRegistrationName'))]"
            }
        }
    ]
}
```
with parameters file:
```json
{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "mspOfferName": {
            "value": "Test PW Lighthouse"
        },
        "mspOfferDescription": {
            "value": "Test PW Lighthouse"
        },
        "managedByTenantId": {
            "value": "11111111-1111-1111-1111-111111111111"
        },
        "authorizations": {
            "value": [
                {
                    "principalId": "22222222-2222-2222-2222-222222222222",
                    "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c",
                    "principalIdDisplayName": "PW MC"
                }
            ]
        }
    }
}
```
I translated the above template to Bicep - it's much easier to read and edit for me:
```bash
@description('Specify a unique name for your offer')
param mspOfferName string

@description('Name of the Managed Service Provider offering')
param mspOfferDescription string

@description('Specify the tenant id of the Managed Service Provider')
param managedByTenantId string

@description('Specify an array of objects, containing tuples of Azure Active Directory principalId, a Azure roleDefinitionId, and an optional principalIdDisplayName. The roleDefinition specified is granted to the principalId in the provider`s Active Directory and the principalIdDisplayName is visible to customers.')
param authorizations array

var mspRegistrationName = guid(mspOfferName)
var mspAssignmentName = guid(mspOfferName)

targetScope = 'subscription'

resource regDefinintion 'Microsoft.ManagedServices/registrationDefinitions@2019-09-01' = {
  name: mspRegistrationName
  properties: {
    registrationDefinitionName: mspOfferName
    description: mspOfferDescription
    managedByTenantId: managedByTenantId
    authorizations: authorizations
  }
}

resource regAssignment 'Microsoft.ManagedServices/registrationAssignments@2019-09-01' = {
  name: mspAssignmentName
  properties: {
    registrationDefinitionId: regDefinintion.id
  }
}
```

The Bicep file can be used with the same parameters file as the classic ARM template.

For test purposes, I used two different Azure tenants. I 'home' tenant I created AD Security group called '**PW MC**'. I wanted to give to chosen subscription the Contributor rights for '**PW MC**' AD group, so I deployed the above template with the Azure CLI:
```bash
az deployment sub create --location WestEurope --template-file ./lighthouse.bicep --parameters lighthouse.parameters.json
```
Previously I logged into the tenant with `az login --tenant` command.

# Effect
The effect should be visible in two places, in both tenants. In the customer tenant when we will go to the **Service providers** page, we will able to see something like on the screenshot below:

![Service providers in Customer's tenant](https://wachulec.me/images/posts/2021-04-19-how-to-manage-resources-in-the-external-tenant-in-azure/customer-lighthouse.png)

and in the 'home' tenant when we will visit the **My customers** page:

![My customers page in home tenant](https://wachulec.me/images/posts/2021-04-19-how-to-manage-resources-in-the-external-tenant-in-azure/service-lighthouse.png)

If we click on 'Default directory':

![Subscription details](https://wachulec.me/images/posts/2021-04-19-how-to-manage-resources-in-the-external-tenant-in-azure/service-lighthouse-2.png)

Then you can click on the subscription name and do whatever you want with the resources.

# Summary

You can see that implementing Azure Lighthouse is quite easy and reduce management concerns. And what is the most important - it is free!

Original post you can find on [my website](https://wachulec.me/posts/how-to-manage-resources-in-the-external-tenant-in-azure/).