Manage your Azure Virtual Machines
This guide demonstrates how to bring your Azure Virtual Machine management experience into Port. You will learn how to:
- Ingest Azure Virtual Machine data into Port's software catalog using Port's Azure integration.
- Set up self-service actions to manage Azure Virtual Machines (start, deallocate, and restart VMs).
 
Common use casesโ
- Monitor the status and health of all Azure Virtual Machines across subscriptions from a single view.
- Empower platform teams to automate VM lifecycle management via GitHub workflows.
Prerequisitesโ
This guide assumes the following:
- You have a Port account and have completed the onboarding process.
- Port's Azure integration is installed in your account.
We recommend creating a dedicated repository for the workflows that are used by Port actions.
Set up data modelโ
When installing the Azure integration in Port, the Azure Subscription and Azure Resource Group blueprints are created by default.
However, the Virtual Machine blueprint is not created automatically so we will need to create it manually.
Create the Virtual Machine blueprintโ
- 
Go to the Builder page of your portal. 
- 
Click on + Blueprint.
- 
Click on the {...}button in the top right corner, and chooseEdit JSON.
- 
Add this JSON schema: Azure Virtual Machine blueprint (Click to expand){
 "identifier": "azureVirtualMachine",
 "description": "This blueprint represents an Azure Virtual Machine in our software catalog",
 "title": "Virtual Machine",
 "icon": "Azure",
 "schema": {
 "properties": {
 "location": {
 "title": "Location",
 "type": "string"
 },
 "provisioningState": {
 "title": "Provisioning State",
 "type": "string"
 },
 "vmSize": {
 "title": "VM Size",
 "type": "string"
 },
 "osDiskName": {
 "title": "OS Disk Name",
 "type": "string"
 },
 "osType": {
 "title": "OS Type",
 "type": "string"
 },
 "osDiskCaching": {
 "title": "OS Disk Caching",
 "type": "string"
 },
 "osDiskSizeGB": {
 "title": "OS Disk Size GB",
 "type": "number"
 },
 "osDiskCreateOption": {
 "title": "OS Disk Create Option",
 "type": "string"
 },
 "networkInterfaceIds": {
 "title": "Network Interface IDs",
 "type": "array"
 },
 "licenseType": {
 "title": "License Type",
 "type": "string"
 },
 "vmOsProfile": {
 "title": "VM OS Profile",
 "type": "object"
 },
 "vmHardwareProfile": {
 "title": "VM Hardware Profile",
 "type": "object"
 },
 "vmStorageProfile": {
 "title": "VM Storage Profile",
 "type": "object"
 },
 "tags": {
 "title": "Tags",
 "type": "object"
 }
 },
 "required": []
 },
 "mirrorProperties": {},
 "calculationProperties": {},
 "aggregationProperties": {},
 "relations": {
 "resourceGroup": {
 "title": "Resource Group",
 "target": "azureResourceGroup",
 "required": false,
 "many": false
 }
 }
 }
