Edit

Share via


Configure Microsoft Entra Private Access using Microsoft Graph APIs

Microsoft Entra Private Access enables secure remote access to on-premises apps without requiring a VPN.

This tutorial explains how to configure Microsoft Entra Private Access using Microsoft Graph network access APIs. You will:

  • Create a custom app to configure backend app settings.
  • Set up Microsoft Entra application proxy for private access.

Important

Some API operations in this tutorial use the beta endpoint.

Prerequisites

To follow this tutorial:

  • Have a Microsoft Entra tenant with the Microsoft Entra Suite license.
  • Install and set up the Private Network Access connector. For more information, see Add an on-premises application for remote access through application proxy in Microsoft Entra ID.
  • Sign in to an API client like Graph Explorer with an account that has the required administrator roles. The following Microsoft Entra roles are the minimum needed for this tutorial:
    • Application Administrator to create the app.
    • Global Secure Access Administrator to configure Global Secure Access settings on the app.
  • Grant the app Directory.ReadWrite.All and NetworkAccess.ReadWrite.All delegated permissions.
  • Have a test user to assign to the app.

Step 1: Create a custom application

To set up the application proxy, first create a custom application, then update the app proxy settings in the onPremisesPublishing property.

Use an application template to create a custom application and service principal in your tenant. The template ID for a custom application is 8adf8e6e-67b2-4cf2-a259-e3dc5476c621. You can find it by running this query: GET https://graph.microsoft.com/v1.0/applicationTemplates?$filter=displayName eq 'Custom'.

From the response, note the id of the service principal and application objects, and the appId for later use.

The following request creates a custom application named newPrivateApp.

Request

POST https://graph.microsoft.com/v1.0/applicationTemplates/8adf8e6e-67b2-4cf2-a259-e3dc5476c621/instantiate
Content-type: application/json

{ 
   "displayName": "newPrivateApp" 
} 

