Leveraging Logic Apps to prevent over-provisioning owner access to subscriptions

unosd

Stefano d'Antonio

Posted on November 11, 2021

Leveraging Logic Apps to prevent over-provisioning owner access to subscriptions

Often happens that agility and freedom conflict with security.

(Aaron Paul voice) Has this ever happened to you?

Have you ever had developer teams request ownership of a full subscription to be able to freely experiment?

You still want to keep isolation, segregate responsibilities and permissions.

Ability to experiment freely is paramount to innovation, but uncontrolled proliferation of subscriptions can bear a significant management overhead.

Can we have the best of both worlds? The short answer is: yes.

A resource group can be an effective boundary as it allows its contributors to yet create any resource, but also restrict the scope of access within a subscription.

You can also enforce tags and Azure policies to control costs and enforce security.

But now a team is restricted to creating resources within a single resource group and it can get messy quite quickly and permission-wise is not so granular within teams.

What if we could allow teams to create their own resource groups within a subscription with contributor access and not being able to read/write other resource groups?

We can quickly set up a Logic App to enable this. Orchestrating creation and role assignment of resource groups within a single workflow, this enables:

  • Invoking the Logic App manually through a REST API
  • Invoking from a DevOps pipeline to create resources as part of a dev/test automated environment

The Logic App can create a resource group and assign a certain contributor based on the input payload from the HTTP trigger.

Warning

Setting up this workflow could lead to a security loophole: What if someone uses the name of an existing resource group so the workflow grants access to other teams' resources? We need to make sure we address this concern when building our Logic App as, usually, Azure management operations are idempotent and the Logic App won't fail if we pass the name of an existing resource group.

The Logic App

Let's have a look at the flow:

Image description

The logic is pretty simple and most of the operations we require have a native connector; the only missing one is "Create role assignment", but we can easily perform the operation by invoking the Azure REST API and we do not have to worry about authentication as the managed identity will do this for us.

Now, you may have noticed that there is no condition stating: "If the group already exists, interrupt the flow"; but if you look at the picture above, you may notice a red dotted line between two operations, this is because we changed the Run after settings of our create operation:

Image description

Image description

As you can see, the operation of creation (and so the role assignment after) will only occur if the Read resource group failed, hence the group does not exist; this will block the loophole described above.

Now, we can prevent people from having broad access, but our Logic App's managed identity still requires owner permissions on the subscription; more secure, but we can do even better. Let's create a custom role that has only enough permissions to read/create a resource group and assign permissions to it:

Image description

We can now use the UI to create a custom role, but we may also want to script it and define it in JSON format:

Image description

Let's now assign that to the managed identity of our Logic App:

Image description

Now we should have all permissions in place. If you also want to use a security group, your Logic App identity may also require Directory.Read.All permissions on your Azure AD instance.

Creating role assignment

I mentioned above that all the other actions can be performed with native Logic App connectors, but the role assignment, at the time of writing, requires the HTTP connector to invoke the Azure REST API, let's have a look at that:

Image description

Even if we cannot do it in idiomatic Logic App, that is yet pretty simple.

This is the API documentation: https://docs.microsoft.com/en-us/rest/api/authorization/role-assignments/create

We just need to set the right method (PUT), the correct URL, using the resource group ID as scope from the output of the previous connector and we can auto-generate a random GUID as name for the assignment using Logic App expressions (guid()). The body must contain the role definition ID, which needs to be the built-in contributor GUID under our subscription ID, I have used a variable for that to improve clarity:

Image description

The subscription ID is one of the input of our workflow and the hardcoded GUID can be found here: Contributor

To get values as input from the workflow invocation, we need to set the input JSON schema in the HTTP trigger:

Image description

We need:

  • principalId The object ID of the assignee of the contributor role, this can be found looking up the user in Azure AD from the portal
  • resourceGroupLocation, resourceGroupName, subscriptionId Quite self-explanatory arguments
{
    "properties": {
        "principalId": {
            "type": "string"
        },
        "resourceGroupLocation": {
            "type": "string"
        },
        "resourceGroupName": {
            "type": "string"
        },
        "subscriptionId": {
            "type": "string"
        }
    },
    "type": "object"
}
Enter fullscreen mode Exit fullscreen mode

After adding the schema above to the trigger, those values will be available as variables in the rest of the workflow.

Testing

All there is left now is to test, let's run our workflow with the following input:

{
    "subscriptionId": "<Target subscription ID to create groups>",
    "resourceGroupName": "rg-test2",
    "resourceGroupLocation": "West Europe",
    "principalId": "<Object ID GUID of your user from AAD>"
}
Enter fullscreen mode Exit fullscreen mode

And this is what happens, assuming rg-test2 does not exist:

Image description

Looks good, all the steps we wanted to run were successful.

Now, let's try and run this again with the same inputs:

Image description

OK, as you can see from the grey circles next to the actions below Read a resource group, none of the other operations were performed, exactly as expected.

Now let's have a look at our newly created resource group IAM blade:

Image description

Exactly what we wanted; a new resource group of which I am contributor without requiring any permission on the subscription.

The full template for the Logic App is available on my GitHub so you can save the extra 5 minutes it took me to create it to enable this security feature for your teams.

You can also enhance security of the Logic App to prevent unauthorised users from calling it by fronting it with API Management or you can use Azure Active Directory Authorization Policies on the Logic App itself or a combination of the two.

💖 💪 🙅 🚩
unosd
Stefano d'Antonio

Posted on November 11, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related