Skip to main content

Check out Port for yourselfย 

Azure Resource Graph (Beta)

Loading version...
Availability Notice

This integration is in closed beta and is not available for general use. Please contact Port's support team to request access.

This integration provides a robust solution for syncing your Azure resources to Port by leveraging the open-source Ocean framework. It is designed for high-volume data ingestion across multiple subscriptions and efficiently queries the Azure Resource Graph API, ensuring high-performance data ingestion even in large-scale environments.

Key advantages:

  • Centralized Syncing: Ingest resources from all your Azure subscriptions with a single deployment.
  • High-Speed Ingestion: Leverage Azure Resource Graph to query and sync up to 5000 subscriptions simultaneously for maximum performance.
  • Customizable Mapping: Take full control over which resource types are ingested and how they are mapped to your software catalog.

On each run, the integration performs a full synchronization, so your software catalog always reflects the current state of your Azure resources. You can use declarative YAML mapping to transform raw data and model it according to your software catalog's structure.

The integration is packaged as a Docker container and can be deployed in any environment that supports it, such as Kubernetes or your CI/CD pipeline. This gives you full control over its execution schedule and operational management.

Supported resourcesโ€‹

The Azure Resource Graph integration supports the following kinds:

  • Resources - represents an Azure resource. By default, they're pulled from the resources table, which includes a wide array of Azure resources such as virtual machines, storage accounts, network interfaces, and more. You can override this by specifying another Azure Resource Graph table. To see all supported tables, refer to the official documentation.
  • ResourceContainers - represents management groups, subscriptions, and resource groups, providing the hierarchical context for your Azure resources.
  • Subscription - represents subscriptions from the Azure Resource Manager API.

Configurationโ€‹

Port integrations use a YAML mapping block to ingest data from the third-party api into Port.

The mapping makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from the integration API.

Default mapping configurationโ€‹

This is the default mapping configuration you get after installing the Azure integration.

Default mapping configuration (click to expand)
resources:
- kind: subscription
selector:
query: 'true'
port:
entity:
mappings:
identifier: .subscriptionId
title: .displayName
blueprint: '"azureSubscription"'
properties:
tags: .tags
state: .state
subscriptionPolicies: .subscriptionPolicies
- kind: resourceContainer
selector:
query: 'true'
graphQuery: >-
"resourcecontainers
| where type =~'microsoft.resources/subscriptions/resourcegroups'
| project id, type, name, location, tags, subscriptionId, resourceGroup
| extend resourceGroup=tolower(resourceGroup)
| extend type=tolower(type)"
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureResourceGroup"'
properties:
tags: .tags
type: .type
location: .location
relations:
subscription: ("/subscriptions/" + .subscriptionId) | gsub(" ";"_")
- kind: resource
selector:
query: 'true'
graphQuery: >-
"resources
| project id, type, name, location, tags, subscriptionId, resourceGroup
| extend resourceGroup=tolower(resourceGroup)
| extend type=tolower(type)
| join kind=leftouter (
resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| project rgName=tolower(name), rgTags=tags, rgSubscriptionId=subscriptionId
) on $left.subscriptionId == $right.rgSubscriptionId and
$left.resourceGroup == $right.rgName
| project id, type, name, location, tags, subscriptionId, resourceGroup, rgTags"
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureResource"'
properties:
tags: .tags
location: .location
relations:
resource_group: >-
("/subscriptions/" + .subscriptionId + "/resourceGroups/" +
.resourceGroup) | gsub(" ";"_")


The graphQuery selectorโ€‹

The graphQuery selector is a powerful feature that allows you to specify a custom query to fetch the exact Azure resources you need. It uses the Kusto Query Language (KQL).

With graphQuery, you can:

  • Filter resources based on their properties, tags, or any other attribute.
  • Select specific properties to reduce the amount of data ingested.
  • Join different resource tables to enrich the data.
  • Perform aggregations and other advanced operations.