Response

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.applicationServicePrincipal",
    "application": {
        "id": "bf21f7e9-9d25-4da2-82ab-7fdd85049f83",
        "appId": "32977d3b-ee0e-4614-9f50-f583a07842d2",
        "applicationTemplateId": "8adf8e6e-67b2-4cf2-a259-e3dc5476c621",
        "createdDateTime": "2024-02-22T16:48:09Z",
        "deletedDateTime": null,
        "displayName": "newPrivateApp",
        "description": null,
        "groupMembershipClaims": null,
        "identifierUris": [],
        "isFallbackPublicClient": false,
        "signInAudience": "AzureADMyOrg",
        "tags": [],
        "tokenEncryptionKeyId": null,
        "defaultRedirectUri": null,
        "samlMetadataUrl": null,
        "optionalClaims": null,
        "addIns": [],
        "api": {
            "acceptMappedClaims": null,
            "knownClientApplications": [],
            "requestedAccessTokenVersion": null,
            "oauth2PermissionScopes": [
                {
                    "adminConsentDescription": "Allow the application to access newPrivateApp on behalf of the signed-in user.",
                    "adminConsentDisplayName": "Access newPrivateApp",
                    "id": "5cda2e1e-d9fd-4f69-b981-48fbc8a16be1",
                    "isEnabled": true,
                    "type": "User",
                    "userConsentDescription": "Allow the application to access newPrivateApp on your behalf.",
                    "userConsentDisplayName": "Access newPrivateApp",
                    "value": "user_impersonation"
                }
            ],
            "preAuthorizedApplications": []
        },
        "appRoles": [
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "displayName": "User",
                "id": "18d14569-c3bd-439b-9a66-3a2aee01d14f",
                "isEnabled": true,
                "description": "User",
                "value": null,
                "origin": "Application"
            },
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "displayName": "msiam_access",
                "id": "b9632174-c057-4f7e-951b-be3adc52bfe6",
                "isEnabled": true,
                "description": "msiam_access",
                "value": null,
                "origin": "Application"
            }
        ],
        "info": {
            "logoUrl": null,
            "marketingUrl": null,
            "privacyStatementUrl": null,
            "supportUrl": null,
            "termsOfServiceUrl": null
        },
        "keyCredentials": [],
        "parentalControlSettings": {
            "countriesBlockedForMinors": [],
            "legalAgeGroupRule": "Allow"
        },
        "passwordCredentials": [],
        "publicClient": {
            "redirectUris": []
        },
        "requiredResourceAccess": [],
        "verifiedPublisher": {
            "displayName": null,
            "verifiedPublisherId": null,
            "addedDateTime": null
        },
        "web": {
            "homePageUrl": "https://account.activedirectory.windowsazure.com:444/applications/default.aspx?metadata=customappsso|ISV9.1|primary|z",
            "redirectUris": [],
            "logoutUrl": null
        }
    },
    "servicePrincipal": {
        "id": "a8cac399-cde5-4516-a674-819503c61313",
        "deletedDateTime": null,
        "accountEnabled": true,
        "appId": "32977d3b-ee0e-4614-9f50-f583a07842d2",
        "applicationTemplateId": "8adf8e6e-67b2-4cf2-a259-e3dc5476c621",
        "appDisplayName": "newPrivateApp",
        "alternativeNames": [],
        "appOwnerOrganizationId": "38d49456-54d4-455d-a8d6-c383c71e0a6d",
        "displayName": "newPrivateApp",
        "appRoleAssignmentRequired": true,
        "loginUrl": null,
        "logoutUrl": null,
        "homepage": "https://account.activedirectory.windowsazure.com:444/applications/default.aspx?metadata=customappsso|ISV9.1|primary|z",
        "notificationEmailAddresses": [],
        "preferredSingleSignOnMode": null,
        "preferredTokenSigningKeyThumbprint": null,
        "replyUrls": [],
        "servicePrincipalNames": [
            "32977d3b-ee0e-4614-9f50-f583a07842d2"
        ],
        "servicePrincipalType": "Application",
        "tags": [
            "WindowsAzureActiveDirectoryCustomSingleSignOnApplication",
            "WindowsAzureActiveDirectoryIntegratedApp"
        ],
        "tokenEncryptionKeyId": null,
        "samlSingleSignOnSettings": null,
        "addIns": [],
        "appRoles": [
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "displayName": "User",
                "id": "18d14569-c3bd-439b-9a66-3a2aee01d14f",
                "isEnabled": true,
                "description": "User",
                "value": null,
                "origin": "Application"
            },
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "displayName": "msiam_access",
                "id": "b9632174-c057-4f7e-951b-be3adc52bfe6",
                "isEnabled": true,
                "description": "msiam_access",
                "value": null,
                "origin": "Application"
            }
        ],
        "info": {
            "logoUrl": null,
            "marketingUrl": null,
            "privacyStatementUrl": null,
            "supportUrl": null,
            "termsOfServiceUrl": null
        },
        "keyCredentials": [],
        "oauth2PermissionScopes": [
            {
                "adminConsentDescription": "Allow the application to access newPrivateApp on behalf of the signed-in user.",
                "adminConsentDisplayName": "Access newPrivateApp",
                "id": "5cda2e1e-d9fd-4f69-b981-48fbc8a16be1",
                "isEnabled": true,
                "type": "User",
                "userConsentDescription": "Allow the application to access newPrivateApp on your behalf.",
                "userConsentDisplayName": "Access newPrivateApp",
                "value": "user_impersonation"
            }
        ],
        "passwordCredentials": [],
        "verifiedPublisher": {
            "displayName": null,
            "verifiedPublisherId": null,
            "addedDateTime": null
        }
    }
}

Step 2: Specify the type of private application

You can create a Global Secure Access private app as either a Quick Access or enterprise app. Set the application > onPremisesPublishing > applicationType property to quickaccessapp for Quick Access apps or nonwebapp for enterprise apps.

In this step, you set up a Global Secure Access private app as an enterprise app.

The request returns a 204 No Content response.

PATCH https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83

