APIM and Azure OpenAI Service – Azure AD

Hello folks!

I’m back with another entry on the Azure OpenAI Service (AOAI). In my previous posts, I’ve focused on the native security features that Microsoft provides to its customers to secure their instance of the service. However, in this post, I’ll be taking a slightly different approach. I’ll be walking you through a pattern that can be used to supplement those native features using Azure API Management (APIM)

For those who are unfamiliar with APIM, it is Azure’s API Gateway PaaS (platform-as-a-service) offering. Like any good API Gateway, it provides an abstraction layer away from backend APIs, which allows you to add additional authentication/authorization controls, throttling, transform requests, and log information from the requests and responses. In this post, I’ll be covering how the authentication/authorization controls can be used to supplement what is provided natively in AOAI. 

I’ve covered authentication in the AOAI in a previous post, refer to that post for the gory details. For the purposes of this post, you need to understand at the data plane it supports both Azure AD authentication/Azure RBAC authorization and authentication with two API keys created when the service is instantiated.

Azure OpenAI Service Authentication and Authorization

To my knowledge, there is no way to disable the usage of API keys. Moreover, as I’ve discussed in my logging post, it is extremely difficult to trace back to what is using the API keys because the source IP address is masked and the calls aren’t associated with specific API keys or Azure AD identities. This makes it critically important to control who has access to the API keys. In my post on authorization within the service, I cover this conversation in more detail, and yes, it can be done with Azure RBAC.

Sample log entry from Azure Open AI Service


Controlling access should be your first priority. However, wouldn’t it be great to restrict access to the service to Azure AD authentication only? This is where APIM comes in. APIM is placed between the application calling the AOAI service and the AOAI service. This establishes a man-in-the-middle scenario where APIM can analyze and modify the request and responses between the application and AOAI service.

APIM and AOAI Data Flow

The image above is an example of this pattern. Here, the calling application is provisioned with either a service principal (running outside of Azure) or a managed identity (running within Azure or integrated with Azure Arc). Instead of pointing the application directly to the Azure OpenAI Service, it is pointed to a custom domain configured on the APIM instance, and the APIM instance is configured to front the Azure OpenAI Service API. My peer Jake Wang put together some wonderful instructions on how to set this piece up in this repository.

Once APIM is set up to pass traffic along to the AOAI service, a custom APIM policy can be introduced to start controlling access. Since the goal is to limit access to the AOAI service to applications using an Azure AD identity, the validate-jwt policy can be used. This policy captures and extracts the JSON Web Token (bearer token) and parses the content within it to verify that the token was issued by the issuer specified in the policy. 

The policy would be structured as shown below. In this policy, any request made to the API must include a JWT issued by the Azure AD tenant (you can find your tenant ID here). Additionally, the policy filters to ensure that the token is intended for the Cognitive Services OAuth scope, which AOAI falls under. If the request doesn’t include the JWT issued by the tenant, the user receives a 403.

<!--
    This sample policy enforces Azure AD authentication and authorization to the Azure OpenAI Service. 
    It limits the authorization tokens issued by the organization's tenant for Cognitive Services.
    The authorization token is passed on to the Azure OpenAI Service ensuring authorization to the actions within
    the service are limited to the permissions defined in Azure RBAC.

    You must provide values for the AZURE_OAI_SERVICE_NAME and TENANT_ID parameters.
-->
<policies>
    <inbound>
        <base />
        <set-backend-service base-url="https://{{AZURE_OAI_SERVICE_NAME}}.openai.azure.com/openai" />
        <validate-jwt header-name="Authorization" failed-validation-httpcode="403" failed-validation-error-message="Forbidden">
            <openid-config url="https://login.microsoftonline.com/{{TENANT_ID}}/v2.0/.well-known/openid-configuration" />
            <issuers>
                <issuer>https://sts.windows.net/{{TENANT_ID}}/</issuer>
            </issuers>
            <required-claims>
                <claim name="aud">
                    <value>https://cognitiveservices.azure.com</value>
                </claim>
            </required-claims>
        </validate-jwt>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

If you followed the instructions in the repository I linked above, you can enforce this policy for the API you created as seen below.

APIM Policy In Place

Once the policy is in place, you can test it by attempting to authenticate to the APIM API endpoint and specifying an AOAI API key. In the image below, an attempt is made to call the endpoint with an API key.

APIM Denying Request with API Keys