Optimizing your graphQuery

It is highly recommended to optimize your graphQuery to fetch only the data you need. A well-crafted query can significantly improve the performance of the integration and ensure that your software catalog is not cluttered with unnecessary information.

For example, instead of fetching all resources and then filtering them in the mapping, you can use the where clause in your query to filter the resources at the source.

Here is an example of a broad query versus an optimized query:

Broad Query:

resources
| project id, type, name, location, tags, subscriptionId, resourceGroup

Optimized Query:

resources
| where type in~ ('microsoft.compute/virtualmachines', 'microsoft.storage/storageaccounts') and tags.environment == 'production'
| project id, type, name, location, tags, subscriptionId, resourceGroup

The optimized query fetches only virtual machines and storage accounts that have the environment tag set to production, which is much more efficient.

Setupโ€‹

To set up the Azure Resource Graph exporter, you'll need to configure both Port credentials and Azure app registration.

Port credentials

To get your Port credentials, go to your Port application, click on the ... button in the top right corner, and select Credentials. Here you can view and copy your CLIENT_ID and CLIENT_SECRET:

Azure setup

This integration requires the standard Azure app registration setup.

Keep the following credentials handy after setup:

  • AZURE_CLIENT_ID: The client ID of the Azure service principal
  • AZURE_CLIENT_SECRET: The client secret of the Azure service principal
  • AZURE_TENANT_ID: The tenant ID of the Azure service principal

Azure App Registration Setup

To ingest resources from Azure, you will need to create an Azure App Registration and assign it read permissions to the resources you want to ingest.

  1. Create an Azure App Registration in the Azure portal.





  2. Copy the Application (client) ID and Directory (tenant) ID from the App Registration.



  3. Create a client secret for the App Registration.



  4. Copy the Application (client) Secret from the App Registration.



  5. Create a new role assignment for the App Registration. Go to the Access control (IAM) section of the subscription you want to ingest resources from.

    Click on Add role assignment.

    Multi Account Support

    It is supported to ingest resources from multiple subscriptions, for that you will have to repeat the role assignment for each subscription you want to ingest resources from.



  6. Assign the Reader role to the App Registration.

    Permissions

    The Reader role is recommended for querying all resources in your Azure subscription. You can restrict permissions to specific resource groups or types by assigning a different role. If you do this, remember to adjust permissions when adding more resources to the catalog. Basic permissions required for ingesting resources from Azure include:

    • Microsoft.Resources/subscriptions/read (to list the accessible subscriptions)
    • Microsoft.Resources/subscriptions/resourceGroups/read (to list the accessible resource groups)
    • read/list permissions to the resources you want to ingest


Installationโ€‹

Deploy the Azure resource graph exporter using Helm on Kubernetes to support scheduled resyncs of resources from Azure to Port.

Prerequisites

Installation

Now that you have the Azure App Registration details, you can install the Azure exporter using Helm.

You should have the following information ready:

  • Port API credentials, you can check out the Port API documentation.
    • PORT_CLIENT_ID
    • PORT_CLIENT_SECRET
  • Azure Credentials:
    • AZURE_CLIENT_ID: The Application (client) ID from the Azure App Registration.
    • AZURE_CLIENT_SECRET: The Application (client) Secret from the Azure App Registration.
    • AZURE_TENANT_ID: The Directory (tenant) ID from the Azure App Registration.
helm repo add --force-update port-labs https://port-labs.github.io/helm-charts
helm upgrade --install azure port-labs/port-ocean \
--set port.clientId="PORT_CLIENT_ID" \
--set port.clientSecret="PORT_CLIENT_SECRET" \
--set port.baseUrl="https://api.getport.io" \
--set initializePortResources=true \
--set sendRawDataExamples=true \
--set scheduledResyncInterval=1440 \
--set integration.type="azure-rg" \
--set integration.identifier="azure-resource-graph" \
--set integration.eventListener.type="POLLING" \
--set integration.config.azureClientId="<AZURE_CLIENT_ID>" \
--set integration.config.azureClientSecret="<AZURE_CLIENT_SECRET>" \
--set integration.config.azureTenantId="<AZURE_TENANT_ID>"
Selecting a Port API URL by account region