- 
Click Saveto create the blueprint.
Update the integration mappingโ
- 
Go to the Data Sources page of your portal. 
- 
Select the Azure integration. 
- 
Add the following YAML block into the editor to ingest Azure Virtual Machines from your Azure subscription: Azure integration configuration (Click to expand)deleteDependentEntities: true
 createMissingRelatedEntities: true
 enableMergeEntity: true
 resources:
 - kind: subscription
 selector:
 query: 'true'
 apiVersion: '2022-09-01'
 port:
 entity:
 mappings:
 identifier: .id
 title: .display_name
 blueprint: '"azureSubscription"'
 properties:
 tags: .tags
 - kind: Microsoft.Resources/resourceGroups
 selector:
 query: 'true'
 apiVersion: '2022-09-01'
 port:
 entity:
 mappings:
 identifier: >-
 .id | split("/") | .[3] |= ascii_downcase |.[4] |= ascii_downcase |
 join("/")
 title: .name
 blueprint: '"azureResourceGroup"'
 properties:
 location: .location
 provisioningState: .properties.provisioningState + .properties.provisioning_state
 tags: .tags
 relations:
 subscription: >-
 .id | split("/") | .[1] |= ascii_downcase |.[2] |= ascii_downcase
 | .[:3] |join("/")
 - kind: Microsoft.Compute/virtualMachines
 selector:
 query: 'true'
 apiVersion: '2023-03-01'
 port:
 entity:
 mappings:
 identifier: >-
 .id | split("/") | .[3] |= ascii_downcase |.[4] |= ascii_downcase |
 join("/")
 title: .name
 blueprint: '"azureVirtualMachine"'
 properties:
 location: .location
 provisioningState: .properties.provisioningState
 vmSize: .properties.hardwareProfile.vmSize
 osDiskName: .properties.storageProfile.osDisk.name
 osType: .properties.storageProfile.osDisk.osType
 osDiskCaching: .properties.storageProfile.osDisk.caching
 osDiskSizeGB: .properties.storageProfile.osDisk.diskSizeGB
 osDiskCreateOption: .properties.storageProfile.osDisk.createOption
 networkInterfaceIds: .properties.networkProfile.networkInterfaces[].id
 licenseType: .properties.licenseType
 vmOsProfile: .properties.osProfile
 vmHardwareProfile: .properties.hardwareProfile
 vmStorageProfile: .properties.storageProfile
 tags: .tags
 relations:
 resourceGroup: >-
 .id | split("/") | .[3] |= ascii_downcase | .[4] |= ascii_downcase
 | .[:5] |join("/")
- 
Click Save & Resyncto apply the mapping.
Set up self-service actionsโ
Now let us create self-service actions to manage your Azure Virtual Machines directly from Port using GitHub Actions. You will implement workflows to:
- Start an Azure Virtual Machine.
- Deallocate an Azure Virtual Machine.
- Restart an Azure Virtual Machine.
Add GitHub secretsโ
In your GitHub repository, go to Settings > Secrets and add the following secrets:
- PORT_CLIENT_ID- Port Client ID learn more.
- PORT_CLIENT_SECRET- Port Client Secret learn more.
- AZURE_CLIENT_ID- Azure service principal client ID.
- AZURE_CLIENT_SECRET- Azure service principal client secret.
- AZURE_TENANT_ID- Azure tenant ID.
The Azure service principal must have the following permissions to manage Virtual Machines:
- Microsoft.Compute/virtualMachines/start/action
- Microsoft.Compute/virtualMachines/powerOff/action
- Microsoft.Compute/virtualMachines/restart/action
- Microsoft.Compute/virtualMachines/read
Alternatively, you can assign the Virtual Machine Contributor built-in role which includes these permissions.
Start an Azure Virtual Machineโ
Add GitHub workflow
Create the file .github/workflows/start-azure-vm.yaml in the .github/workflows folder of your repository.
Start Azure Virtual Machine GitHub workflow (Click to expand)
name: Start Azure Virtual Machine
on:
  workflow_dispatch:
    inputs:
      port_context:
        required: true
        description: 'Action and general context (blueprint, entity, run id, etc...)'
        type: string
jobs:
  start-vm:
    runs-on: ubuntu-latest
    steps:
      - name: Inform Port of workflow start
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{fromJson(inputs.port_context).runId}}
          logMessage: Configuring Azure credentials to start VM ${{ fromJson(inputs.port_context).entity.title }}
      - name: Azure Login
        uses: azure/login@v2
        env:
          AZURE_LOGIN_PRE_CLEANUP: true
          AZURE_LOGIN_POST_CLEANUP: true
        with:
          creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ fromJson(inputs.port_context).subscription_id }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
      - name: Start Azure Virtual Machine
        run: |
          az vm start --name ${{ fromJson(inputs.port_context).entity.title }} --resource-group ${{ fromJson(inputs.port_context).resource_group }}
      - name: Inform Port about VM start success
        if: success()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'SUCCESS'
          logMessage: โ
 Azure VM ${{ fromJson(inputs.port_context).entity.title }} started successfully
          summary: Azure VM started successfully
      - name: Inform Port about VM start failure
        if: failure()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'FAILURE'
          logMessage: โ Failed to start Azure VM ${{ fromJson(inputs.port_context).entity.title }}
          summary: Azure VM start failed