Success! Even though the API key is valid, APIM is rejecting the request before it ever reaches the AOAI instance, preventing the API keys from being used. 

This pattern also passes the bearer token on to the AOAI service, so the RBAC you configure on your AOAI instance will be enforced. In my post on authorization, I provide some guidance on which built-in RBAC roles make since and which permissions you’ll want to carefully distribute.

What’s even cooler is that now that the application is forced to authenticate using Azure AD, the application ID can be extracted. If there are multiple applications hitting the same AOAI instance, different throttling can be applied on a per-application basis instead of having them share one big pool of request/token allowance at the AOAI service level

This can be achieved with a policy similar to the one shown below. This policy looks for specific app IDs in the bearer token and applies different throttling based on the application.

<!--
    This sample policy enforces Azure AD authentication and authorization to the Azure OpenAI Service. 
    It limits the authorization tokens issued by the organization's tenant for Cognitive Services.
    The authorization token is passed on to the Azure OpenAI Service ensuring authorization to the actions within
    the service are limited to the permissions defined in Azure RBAC.

    The sample policy also sets different throttling limits per application id. This is useful when an organization
    has multiple applications consuming the same instance of the Azure OpenAI Service. This sample shows throttling
    rules for two separate applications.

    You must provide values for the AZURE_OAI_SERVICE_NAME, TENANT_ID, and CLIENT_ID_APP parameters. You can add multiple
    lines for as many applications as you need to throttle.
-->
<policies>
    <inbound>
        <base />
        <set-backend-service base-url="https://{{AZURE_OAI_SERVICE_NAME}}.openai.azure.com/openai" />
        <validate-jwt header-name="Authorization" failed-validation-httpcode="403" failed-validation-error-message="Forbidden">
            <openid-config url="https://login.microsoftonline.com/{{TENANT_ID}}/v2.0/.well-known/openid-configuration" />
            <issuers>
                <issuer>https://sts.windows.net/{{TENANT_ID}}/</issuer>
            </issuers>
            <required-claims>
                <claim name="aud">
                    <value>https://cognitiveservices.azure.com</value>
                </claim>
            </required-claims>
        </validate-jwt>
        <choose>
            <when condition="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt().Claims.GetValueOrDefault("appid", string.Empty).Equals("{{CLIENT_ID_APP1}}"))">
                <rate-limit-by-key calls="1" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt().Claims.GetValueOrDefault("appid", string.Empty))" increment-condition="@(context.Response.StatusCode == 200)" />
            </when>
        </choose>
        <choose>
            <when condition="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt().Claims.GetValueOrDefault("appid", string.Empty).Equals("{{CLIENT_ID_APP2}}"))">
                <rate-limit-by-key calls="10" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt().Claims.GetValueOrDefault("appid", string.Empty))" increment-condition="@(context.Response.StatusCode == 200)" />
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

While the above is impressive, it only works if the application is restricted from direct access to the Azure OpenAI Service. To achieve this, I recommend creating a Private Endpoint for the AOAI service and wrapping a Network Security Group around the subnet (NSGs are now supported for private endpoints) to block access to the resources within the subnet to anything but the APIM instance. Keep in mind that the APIM instance needs to be able to access resources within the virtual network, which means that an APIM needs to be deployed in internal mode. The architecture could look similar to the image below.

APIM and Azure OpenAI Service with Private Networking

One thing to note is that if access is blocked as described above, it will break the AOAI studio feature within the Azure Portal. This is because calls to the data plane of the AOAI service are now blocked. A workaround could be to use a jump host or shared server if you need to continue supporting that feature. However, that opens up the risk that someone could write some code while on that machine and use the API keys. 

Let me sum up what we learned today:

  • APIM policies can be used to enforce Azure AD authentication and can block the use of API keys.
  • You must lock down the Azure OpenAI Service to just APIM to make this effective. Remember this will break access to the Studio within the Azure Portal.
  • Since you’re forcing Azure AD authentication, you can use the application id to add custom throttling.

That’s all for this post. The policy samples used in this blog have been uploaded to this repository on GitHub. Feel free to experiment with them and build upon them. If you end up building upon them and doing anything interesting, do reach out and let me know. I’m always interested in geeking out! In my next post, I’ll cover how to use an APIM policy to create custom logging that can be delivered to an Event Hub and consumed by the upstream service of your choice. Have a great week!

Authorization in Azure OpenAI Service

Hello folks!