{
   "onPremisesPublishing":{
      "applicationType":"nonwebapp",
      "isAccessibleViaZTNAClient": true
   }
}

Step 3: Assign a connector group to the application

Step 3.1: Get connectors

Identify the connector that you want to assign to the connector group. Record its id.

Request

GET https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectors

Response

HTTP/1.1 200 OK
Content-type: application/json

{
  "@odata.context": "https://graph.microsoft.com/beta/$metadata#onPremisesPublishingProfiles('applicationProxy')/connectors",
  "@microsoft.graph.tips": "Use $select to choose only the properties your app needs, as this can lead to performance improvements. For example: GET onPremisesPublishingProfiles('<key>')/connectors?$select=externalIp,machineName",
  "value": [
    {
      "id": "d2b1e8e8-8511-49d6-a4ba-323cb083fbb0",
      "machineName": "connectorA.redmond.contoso.com",
      "externalIp": "131.137.147.164",
      "status": "active"
    },
    {
      "id": "f2cab422-a1c8-4d70-a47e-2cb297a2e051",
      "machineName": "connectorB.contoso.com",
      "externalIp": "68.0.191.210",
      "status": "active"
    }
  ]
}

Step 3.2: Create a connectorGroup

Create a connectorGroup named Private Access ConnectorGroup for the application. Record its id.

Request

POST https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups
Content-type: application/json

{
  "name": "Private Access ConnectorGroup"
}

Response

HTTP/1.1 201 Created
Content-type: application/json

{
  "@odata.context": "https://graph.microsoft.com/beta/$metadata#connectorGroups/$entity",
  "id": "daf709c2-6072-414f-b08c-bb2a80c631c",
  "name": "Private Access ConnectorGroup",
  "connectorGroupType": "applicationProxy",
  "region": "eur",
  "isDefault": false
}

Step 3.3: Assign a connector to the connectorGroup

The following request returns a 204 No content response.

POST https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectors/27049d40-6e0a-4c53-a171-daada6e9c8a0/memberOf/$ref
Content-type: application/json

{
  "@odata.id":"https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/daf709c2-6072-414f-b08c-bb2a80c631c"
}

Step 3.4: Assign the private app to the connector group

The following request returns a 204 No content response.

PUT https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83/connectorGroup/$ref
Content-type: application/json

{
  "@odata.id":"https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationproxy/connectorGroups/daf709c2-6072-414f-b08c-bb2a80c631c"
}

Step 4: Add application segments to the private application

In the example, create a new app segment with the following settings:

  • Replace the value of destinationHost with the private app destination.
  • Replace the value of destinationType with either ipAddress, ipRange, ipRangeCidr, fqdn, or dnsSuffix.
  • Replace the value of protocol with either tcp, udp, or tcp,udp depending on the protocol your app uses.

Request

POST https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83/onPremisesPublishing/segmentsConfiguration/microsoft.graph.ipSegmentConfiguration/applicationSegments

{
   "destinationHost": "test2.com",
   "destinationType": "fqdn",
   "port": 0,
   "ports": [
      "445-445",
      "3389-3389"
   ],
   "protocol": "tcp"
}

Response

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#applications('8706aca4-94e9-4783-a23d-7dae1599a6e0')/onPremisesPublishing/segmentsConfiguration/microsoft.graph.ipSegmentConfiguration/applicationSegments/$entity",
    "destinationHost": "test2.com",
    "destinationType": "fqdn",
    "port": 0,
    "ports": [
        "445-445",
        "3389-3389"
    ],
    "protocol": "tcp",
    "id": "2b52958c-9d0c-449d-a985-c29d488a6335"
}

[Optional] Update or delete an existing app segment

The following request updates an existing segment by changing the protocol to both tcp and udp. The request returns a 204 No Content response code.

PATCH https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83/onPremisesPublishing/segmentsConfiguration/microsoft.graph.ipSegmentConfiguration/applicationSegments/{segmentID}

{ 
   "protocol":"tcp,udp" 
} 

Step 5: Assign a user to the private application