Create Port action
- 
Go to the Self-service page of your portal. 
- 
Click on the + New Actionbutton.
- 
Click on the {...} Edit JSONbutton.
- 
Copy and paste the following JSON configuration into the editor. Start Azure Virtual Machine action (Click to expand)Modification RequiredMake sure to replace <GITHUB_ORG>and<GITHUB_REPO>with your GitHub organization and repository names respectively.{
 "identifier": "start_azure_vm",
 "title": "Start Azure Virtual Machine",
 "icon": "Azure",
 "description": "Starts an Azure Virtual Machine",
 "trigger": {
 "type": "self-service",
 "operation": "DAY-2",
 "userInputs": {
 "properties": {},
 "required": [],
 "order": []
 },
 "blueprintIdentifier": "azureVirtualMachine"
 },
 "invocationMethod": {
 "type": "GITHUB",
 "org": "<GITHUB-ORG>",
 "repo": "<GITHUB-REPO>",
 "workflow": "start-azure-vm.yaml",
 "workflowInputs": {
 "port_context": {
 "runId": "{{ .run.id }}",
 "entity": "{{ .entity }}",
 "subscription_id": "{{.entity.relations.resourceGroup | split(\"/\")[2]}}",
 "resource_group": "{{.entity.relations.resourceGroup | split(\"/\")[-1]}}"
 }
 },
 "reportWorkflowStatus": true
 },
 "requiredApproval": false
 }
- 
Click Save.
Deallocate an Azure Virtual Machineโ
Add GitHub workflow
Create the file .github/workflows/deallocate-azure-vm.yaml in the .github/workflows folder of your repository.
Deallocate Azure Virtual Machine GitHub workflow (Click to expand)
name: Deallocate Azure Virtual Machine
on:
  workflow_dispatch:
    inputs:
      port_context:
        required: true
        description: 'Action and general context (blueprint, entity, run id, etc...)'
        type: string
jobs:
  deallocate-vm:
    runs-on: ubuntu-latest
    steps:
      - name: Inform Port of workflow stop
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{fromJson(inputs.port_context).runId}}
          logMessage: Configuring Azure credentials to deallocate VM ${{ fromJson(inputs.port_context).entity.title }}
      - name: Azure Login
        uses: azure/login@v2
        env:
          AZURE_LOGIN_PRE_CLEANUP: true
          AZURE_LOGIN_POST_CLEANUP: true
        with:
          creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ fromJson(inputs.port_context).subscription_id }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
      - name: Deallocate Azure Virtual Machine
        run: |
          az vm deallocate --name ${{ fromJson(inputs.port_context).entity.title }} --resource-group ${{ fromJson(inputs.port_context).resource_group }}
      - name: Inform Port about VM deallocate success
        if: success()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'SUCCESS'
          logMessage: โ
 Azure VM ${{ fromJson(inputs.port_context).entity.title }} deallocated successfully
          summary: Azure VM deallocated successfully
      - name: Inform Port about VM deallocate failure
        if: failure()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'FAILURE'
          logMessage: โ Failed to deallocate Azure VM ${{ fromJson(inputs.port_context).entity.title }}
          summary: Azure VM deallocate failed
Create Port action
- 
Go to the Self-service page of your portal. 
- 
Click on the + New Actionbutton.
- 
Click on the {...} Edit JSONbutton.
- 
Copy and paste the following JSON configuration into the editor. Deallocate Azure Virtual Machine action (Click to expand)Modification RequiredMake sure to replace <GITHUB_ORG>and<GITHUB_REPO>with your GitHub organization and repository names respectively.{
 "identifier": "deallocate_azure_vm",
 "title": "Deallocate Azure Virtual Machine",
 "icon": "Azure",
 "description": "Deallocates an Azure Virtual Machine",
 "trigger": {
 "type": "self-service",
 "operation": "DAY-2",
 "userInputs": {
 "properties": {},
 "required": [],
 "order": []
 },
 "blueprintIdentifier": "azureVirtualMachine"
 },
 "invocationMethod": {
 "type": "GITHUB",
 "org": "<GITHUB-ORG>",
 "repo": "<GITHUB-REPO>",
 "workflow": "deallocate-azure-vm.yaml",
 "workflowInputs": {
 "port_context": {
 "runId": "{{ .run.id }}",
 "entity": "{{ .entity }}",
 "subscription_id": "{{.entity.relations.resourceGroup | split(\"/\")[2]}}",
 "resource_group": "{{.entity.relations.resourceGroup | split(\"/\")[-1]}}"
 }
 },
 "reportWorkflowStatus": true
 },
 "requiredApproval": false
 }