The fun with the new Azure OpenAI Service continues! I’ve been lucky enough to have been tapped to help a number of Microsoft financial services customers with getting the Azure OpenAI Service in place with the appropriate infrastructure and security controls. In the process, I get to learn from a ton of smart people in the AI space. It’s truly been one of the highlights of my 20-year career.

Over the past few weeks I’ve been posting about what I’ve learned, and today I’m going to continue with that. In my first post on the service I gave a high level overview of the security controls Microsoft makes available to the customer to secure their instance of Azure OpenAI Service. In my second post I dove deep into how the service handles authentication and how Azure Active Directory (Azure AD) can be used to improve over the built-in API key-based authentication. Today I’m going to cover authorization and demonstrate how using Azure AD authentication lets you take advantage of granular authorization with Azure RBAC.

Let’s dig in!

As I covered in my last post, the Azure OpenAI Service has both a management plane and data plane. Each plane supports different types of authentication (process of verifying the identity of a user, process, or device, often as a prerequisite to allowing access to resources in an information system) and authorization (The right or a permission that is granted to a system entity to access a system resource). Operations such as swapping to a customer-managed key, enabling a private endpoint, or assigning a managed identity to the service occur within the management plane. Activities such as uploading training data or issuing a prompt to a model occur at the data plane. Each plane uses a different API endpoint. The image below will help you visualize the different planes.

Azure OpenAI Service Management and Data Planes

As illustrated above, authorization within the management plane is handled using Azure RBAC because authentication to that plane requires Azure AD-based authentication. Here we can limit the operations occurring at the management plane a security principal (user, service principal, managed identity, Azure Active Directory group (local or synchronized from on-premises) can perform by using Azure RBAC. For those of you coming from the AWS world, and where the Azure OpenAI Service may be your first venture into Azure, Azure RBAC is Azure’s authorization solution. It’s similar to an AWS IAM Policy. Let’s take a look at a built-in RBAC role that a customer might grant a data scientist who will be using the Azure OpenAI Service.

{
    "id": "/subscriptions/de90ea7d-a9c3-4957-8c96-XXXXXXXXXXXX/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908",
    "properties": {
        "roleName": "Cognitive Services User",
        "description": "Lets you read and list keys of Cognitive Services.",
        "assignableScopes": [
            "/"
        ],
        "permissions": [
            {
                "actions": [
                    "Microsoft.CognitiveServices/*/read",
                    "Microsoft.CognitiveServices/accounts/listkeys/action",
                    "Microsoft.Insights/alertRules/read",
                    "Microsoft.Insights/diagnosticSettings/read",
                    "Microsoft.Insights/logDefinitions/read",
                    "Microsoft.Insights/metricdefinitions/read",
                    "Microsoft.Insights/metrics/read",
                    "Microsoft.ResourceHealth/availabilityStatuses/read",
                    "Microsoft.Resources/deployments/operations/read",
                    "Microsoft.Resources/subscriptions/operationresults/read",
                    "Microsoft.Resources/subscriptions/read",
                    "Microsoft.Resources/subscriptions/resourceGroups/read",
                    "Microsoft.Support/*"
                ],
                "notActions": [],
                "dataActions": [
                    "Microsoft.CognitiveServices/*"
                ],
                "notDataActions": []
            }
        ]
    }
}

Let’s briefly walkthrough each property. The id property is the unique resource name assigned to this role definition. Next up we have the name property and description properties which need no explanations. The assignableScopes property determines at which scope an RBAC role can be assigned. Typical scopes include management groups, subscriptions, resource groups, and resources. Built-in roles will always have an assignable scope of “/” which denotes the RBAC role can be assigned to any management group, subscription, resource group, or role.

I’ll spend a bit of time on the permissions property. The permissions property contains a few different child properties including actions, notActions, dataActions, and notDataActions. The actions property lists the management plane operations allowed by the role while the dataActions lists the data plane operations allowed by the role. The notActions and notDataActions are interesting in that they are used to strip permissions out of the actions or dataActions. For example, say you granted a user full data plane operations to an Azure Key Vault but didn’t want them to have the ability to delete keys. You could to this by giving the user the dataAction of Microsoft.KeyVaults/* and notDataAction of Microsoft.KeyVaults/keys/purge/action. Take note this is NOT an explicit deny. If the user gets this permission in another way through assignment of a different RBAC role the user will be able to perform the action. At this time, Azure does not have a generally available feature that allows for an explicit deny like AWS IAM and what does exist in preview has an extremely narrow scope such that it isn’t very useful.

When you’re ready to assign a role to a security principal (user, service principal, managed identity, Azure Active Directory group (local or synchronized from on-premises) you create what is called a role assignment. A role assignment associates an Azure RBAC Role Definition to a security principal and scope. For example, in the below image I’ve created an RBAC Role Assignment for the Cognitive Services User Role at the resource group scope for the user Carl Carlson. This grants Carl the permission to perform the operations listed in the role definition above to any resource within the resource group, including the Azure OpenAI Resource.

Azure RBAC Role Assignment

Scroll back and take a look at the role definition, notice any risky permission? If you noticed the permission Microsoft.CognitiveServices/accounts/listkeys/action (remember that the Azure OpenAI Service falls under the Cognitive Services umbrella), grab yourself a cookie. As I’ve covered previously, every instance of the Azure OpenAI Service comes with two API keys. These API keys allow for authentication to the instance at the data plane level, can’t be limited in what they can do, and are very difficult to ever track back to who used them. You will want to very tightly control access to those API keys so be wary of who you give this role out to and may want to instead create a similar custom role but without this permission.

The are two other roles which are specific two the Azure OpenAI Service are the Cognitive Services OpenAI Contributor and Cognitive Services OpenAI User. Let’s look at the contributor role first.

{
    "id": "/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-XXXXXXXXXXXX",
    "properties": {
        "roleName": "Cognitive Services OpenAI Contributor",
        "description": "Full access including the ability to fine-tune, deploy and generate text",
        "assignableScopes": [
            "/"
        ],
        "permissions": [
            {
                "actions": [
                    "Microsoft.CognitiveServices/*/read",
                    "Microsoft.Authorization/roleAssignments/read",
                    "Microsoft.Authorization/roleDefinitions/read"
                ],
                "notActions": [],
                "dataActions": [
                    "Microsoft.CognitiveServices/accounts/OpenAI/*"
                ],
                "notDataActions": []
            }
        ]
    }
}

