Azure Resource Graph (Beta)
Sync your Azure environment to Port at scale using Azure Resource Graph and Ocean framework. This integration is designed for high-volume data ingestion across multiple subscriptions, offering several 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.
Overviewโ
This integration provides a robust solution for syncing your Azure resources to Port by leveraging our open-source Ocean framework. It uses the Azure SDK to efficiently query the Azure Resource Graph API, ensuring high-performance data ingestion even in large-scale environments.
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 integration syncs data from two main Azure Resource Graph tables:
Resources
: This table includes a wide array of Azure resources, such as virtual machines, storage accounts, network interfaces, and more. The integration syncs their properties, tags, and metadata.ResourceContainers
: This table contains management groups, subscriptions, and resource groups, providing the hierarchical context for your Azure resources.
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: resource
selector:
query: 'true'
port:
entity:
mappings:
identifier: '.id | gsub(" ";"_")'
title: .name
blueprint: '"azureCloudResources"'
properties:
tags: .tags
type: .type
location: .location
- kind: resourceContainer
selector:
query: .type == "microsoft.resources/subscriptions"
port:
entity:
mappings:
identifier: '.id | gsub(" ";"_")'
title: .name
blueprint: '"azureSubscription"'
properties:
subscriptionId: .subscriptionId
location: .location
- kind: resourceContainer
selector:
query: .type == "microsoft.resources/subscriptions/resourcegroups"
port:
entity:
mappings:
identifier: '.id | gsub(" ";"_")'
title: .name
blueprint: '"azureResourceGroup"'
properties:
tags: .tags
location: .location
relations:
subscription: '("/subscriptions/" + .subscriptionId) | gsub(" ";"_")'
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 principalAZURE_CLIENT_SECRET
: The client secret of the Azure service principalAZURE_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.
-
Create an Azure App Registration in the Azure portal.
-
Copy the
Application (client) ID
andDirectory (tenant) ID
from the App Registration.
-
Create a client secret for the App Registration.
-
Copy the
Application (client) Secret
from the App Registration.
-
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 onAdd role assignment
.Multi Account SupportIt 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.
-
Assign the
Reader
role to the App Registration.PermissionsThe 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โ
- Helm (Scheduled)
- CI/CD (Scheduled)
- On-Prem (Once)
The Azure resource graph exporter is deployed using helm on kubernetes.
This way of deployment supports scheduled resyncs of resources from Azure to Port.
Prerequisites
Installation
Loading version...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>"
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.
- If you use the EU region of Port (https://app.port.io), your API URL is
https://api.port.io
. - If you use the US region of Port (https://app.us.port.io), your API URL is
https://api.us.port.io
.
- Azure DevOps
- GitHub Actions
- ArgoCD
- GitLab
The Azure exporter is deployed using Azure DevOps pipline, which supports scheduled resyncs of resources from Azure to Port.
Prerequisites
- Port API credentials
- Access to an Azure DevOps project with permission to configure pipelines and secrets.
- Azure App Registration Credentials
Installation
Now that you have the Azure App Registration details, you can set up the Azure exporter using an Azure DevOps pipeline.
Make sure to configure the following secret variables in a variable group:
- Port API credentials:
PORT_CLIENT_ID
PORT_CLIENT_SECRET
- Azure Credentials:
OCEAN__SECRET__AZURE_CLIENT_ID
: The Application (client) ID from the Azure App Registration.OCEAN__SECRET__AZURE_CLIENT_SECRET
: The Application (client) Secret from the Azure App Registration.OCEAN__SECRET__AZURE_TENANT_ID
: The Directory (tenant) ID from the Azure App Registration.
Here is an example for azure-pipeline-integration.yml
workflow file:
Make sure to change the highlighted line to your variable group's name.Azure pipline integration (Click to expand)
name: Azure Resource Graph Exporter Pipeline
trigger: none
schedules:
- cron: "0 */4 * * *"
displayName: Every 4 Hours
branches:
include:
- main
always: true
variables:
- group: port-azure-exporter-secrets # Contains the secrets used below
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Bash@3
displayName: 'Run Ocean Sail (Azure-RG)'
inputs:
targetType: 'inline'
script: |
set -euo pipefail
echo "Building .env file for Ocean Sail..."
echo "OCEAN__PORT__CLIENT_ID=$(PORT_CLIENT_ID)" > .sail-env
echo "OCEAN__PORT__CLIENT_SECRET=$(PORT_CLIENT_SECRET)" >> .sail-env
echo "OCEAN__PORT__BASE_URL=https://api.getport.io" >> .sail-env
echo "OCEAN__EVENT_LISTENER={\"type\":\"ONCE\"}" >> .sail-env
echo "OCEAN__INITIALIZE_PORT_RESOURCES=true" >> .sail-env
echo "OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_ID=$(OCEAN__SECRET__AZURE_CLIENT_ID)" >> .sail-env
echo "OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_SECRET=$(OCEAN__SECRET__AZURE_CLIENT_SECRET)" >> .sail-env
echo "OCEAN__INTEGRATION__CONFIG__AZURE_TENANT_ID=$(OCEAN__SECRET__AZURE_TENANT_ID)" >> .sail-env
echo "Running Ocean Sail container..."
docker run -i --rm \
--platform=linux/amd64 \
--env-file .sail-env \
ghcr.io/port-labs/port-ocean-azure-rg:latest
- task: Bash@3
displayName: 'Clean up .env file'
condition: always()
inputs:
targetType: 'inline'
script: |
rm -f .sail-env
The Azure exporter is deployed using Github Actions, which supports scheduled resyncs of resources from Azure to Port.
Installation
Now that you have the Azure App Registration details, you can set up the Azure exporter using Github Actions.
Make sure to configure the following Github Secrets:
- Port API credentials:
PORT_CLIENT_ID
PORT_CLIENT_SECRET
- Azure Credentials:
OCEAN__SECRET__AZURE_CLIENT_ID
: The Application (client) ID from the Azure App Registration.OCEAN__SECRET__AZURE_CLIENT_SECRET
: The Application (client) Secret from the Azure App Registration.OCEAN__SECRET__AZURE_TENANT_ID
: The Directory (tenant) ID from the Azure App Registration.
Parameter | Description | Required |
---|---|---|
OCEAN__PORT__CLIENT_ID | Your port client id. | โ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret. | โ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US. | โ |
OCEAN__SECRET__AZURE_CLIENT_ID | Your Azure client ID. | โ |
OCEAN__SECRET__AZURE_CLIENT_SECRET | Your Azure client secret. | โ |
OCEAN__SECRET__AZURE_TENANT_ID | Your Azure tenant ID. | โ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping. | โ |
OCEAN__SEND_RAW_DATA_EXAMPLES | Enable sending raw data examples from the third party API to port for testing and managing the integration mapping. Default is true. | โ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one. | โ |
Here is an example for azure-rg-integration.yml
workflow file:GitHub Action integration (Click to expand)
name: Azure Resource Graph Exporter Workflow
on:
workflow_dispatch:
schedule:
- cron: '0 */4 * * *'
jobs:
run-integration:
runs-on: ubuntu-latest
steps:
- name: Run azure-rg Integration
uses: port-labs/ocean-sail@v1
with:
type: azure-rg
port_client_id: ${{ secrets.PORT_CLIENT_ID }}
port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }}
port_base_url: "https://api.getport.io"
config: |
azure_client_id: ${{ secrets.OCEAN__SECRET__AZURE_CLIENT_ID }}
azure_client_secret: ${{ secrets.OCEAN__SECRET__AZURE_CLIENT_SECRET }}
azure_tenant_id: ${{ secrets.OCEAN__SECRET__AZURE_TENANT_ID }}
Prerequisites
Installation
- Create a
values.yaml
file inargocd/azure-rg-integration
in your git repository with the content:
initializePortResources: true
scheduledResyncInterval: 120
integration:
identifier: azure-rg-integration
type: azure-rg
eventListener:
type: POLLING
config:
azureClientId: <AZURE_CLIENT_ID>
azureClientSecret: <AZURE_CLIENT_SECRET>
azureTenantId: <AZURE_TENANT_ID>
- Install the
azure-rg-integration
ArgoCD Application by creating the followingazure-rg-integration.yaml
manifest:
Remember to replace the placeholders for YOUR_PORT_CLIENT_ID
YOUR_PORT_CLIENT_SECRET
and YOUR_GIT_REPO_URL
.
Multiple sources ArgoCD documentation can be found here.
ArgoCD Application (Click to expand)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-ocean-azure-rg-integration
namespace: argocd
spec:
destination:
namespace: mmy-ocean-azure-rg-integration
server: https://kubernetes.default.svc
project: default
sources:
- repoURL: 'https://port-labs.github.io/helm-charts/'
chart: port-ocean
targetRevision: 0.9.5
helm:
valueFiles:
- $values/argocd/my-ocean-azure-rg-integration/values.yaml
parameters:
- name: port.clientId
value: YOUR_PORT_CLIENT_ID
- name: port.clientSecret
value: YOUR_PORT_CLIENT_SECRET
- name: port.baseUrl
value: https://api.getport.io
- repoURL: YOUR_GIT_REPO_URL
targetRevision: main
ref: values
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
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.
- If you use the EU region of Port (https://app.port.io), your API URL is
https://api.port.io
. - If you use the US region of Port (https://app.us.port.io), your API URL is
https://api.us.port.io
.
- Apply the
azure-rg-integration.yaml
manifest to your Kubernetes cluster.
kubectl apply -f azure-rg-integration.yaml
Prerequisites
Make sure to configure the following GitLab variables:
Parameter | Description | Required |
---|---|---|
OCEAN__PORT__CLIENT_ID | Your port client id. | โ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret. | โ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US. | โ |
OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_ID | The client ID of the Azure App Registration. | โ |
OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_SECRET | The client secret of the Azure App Registration. | โ |
OCEAN__INTEGRATION__CONFIG__AZURE_TENANT_ID | The tenant ID of the Azure App Registration. | โ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, when set to false the integration will not create default blueprints and the port App config mapping. | โ |
OCEAN__SEND_RAW_DATA_EXAMPLES | Enable sending raw data examples from the third party API to port for testing and managing the integration mapping. Default is true. | โ |
Here is an example for .gitlab-ci.yml
pipeline file:
default:
image: docker:24.0.5
services:
- docker:24.0.5-dind
before_script:
- docker info
variables:
INTEGRATION_TYPE: azure-rg
VERSION: latest
stages:
- ingest
ingest_data:
stage: ingest
variables:
IMAGE_NAME: ghcr.io/port-labs/port-ocean-$INTEGRATION_TYPE:$VERSION
script:
- |
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__PORT__CLIENT_ID=$PORT_CLIENT_ID \
-e OCEAN__PORT__CLIENT_SECRET=$PORT_CLIENT_SECRET \
-e OCEAN__PORT__BASE_URL="https://api.port.io" \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__SEND_RAW_DATA_EXAMPLES=true \
-e OCEAN__EVENT_LISTENER='{"type": "ONCE"}' \
-e OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_ID="Enter value here" \
-e OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_SECRET="Enter value here" \
-e OCEAN__INTEGRATION__CONFIG__AZURE_TENANT_ID="Enter value here" \
$IMAGE_NAME
rules: # Run only when changes are made to the main branch
- if: '$CI_COMMIT_BRANCH == "main"'
Prerequisites
Installation
Now that you have the Azure App Registration details, you can install the Azure exporter using Docker.
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.
Environment Variables
Variable | Description |
---|---|
OCEAN__PORT__CLIENT_ID | Your Port client ID. |
OCEAN__PORT__CLIENT_SECRET | Your Port client secret. |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US |
OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_ID | The client ID of the Azure App Registration. |
OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_SECRET | The client secret of the Azure App Registration. |
OCEAN__INTEGRATION__CONFIG__AZURE_TENANT_ID | The tenant ID of the Azure App Registration. |
OCEAN__EVENT_LISTENER | The event listener object. |
OCEAN__INTEGRATION__IDENTIFIER | The identifier of the integration. |
OCEAN__INTEGRATION__TYPE | should be set to azure-rg . |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to true the integration will create default blueprints and the port App config Mapping. Read more about initializePortResources |
OCEAN__SEND_RAW_DATA_EXAMPLES | Enable sending raw data examples from the third party API to port for testing and managing the integration mapping. Default is true |
For example:
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__PORT__CLIENT_ID="$PORT_CLIENT_ID" \
-e OCEAN__PORT__CLIENT_SECRET="$PORT_CLIENT_SECRET" \
-e OCEAN__PORT__BASE_URL="https://api.getport.io" \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__SEND_RAW_DATA_EXAMPLES=true \
-e OCEAN__EVENT_LISTENER='{"type": "ONCE"}' \
-e OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_ID=AZURE_CLIENT_ID \
-e OCEAN__INTEGRATION__CONFIG__AZURE_CLIENT_SECRET=$AZURE_CLIENT_SECRET \
-e OCEAN__INTEGRATION__CONFIG__AZURE_TENANT_ID=$AZURE_TENANT_ID \
ghcr.io/port-labs/port-ocean-azure-rg:latest
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'
resource_types:
- microsoft.insights/datacollectionendpoints
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureCloudResources"'
properties:
tags: .tags
type: .type
location: .location
You can filter resources from Azure Resource Graph by specifying resource_types
in the mapping configuration. This provides precise control over synced data, streamlining ingestion and keeping your catalog focused on relevant resources.
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": "azureCloudResources",
"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: .type == "microsoft.resources/subscriptions/resourcegroups"
tags:
included:
environment: staging
exluded:
environment: production
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureResourceGroup"'
properties:
tags: .tags
location: .location
- kind: resource
selector:
query: 'true'
tags:
included:
environment: staging
exluded:
environment: production
port:
entity:
mappings:
identifier: .id | gsub(" ";"_")
title: .name
blueprint: '"azureCloudResources"'
properties:
tags: .tags
type: .type
location: .location
relations:
resource_group: >-
("/subscriptions/" + .subscriptionId + "/resourceGroups/" + .resourceGroup) | gsub(" ";"_")
You can filter Azure resources using tags from their parent resource groups. This allows you to define both inclusion and exclusion rules in a single configuration, giving you precise control over which resources are synchronized.
Frequently asked questionsโ
Why should I filter resources by their resource group tags?โ
Filtering resources by their parent resource group's tags simplifies management and synchronization for several reasons:
- Simplified Tagging: Apply tags at the resource group level instead of to individual resources. This is more manageable and ensures resources share a common context.
- Consistent Classification: Resource group tags are often more consistent than individual resource tags, allowing for reliable filtering of related resources.
- Improved Efficiency: Filtering reduces the amount of data synced from Azure, speeding up ingestion and creating a more focused software catalog.