Assign the user to the service principal and grant them the User app role. In the request body, provide the following values:

  • principalId - The ID of the user account that you created.
  • appRoleId - The ID of the default User app role that you retrieved from the service principal.
  • resourceId - The ID of the service principal.

Request

POST https://graph.microsoft.com/beta/servicePrincipals/a8cac399-cde5-4516-a674-819503c61313/appRoleAssignments
Content-type: application/json

{
  "principalId": "4628e7df-dff3-407c-a08f-75f08c0806dc",
  "principalType": "User",
  "appRoleId":"18d14569-c3bd-439b-9a66-3a2aee01d14f",
  "resourceId":"a8cac399-cde5-4516-a674-819503c61313"
}

Response

HTTP/1.1 200 OK
Content-type: application/json

{
  "@odata.context": "https://graph.microsoft.com/beta/$metadata#appRoleAssignments/$entity",
  "id": "I23pL8ZdNU-CIgQmqMEVyLJ0E6fx0ixEo92az8MnhtU",
  "creationTimestamp": "2020-06-09T00:06:07.5129268Z",
  "appRoleId": "18d14569-c3bd-439b-9a66-3a2aee01d14f",
  "principalDisplayName": "MyTestUser1",
  "principalId": "4628e7df-dff3-407c-a08f-75f08c0806dc",
  "principalType": "User",
  "resourceDisplayName": "newPrivateApp",
  "resourceId": "a8cac399-cde5-4516-a674-819503c61313"
}

Step 6: Enable the Private Access traffic forwarding profile for your tenant

Step 6.1: Retrieve the private access forwarding traffic profiles in your tenant

Record the id of the profile to use.

Request

GET https://graph.microsoft.com/beta/networkAccess/forwardingProfiles?$filter=trafficForwardingType eq 'private'

Response

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#networkAccess/forwardingProfiles",
    "@microsoft.graph.tips": "Use $select to choose only the properties your app needs, as this can lead to performance improvements. For example: GET networkAccess/forwardingProfiles?$select=associations,priority",
    "value": [
        {
            "trafficForwardingType": "private",
            "priority": 1,
            "id": "983891f5-e561-40ca-a4d1-cf4540d9a000",
            "name": "Private access traffic forwarding profile",
            "description": "Default traffic forwarding profile for Private access traffic acquisition. Assign the profile to client or branch offices to acquire Private access traffic for Zero Trust Network Access.",
            "state": "enabled",
            "version": "1.0.0",
            "lastModifiedDateTime": "2024-03-12T17:35:36Z",
            "associations": [],
            "servicePrincipal": null
        }
    ]
}

Step 6.2: Enable the state of the Private Access forwarding profile

The request returns a 204 No content response.

PATCH https://graph.microsoft.com/beta/networkAccess/forwardingProfiles/983891f5-e561-40ca-a4d1-cf4540d9a000

{
   "state": "enabled"
}

Step 7: Enable private Domain Name System (DNS) resolution

This capability is only available for Global Secure Access private applications of type Quick Access. The request returns a 204 No Content response code.

PATCH https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83/onPremisesPublishing

{ 
   "isDnsResolutionEnabled": true 
} 

Step 8: Create a new DNS suffix

Request

POST https://graph.microsoft.com/beta/applications/bf21f7e9-9d25-4da2-82ab-7fdd85049f83/onPremisesPublishing/segmentsConfiguration/microsoft.graph.ipSegmentConfiguration/applicationSegments

{ 
   "destinationHost": "app1.dns.com", 
   "destinationType": "dnsSuffix" 
} 

Response

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#applications('71fe4b11-db80-4525-b7e9-f503fb748180')/onPremisesPublishing/segmentsConfiguration/microsoft.graph.ipSegmentConfiguration/applicationSegments/$entity",
    "destinationHost": "app3.dns.com",
    "destinationType": "dnsSuffix",
    "port": 0,
    "ports": [],
    "protocol": "0",
    "id": "6ce9df44-734d-4240-aa3b-789ecaf7b7ce"
}