The big difference here is this role doesn’t grant much at the management plane. While this role may seem appealing to give to a data scientist because it doesn’t allow access to the API keys, it also doesn’t allow access to the instance metrics. I’ll talk about this more when I do a post on logging and monitoring in the service, but access to the metrics are important for the data scientists. These metrics allow them to see how much volume they’re doing with the service which can help them estimate costs and avoid hitting API limits.

Under the dataActions you can see this role allows all data plane operations. These operations include uploading training data for the creation of fine-tuned models. If you don’t want your users to have this access, then you can either strip the permissions Microsoft.CognitiveServices/accounts/OpenAI/files/import/action or grant the user the next role I’ll talk about.

One interesting thing to note is that while this role grants all data actions, which include data plane permissions around deployments, users with this role cannot deploy models to the instance. An error will be thrown that the user does not have the Microsoft.CognitiveServices/accounts/deployments/write permission. I’m not sure if this by design, but if anyone has a workaround for it, let me know in the comments. It would seem like if you want the user to deploy a model, you’ll need to model a custom role after this role and add that permissions.

The last role I’m going to cover is the Cognitive Services OpenAI User role. Let’s look at the permissions for this one.

{
    "id": "/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-XXXXXXXXXXXX",
    "properties": {
        "roleName": "Cognitive Services OpenAI User",
        "description": "Ability to view files, models, deployments. Readers can't make any changes They can inference",
        "assignableScopes": [
            "/"
        ],
        "permissions": [
            {
                "actions": [
                    "Microsoft.CognitiveServices/*/read",
                    "Microsoft.Authorization/roleAssignments/read",
                    "Microsoft.Authorization/roleDefinitions/read"
                ],
                "notActions": [],
                "dataActions": [
                    "Microsoft.CognitiveServices/accounts/OpenAI/*/read",
                    "Microsoft.CognitiveServices/accounts/OpenAI/engines/completions/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/engines/search/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/engines/generate/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/engines/completions/write",
                    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/search/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/completions/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/embeddings/action",
                    "Microsoft.CognitiveServices/accounts/OpenAI/deployments/completions/write"
                ],
                "notDataActions": []
            }
        ]
    }
}