- 
Click Save.
Restart an Azure Virtual Machineโ
Add GitHub workflow
Create the file .github/workflows/restart-azure-vm.yaml in the .github/workflows folder of your repository.
Restart Azure Virtual Machine GitHub workflow (Click to expand)
name: Restart Azure Virtual Machine
on:
  workflow_dispatch:
    inputs:
      port_context:
        required: true
        description: 'Action and general context (blueprint, entity, run id, etc...)'
        type: string
jobs:
  restart-vm:
    runs-on: ubuntu-latest
    steps:
      - name: Inform Port of workflow restart
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{fromJson(inputs.port_context).runId}}
          logMessage: Configuring Azure credentials to restart VM ${{ fromJson(inputs.port_context).entity.title }}
      - name: Azure Login
        uses: azure/login@v2
        env:
          AZURE_LOGIN_PRE_CLEANUP: true
          AZURE_LOGIN_POST_CLEANUP: true
        with:
          creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ fromJson(inputs.port_context).subscription_id }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
      - name: Restart Azure Virtual Machine
        run: |
          az vm restart --name ${{ fromJson(inputs.port_context).entity.title }} --resource-group ${{ fromJson(inputs.port_context).resource_group }}
      - name: Inform Port about VM restart success
        if: success()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'SUCCESS'
          logMessage: โ
 Azure VM ${{ fromJson(inputs.port_context).entity.title }} restarted successfully
          summary: Azure VM restarted successfully
      - name: Inform Port about VM restart failure
        if: failure()
        uses: port-labs/port-github-action@v1
        with:
          clientId: ${{ secrets.PORT_CLIENT_ID }}
          clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
          baseUrl: https://api.getport.io
          operation: PATCH_RUN
          runId: ${{ fromJson(inputs.port_context).runId }}
          status: 'FAILURE'
          logMessage: โ Failed to restart Azure VM ${{ fromJson(inputs.port_context).entity.title }}
          summary: Azure VM restart failed
Create Port action
- 
Go to the Self-service page of your portal. 
- 
Click on the + New Actionbutton.
- 
Click on the {...} Edit JSONbutton.
- 
Copy and paste the following JSON configuration into the editor. Restart Azure Virtual Machine action (Click to expand)Modification RequiredMake sure to replace <GITHUB_ORG>and<GITHUB_REPO>with your GitHub organization and repository names respectively.{
 "identifier": "restart_azure_vm",
 "title": "Restart Azure Virtual Machine",
 "icon": "Azure",
 "description": "Restarts an Azure Virtual Machine",
 "trigger": {
 "type": "self-service",
 "operation": "DAY-2",
 "userInputs": {
 "properties": {},
 "required": [],
 "order": []
 },
 "blueprintIdentifier": "azureVirtualMachine"
 },
 "invocationMethod": {
 "type": "GITHUB",
 "org": "<GITHUB-ORG>",
 "repo": "<GITHUB-REPO>",
 "workflow": "restart-azure-vm.yaml",
 "workflowInputs": {
 "port_context": {
 "runId": "{{ .run.id }}",
 "entity": "{{ .entity }}",
 "subscription_id": "{{.entity.relations.resourceGroup | split(\"/\")[2]}}",
 "resource_group": "{{.entity.relations.resourceGroup | split(\"/\")[-1]}}"
 }
 },
 "reportWorkflowStatus": true
 },
 "requiredApproval": false
 }
- 
Click Save.