The port_region, port.baseUrl, portBaseUrl, port_base_url and OCEAN__PORT__BASE_URL parameters are used to select which instance of Port API will be used.

Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.

Examplesโ€‹

Mapping Azure cloud resourcesโ€‹

The following example demonstrates how to ingest your Azure Subscriptions to Port.
You can use the following Port blueprint definitions and integration configuration:

Blueprint (click to expand)
{
"identifier": "azureCloudResources",
"description": "This blueprint represents an Azure Cloud Resource in our software catalog",
"title": "Azure Cloud Resources",
"icon": "Azure",
"schema": {
"properties": {
"tags": {
"title": "Tags",
"type": "object"
},
"type": {
"title": "Type",
"type": "string"
},
"location": {
"title": "Location",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {}
}
Mapping configuration (click to expand)
resources:
- kind: resource
selector:
query: 'true'
graphQuery: >-
"resources
| where type in~ ('microsoft.insights/datacollectionendpoints', 'microsoft.compute/virtualmachines')
| project id, type, name, location, tags, subscriptionId, resourceGroup
| extend resourceGroup=tolower(resourceGroup)
| extend type=tolower(type)
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureCloudResources"'
properties:
tags: .tags
type: .type
location: .location

Mapping cloud resources and resource groupsโ€‹

The following example demonstrates how to ingest your Azure Subscriptions to Port.
You can use the following Port blueprint definitions and integration configuration:

Blueprints (click to expand)

[
{
"identifier": "azureResourceGroup",
"description": "This blueprint represents an Azure Resource Group in our software catalog",
"title": "Azure Resource Group",
"icon": "Azure",
"schema": {
"properties": {
"location": {
"title": "Location",
"type": "string"
},
"tags": {
"title": "Tags",
"type": "object"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {}
},
{
"identifier": "azureResource",
"description": "This blueprint represents an AzureCloud Resource in our software catalog",
"title": "Azure Cloud Resources",
"icon": "Git",
"schema": {
"properties": {
"tags": {
"title": "Tags",
"type": "object"
},
"type": {
"title": "Type",
"type": "string"
},
"location": {
"title": "Location",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {
"resource_group": {
"title": "Resource Group",
"target": "azureResourceGroup",
"required": false,
"many": false
}
}
}
]
Mapping configuration (click to expand)
resources:
- kind: resourceContainer
selector:
query: 'true'
graphQuery: >-
"resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where (tostring(tags['environment']) =~ 'prod')
| project id, type, name, location, tags, subscriptionId, resourceGroup
| extend resourceGroup=tolower(resourceGroup)
| extend type=tolower(type)"
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureResourceGroup"'
properties:
tags: .tags
location: .location

- kind: resource
selector:
query: 'true'
graphQuery: >-
"resources
| project id, type, name, location, tags, subscriptionId, resourceGroup
| extend resourceGroup=tolower(resourceGroup)
| extend type=tolower(type)
| join kind=leftouter (
resourcecontainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| project rgName=tolower(name), rgTags=tags, rgSubscriptionId=subscriptionId
) on $left.subscriptionId == $right.rgSubscriptionId and $left.resourceGroup == $right.rgName
| where (tostring(rgTags['environment']) =~ 'prod') and not (tostring(rgTags['environment']) =~ 'staging')
| project id, type, name, location, tags, subscriptionId, resourceGroup, rgTags "
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureResource"'
properties:
tags: .tags
type: .type
location: .location
relations:
resource_group: >-
("/subscriptions/" + .subscriptionId + "/resourceGroups/" + .resourceGroup) | gsub(" ";"_")