Like the contributor role, this role is very limited with management plane permissions. At the data plane level, this role really allows for issuing prompts and not much else. This role is great a non-human application role assigned via service principal or managed identity. It will allow the application to issue prompts and not much else. You don’t have to worry about a user exploiting this role to access training data you may have uploaded or making any modification to the Azure OpenAI Service instance.

Well folks that wraps this up. Let’s sum up what we’ve learned:

  • The Azure OpenAI Service supports fine-grained authorization through Azure RBAC at both the management plane and data plane when the security principal is authenticated through Azure AD.
  • Avoid using API keys where possible and leverage Azure RBAC for authorization. You can make it much more fine-grained, layer in the controls provided by Azure AD on top of it, and associate the usage of the service back to user (kinda as we’ll see in my post on logging).
  • Tightly control access to the API keys. I’d recommend any role you give to a data scientist or an application that you strip out the listkeys permissions.
  • I’d recommend creating a custom role for human users modeled after the Cognitive Services User role but without the listkeys permission. This will grant the user access to the full data plane and allow access to management plane pieces such as metrics. You can optionally be granular with your dataActions and leave out the files permissions to prevent human users from uploading training data.
  • I’d recommend using the built-in Cognitive Services OpenAI User role for service principals and managed identities assigned to applications. It grants only the permissions these applications are likely going to need and nothing more.
  • I’d avoid using notActions and notDataActions since it’s not an explicit deny and it’s very difficult to determine an effective user’s access in Azure without another tool like Entra Permissions Management.

Well folks, I hope this post has helped you better understand authorization in the service and how you could potentially craft it to align with least privilege.

Next post up will be around logging.

Have a great night!

Capturing Azure Management Group Activity Logs Using Azure Automation – Part 1

Capturing Azure Management Group Activity Logs Using Azure Automation – Part 1

Hello again fellow geeks!

Over the past few months I’ve been working with a customer who is just beginning their journey into the cloud.  We’ve had a ton of great conversations around security, governance, and operationalizing Microsoft Azure.  We recently finalized the RACI and identified the controls required by both their internal security policy and their industry compliance requirements.  With those two items complete, we put together our Azure RBAC model and narrowed down the Azure Policies we needed to put in place to satisfy our compliance controls.

After a lot of discussion about the customer’s organization, its geographical locations, business unit makeup, and how its developers and central IT operate, we came up with a subscription model.  This customer had decided on an Azure subscription model where each workload would exist in its own subscription.  Further, each workload’s production and non-production environment would be segmented in different subscriptions.  Keeping each workload in a different subscription ensures no workload will compete for resources with other workloads and hit any subscription limits.  Additionally, it allowed the customer to very easily track the costs associated with each workload.

Now why did we use separate production and non-production subscriptions for each workload?  One reason is to address the same risk as above where a non-production workload could potentially consume all resources within a subscription impacting a production workload.  The other more critical reason is it makes it easier for us to apply different governance and access controls on production workloads vs non-production workloads.  The way we do this is through the usage of Azure Management Groups.

Management Groups were introduced into general availability back in late 2018 to help address the challenges organizations were having operating subscriptions at scale.  They provided a hierarchal method to apply governance and access controls across a collection of subscriptions.  For those of you familiar with AWS, Management Groups are somewhat similar to AWS Organizations and Organizational Units.  For my fellow Windows AD peeps, you can think of Management Groups somewhat like the Active Directory container and organizational unit hierarchy in an Active Directory domain where you apply different access control entries and group policy at high levels in the OU hierarchy that is then enforced and inherited down to the children.  Management Groups work in a similar manner in that the Azure RBAC definitions and assignments and Azure Policy you assign to the parent Management Groups are inherited down into the children.

Every Azure AD tenant starts with a top-level management group called the tenant root group.  Additional management groups created within the tenant are children of the group up to a maximum of 10,000 management groups and up to six levels of depth.  Any RBAC assignment or Azure Policy assigned to the tenant root group applies to all children management group in the tenant.  It’s important to understand that Management Groups are a resource within the Azure AD tenant and not a resource of an Azure subscription.  This will matter for reasons we’ll see later.

The tenant root management group can only be administered by a Global Admin by default and even this requires a configuration change in the tenant.  The method is describe here and what it does is places the global administrator performing the action in the User Access Administrator RBAC role at the root of scope.  Once that is complete, the name of the root management group could be changed, role assignments created, or policy assigned.

Screen Shot 2019-10-17 at 9.59.59 PM

Administering Tenant Root Group

Now there is one aspect of Management Groups that is a bit funky.  If you’re very observant you probably noticed the menu option below.

Screen Shot 2019-10-17 at 9.59.59 PM.png

That’s right folks, Management Groups have their own Activity Log.  Every action you perform at the management group scope such creating an Azure RBAC role assignment or assigning or un-assigning an Azure Policy is captured in this Activity Log.  Now as of today, the only way to access these logs is viewing them through the portal or through the Azure REST API.  Unlike the Activity Logs associated with a subscription, there isn’t native integration with Event Hubs or Azure Storage.  Don’t be fooled by the Export To Event Hub link seen in the screenshot below, this will simply send you to the standard menu where you would configure subscription Activity Logs to be exported.

Screen Shot 2019-10-17 at 10.34.19 PM

Now you could log into the GUI every day and export the logs to a CSV (yes that does work with Management Groups) but that simply isn’t scalable and also prevents you from proactively monitoring the logs.  So how do we deal with this gap while the product team works on incorporating the feature?  This will be the challenge we address in this series.

Over the next few posts I’ll walk through the solution I put together using Azure Automation Runbooks to capture these Activity Logs and send them to Azure Storage for retention and an Azure Log Analytics Workspace for analysis and monitoring using Azure Monitor.

Continue the series in my second post.

Exploring Azure AD Privileged Identity Management (PIM) – Part 4 – Access Review and Azure RBAC

Exploring Azure AD Privileged Identity Management (PIM) – Part 4 – Access Review and Azure RBAC

Access Reviews

Welcome to my final post on Azure Active Directory Privileged Identity Management (AAD PIM).  Over this series of posts I’ve provided an overview of the service, guidance on how to set the service up, and a deep dive and look at the user and approver experience.  We’ll wrap up the series by looking at the Access Review feature, take an intermission to look at a new feature, and wrap up with reviewing the Azure RBAC integration.

We have a lot to cover, so let’s jump into it.

As a quick refresher, I’ll be using my Journey Of the Geek tenant.  Within the tenant I have some Office 365 E5 and EMS E5 licenses provisioned.  Our admin user will be initiating the access review and Homer Simpson will acting as a reviewer.

I first log into the Azure Portal as the admin user and open up the AAD PIM shortcut from my dashboard.  Once the application opens, I’m going to navigate to the Azure AD directory roles option.

4aadpim1

After selecting the option my main menu is refreshed to show the management options for the various AAD PIM features.  As a quick refresher, let’s look at the settings I’ve configured for Access Reviews in my tenant.  We navigate to those Settings by clicking the Settings option as seen below and selecting Access Reviews.

4aadpim2

As you can see from the settings in the screenshot below, my tenant is set to send mail notifications to reviewers when a review is started and to admins when it finishes.  It’s also configured for reminders to be sent out to reviewers who haven’t yet completed their review.  I’ve configured reviewers to provider a reason as to why continued access to a privileged role needs to be maintained.  This is a great little option to capture the business requirements behind the access.  Finally, my access reviews are configured to run a total of 30 days.

4aadpim3

Let’s navigate back to the Access Review blade under the management menu.

4aadpim4

On the Access Reviews blade we see a listing of the access reviews in progress.  You can see I setup an access review for users that are members of the Global Admins role.  On the top we have the menu options to start a new access review, filter what access reviews are displayed, change the way they are grouped, and go back to the Access Review settings I showed earlier.

4aadpim5

et’s spin up a new access review for users who are permanent or eligible members of the User Administrators Azure AD role.  We click the Add link and a new blade opens where we can configure a number of options.  We have the basic options of naming the access review, providing a description, and a start and end date.

I’ve selected the User Administrator role as the role being reviewed during this access review.  Notice the Scope option with the Everyone radio button.  Perhaps that’s a placeholder for functionality that will be introduced in the future to limit the users within a role that the access review will cover.  I’ve selected Homer Simpson to be the reviewer for the role.  The advanced settings have inherited the global settings for my tenant for access reviews I covered previously.  Once the information is filled in, I hit the start button to kick off the access review.

4aadpim6

It takes a few minutes for the access review to be created and then it’s displayed in the listing of access reviews with a status of active.

4aadpim7

If we navigate over to Homer Simpson’s Outlook inbox, we see he has received an email informing him an access review has been kicked off and he has been designated as a reviewer and must approve or reject other members’ continued eligibility for the role.

4aadpim8

If we delay acting on the access review for a day we receive another reminder email per our settings.  The email can be seen below.

4aadpim9

If the approvers do not respond to the access review, the review completes but records that none of the users have been reviewed.

4aadpim10.png

Let’s spin up another review and complete this one.

4aadpim11

Homer Simpson again receives the notice that an Access Review has been kicked off.  Clicking the Start Review button in the email opens up the Azure Portal and the AAD PIM blade.  Here Homer gets an overview of the access review including the user who created the review, the length of the review, the description of the review, and the users who are members (permanent or eligible) for the role.

The filter option allows us to filter on the listing of users based upon whether they still need to be reviewed or have been approved or denied.

4aadpim12

We first check off Bart Simpson and see that we are required to input a reason for Bart’s approval or denial.  I input a reason and choose the deny button.  Bart disappears from the menu.  If I use the filter option to show all three categories of users, Bart now reappears under the denied category.

4aadpim13

I check off both Homer and Marge and provide a reason for both users and hit the approve button. All users have been reviewed by Homer Simpson. After refreshing the page the review now shows 0 users remaining to be reviewed.

4aadpim14

Switching over to the browser for the admin user we see that the access review is still open.

4aadpim15

If we open the access review we can see that all users have been reviewed even though the review is still active.  We have the option to Reset the access review to force the approvers to perform the access review activities again or we can stop it.  We’re going to choose end the access review early now that all the reviews have been completed.

4aadpim16

Re-opening the access review, we now have the option to apply the results of it.  After clicking he Apply button the changes are applied and we’re notified via the Portal notification system.

4aadpim17

Navigating to the roles blade under the Manage section now shows only Homer and Marge as being eligible for the User Administrator role verifying that the changes made during the access review have taken effect.

The access review feature is a wonderful addition by Microsoft.  Back in the olden days of Windows Active Directory, managing the entire lifecycle of an identity and its entitlements often involved complex third-party identity management solutions in combination with request management system.  By including this feature out of the gates, Microsoft is showing a real maturity in its identity offerings.

A Brief Intermission

Before I get into what AAD PIM can do for Azure RBAC, I want to touch on a new feature that went into public preview while I was working on this post.  Notice in the Manage section the Roles blade now has a (Preview) notation after it.

4aadpim18

Navigating into the blade shows an entirely new interface with far more useful information.  We now have a complete list of the roles AAD PIM can manage including descriptions.  If we select a role we go a level deeper and can add users to the role as we would expect.

4aadpim19

We also have two new menu options for Description and Definition.  The Description blade opens up and gives us a link to the Microsoft documentation on the role as well as every permissions the role has (AWESOME!).  The Definition blade gives us a JSON view of the role information.  Perhaps we’ll be able to create custom AAD / O365 roles in the future and we’ll be able to use these JSON views as ARM templates?  Time will tell.

4aadpim20

The introduction of this new feature is a great demonstration of how quickly things change in the cloud.

AAD PIM and Azure RBAC

Most organizations consuming Microsoft cloud services don’t just consume Office 365.  These organizations want to yield the benefits of the infrastructure-as-a-Service (IaaS) and platform-as-a-Service (PaaS) services provide by Microsoft’s Azure offering.  Managing authorization in Azure is handled through Azure Role-Based Access Control (RBAC).  In short, Azure RBAC provides a method of authorizing a security principal (user, group, or service principal) to perform an action on a resource (VM, storage account, Azure SQL, etc) based upon membership in a role.  Out of the box Microsoft provides a few roles such as owner, reader, and contributor.  You can also create custom roles to fit your business needs.

 

Similar to Office 365 prior to AAD PIM, preventing standing access of security principals in Azure RBAC roles was left to custom scripts and third-party solutions.  Last year it was announcedthat AAD PIM capabilities were being extended to Azure RBAC.  The integration of AAD PIM and Azure RBAC become generally available in the commercial offering of Azure AD in May of 2018.

For this demonstration I’m going to switch over to my Geek In The Weeds tenant.  Recall that the tenant is a synchronized and federated tenant using Azure AD Connect and Active Directory Federation Services.  I’ve already activated AAD PIM for the tenant so I’ll be jumping right into its integration with Azure RBAC.

After logging into the portal as a user who has permanent membership in the Privileged Role Administrator role I’m faced with the standard admin view of AAD PIM.  In the Manage menu I’m going to select the Azure resources option.

4aadpim21

If this is your first time using AAD PIM with Azure RBAC you’ll need to go through the discovery stage.  This will discover Azure Resources that you have write permissions to and thus have the ability to manage privileged access to.  After discovery is complete you’ll see a screen similar to the below.  You can see that my user is a member of the owners role for the Visual Studio Enterprise Azure subscription and that there are 77 roles defined for the subscription with three security principals holding one or more roles.

4aadpim22

Selecting the subscription resource gives us a dashboard displaying key metrics about PIM activity within the subscription.

4aadpim23.png

One of the metrics that caught my eye was the single user in the User Access Administrator role.  Selecting that area of the dashboard opens a new blade which lists out the members of the role.  We can see the service principal for PIM has been added to the User Access Administrator role to grant the service permissions to administer the roles within the resource (in this case a subscription).

4aadpim24

Notice also that the PIM menu for managing Azure AD/Office 365 differs for the menu for managing Azure RBAC.  We see that the new Role options I outlined above haven’t been migrated to the Azure RBAC integration yet.  Additionally we see that the request approval workflow is still in public preview in Azure RBAC.  In the Azure RBAC menu we also get a Resource Audit log which details PIM activity within the resource.

4aadpim25

Notice also that the general Settings option isn’t present in the Azure RBAC menu.  Instead we have a Role Settings option.  Selecting this option opens a new blade that lists out the Roles associated with the Resource.  Selecting any of the resources opens a new blade where we have the options of configuring a large selection of options for the role for both assignment (making the user eligible or a permanent member) as well as activation.  If you recall the configurable options for the Azure AD / Office 365 roles, these are far more granular.  The additional flexibility makes sense because these roles are going to managing IaaS and PaaS resources which are much more catered to programmatic access by non-humans.  The non-human access tends to be much more predictable than human access, so enforcing controls such as temporary eligibility for a role makes a lot of sense.

4aadpim26

Let’s take a look at what the experience is adding a user to one of the RBAC roles.  The process is very similar to AAD PIM with Azure AD / Office 365 in that we select the Roles option from the Manage section.  For this demonstration I’m going to add a user to the Virtual Machine Contributor role.

Clicking the Add Member option allows me to assign Ash Williams as an eligible member of the role.  Notice the additional option called Set membership settings.  Here I can set a timespan that Ash is eligible for the role.  This option isn’t available in AAD PIM for Azure AD / Office 365 that I could see.

4aadpim27

After hitting the add button Ash is successfully added as a Direct member to the role.  Notice that I can also add groups as members of the Role.  This is another capability unit to the Azure RBAC integration.

4aadpim28

Let’s go through the user experience for activating a role.  For the sake of simplicity I’m going to cover differences in the user experience.  You can reference my third post if you’re curious of the full user experience.

At this point I’ve logged into a virtual machine as Ash Williams and have authenticated to the Azure Portal.  I’ve entered the Azure resources blade.  Here we see the user being informed that no Azure resources are protected by PIM.  In this instance hitting the Discover resources permission will not update this menu because Ash Williams isn’t a member of any role that would grant him write permissions on an Azure Resource.  Instead I’m going to click the Activate Role button.

4aadpim29

After clicking the Activate role button I’m shown the roles Ash Williams is eligible to activate.  Notice Ash has the ability to activate the role due to both his direct membership and his membership in the GIW AIP Users group.  I’d recommend leveraging groups for this access where possible so you don’t get in the situation where you grant a security principal longer access to the role than you wanted due to a direct role assignment situation.

4aadpim30

he activation experience and approval experience is the same from this point forward so I’m going stop here.

Summing It Up

I really enjoyed this blog series.  I hadn’t done a deep dive into AAD PIM since it was in public preview and much has changed since then.  I really like how Microsoft is finally exposing capabilities which have historically been more Azure AD / Office 365 centric to Microsoft Azure.  It’s an excellent marketing tool for companies who may already be using Office 365 but are using another cloud provider for IaaS and PaaS. The product team has also done great job integrating much needed features such as approval workflows, access reviews, and metrics.

I’m not going to have the time to do a post about the AAD PIM PowerShell module but I recommend you check it out if you have some bandwidth.  There are some great opportunities there to integrate PIM functionality with third party workflow management tools to automate the entire user experience behind a GUI you users are already familiar with.

That wraps up my series on Azure AD Privileged Identity Management.  I hope you enjoyed it as much as I did.

See you next post!