Azure Search Nav
MicrosoftOfficialCreated by Microsoft
- Skill
- Tool integration
- Cloud and DevOps
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The azure-search-nav skill provides a PowerShell-based utility to generate direct deep links into the Azure portal. By leveraging the aks-search-direct-mid semantic search API, it allows users to translate natural-language queries into specific navigation paths for supported Microsoft.ContainerService, Microsoft.Kubernetes, and Microsoft.Compute resources. The tool handles authentication via Az.Accounts, parses resource identifiers, and constructs precise portal URLs, streamlining the process of locating specific configuration blades or menu items.
What it does
- Parses Azure portal URLs and ARM resource IDs to extract necessary context.
- Authenticates users via standard Azure PowerShell account modules.
- Queries the semantic search API to map natural language to portal navigation paths.
- Constructs and outputs direct deep links to specific resource blades.
- Supports extensibility for additional resource types via a configuration file.
- Provides options for both interactive browser-based and headless device-code authentication.
When to use it
- When you need to find the exact location of a setting or operation within the Azure portal.
- When you want to generate a shareable deep link for a specific resource blade.
- When working with supported Container Service, Arc-enabled Kubernetes, or Compute resources.
- When automating navigation tasks in a CLI or scripted environment.
Advantages
- Reduces manual navigation time within the complex Azure portal interface.
- Uses semantic search to interpret intent rather than requiring exact menu path knowledge.
- Integrates directly with existing Azure PowerShell authentication workflows.
- Provides a consistent, programmatic way to access deep links across different resource types.
Usage examples
- “Find the image cleaner settings for my AKS cluster.”
- The script queries the search API for 'image cleaner' relative to the provided cluster resource ID and returns a direct link to the corresponding portal blade.
- “Generate a portal link to the virtual machine scale set networking blade.”
- The tool parses the VMSS resource ID, queries the navigation index, and outputs a URL that opens the specific networking configuration page in the Azure portal.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/azure-search-nav/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip azure-search-nav.zip -d .agents/skills
Documentation by Microsoft
Azure Portal Search Link Builder
Quick Reference
| Item | Value |
|---|---|
| Script | references/Invoke-PortalSearchNav.ps1 |
| Resource map | references/resource-types.json |
| Prerequisite | Az.Accounts PowerShell module |
| Auth | Interactive Microsoft sign-in (Connect-AzAccount), or device-code with -UseDeviceAuthentication |
When to Use This Skill
User asks to find where in the Azure portal to perform an operation on a supported Container Service, Arc-enabled Kubernetes, or Compute resource.
User wants a portal deep link for a specific blade/menu item.
User provides a resource URL or ARM resource ID and a natural-language search query.
Workflow
Collect resource link (portal URL or bare ARM resource ID) and search query from the user.
Run
references/Invoke-PortalSearchNav.ps1 -ResourceUrl '<link>' -Query '<query>'.The script signs in (browser by default; pass
-UseDeviceAuthenticationin headless/CLI environments), calls the search API, and prints portal deep links always rooted athttps://portal.azure.com.
See references/README.md for full parameter reference and API details.
Error Handling
| Error | Cause | Fix |
|---|---|---|
| Resource type not enabled | armProvider not in references/resource-types.json | Confirm the type is in the enabled-provider list |
| 401 from API | Invalid token, or wrong tenant selected | Re-run and select the account with access to the target subscription's tenant |
| Empty results | No matching navigation item for the query | Try a different search term |
Az.Accounts not found | Module not installed | Install-Module Az.Accounts -Scope CurrentUser |
| Browser sign-in fails (no window handle) | Headless/CLI environment | Re-run with -UseDeviceAuthentication |
Files
- ATTRIBUTION.mdAdded by AutoGineer974 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md2.3 KiB
- references/Invoke-PortalSearchNav.ps1Script9.6 KiB
- references/README.md3.8 KiB
- references/resource-types.json4.3 KiB
references/Invoke-PortalSearchNav.ps1
<#
.SYNOPSIS
Calls the Azure portal search API for a supported Azure resource and constructs
the resulting Azure portal deep link from the returned menu id.
.DESCRIPTION
Given a portal link to a supported Container Service, Arc-enabled Kubernetes,
or Compute resource and a natural-language search query, this script:
1. Parses the ARM resource id and tenant id out of the resource link.
2. Signs in to Azure and acquires a bearer token.
3. POSTs a semantic search to the aks-search-direct-mid endpoint.
4. Extracts the menu id(s) from the response and rebuilds the portal link
against https://portal.azure.com.
.PARAMETER ResourceUrl
A portal URL (or bare ARM resource id) pointing at a supported Azure resource.
.PARAMETER Query
The natural-language search query, e.g. "image cleaner".
.PARAMETER ArmProvider
Override for the armProvider filter value. Auto-derived from ResourceUrl when omitted.
.PARAMETER AppTenantId
Value for the App-Tenant-Id header. Auto-derived from the URL or the signed-in token.
.PARAMETER Locale
Value for the locale filter. Default "en.en-us".
.PARAMETER UseDeviceAuthentication
Use device-code authentication instead of opening an interactive browser window.
.PARAMETER Raw
When set, only prints the raw JSON API response (skip link construction).
.EXAMPLE
./Invoke-PortalSearchNav.ps1 -ResourceUrl $aksPortalLink -Query 'image cleaner'
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $ResourceUrl,
[Parameter(Mandatory = $true)]
[string] $Query,
[string] $ArmProvider,
[string] $AppTenantId,
[string] $Locale = 'en.en-us',
[switch] $UseDeviceAuthentication,
[switch] $Raw
)
$ErrorActionPreference = 'Stop'
# Pinned production endpoint and token audience. Not exposed as parameters.
$ApiUrl = 'https://PCNX-AI-etc5bra3fscchrew.b02.azurefd.net/aks-search-direct-mid'
$TokenResourceUrl = 'https://management.azure.com/'
# ---------------------------------------------------------------------------
# 1. Parse the resource link.
# ---------------------------------------------------------------------------
$resourcePattern = '(?i)^(?<prefix>.*?)/resource/(?<resourceId>subscriptions/(?<sub>[^/]+)/resourceGroups/(?<rg>[^/]+)/providers/(?<provider>[^/]+)/(?<type>[^/]+)/(?<name>[^/?#]+))(?<trailing>/[^?#]*)?'
$match = [regex]::Match($ResourceUrl, $resourcePattern)
if (-not $match.Success) {
$resourcePattern = '(?i)^/?(?<resourceId>subscriptions/(?<sub>[^/]+)/resourceGroups/(?<rg>[^/]+)/providers/(?<provider>[^/]+)/(?<type>[^/]+)/(?<name>[^/?#]+))/?$'
$match = [regex]::Match($ResourceUrl, $resourcePattern)
}
if (-not $match.Success) {
throw "Could not parse a resource id out of -ResourceUrl. Expected a portal resource URL or '/subscriptions/.../resourceGroups/.../providers/<provider>/<type>/<name>'."
}
$resourceId = '/' + $match.Groups['resourceId'].Value
$subscriptionId = $match.Groups['sub'].Value
$providerNs = $match.Groups['provider'].Value
$resourceType = $match.Groups['type'].Value
if (-not $ArmProvider) {
$ArmProvider = "$providerNs/$resourceType"
}
# Validate and normalise armProvider casing from resource-types.json.
$resourceTypesPath = Join-Path $PSScriptRoot 'resource-types.json'
$resourceTypes = Get-Content $resourceTypesPath -Raw | ConvertFrom-Json
$armKey = $ArmProvider.ToLowerInvariant()
$entry = $resourceTypes.PSObject.Properties[$armKey]
if ($entry) {
$ArmProvider = $entry.Value.armProvider
}
else {
throw "Resource type '$ArmProvider' is not enabled for portal navigation search."
}
if (-not $AppTenantId) {
$tenantMatch = [regex]::Match($ResourceUrl, '#@(?<tenant>[^/]+)')
if ($tenantMatch.Success) {
$AppTenantId = $tenantMatch.Groups['tenant'].Value
}
}
# Output links are always built against the production portal host.
$prefix = if ($AppTenantId) { "https://portal.azure.com/#@$AppTenantId" } else { 'https://portal.azure.com' }
# ---------------------------------------------------------------------------
# 2. Sign in to Azure and acquire the API token.
# ---------------------------------------------------------------------------
if (-not (Get-Module -ListAvailable -Name Az.Accounts)) {
throw "The Az.Accounts PowerShell module is required. Install it with 'Install-Module Az.Accounts -Scope CurrentUser'."
}
Import-Module Az.Accounts
$connectParameters = @{ SkipContextPopulation = $true }
if ($AppTenantId) { $connectParameters['Tenant'] = $AppTenantId }
if ($subscriptionId) { $connectParameters['Subscription'] = $subscriptionId }
if ($UseDeviceAuthentication) { $connectParameters['UseDeviceAuthentication'] = $true }
Write-Host 'Sign in with the Microsoft account that has access to this Azure resource.' -ForegroundColor Cyan
Connect-AzAccount @connectParameters | Out-Null
$tokenParameters = @{ ResourceUrl = $TokenResourceUrl }
$azTenant = (Get-AzContext).Tenant.Id
if ($azTenant) { $tokenParameters['TenantId'] = $azTenant }
$accessToken = Get-AzAccessToken @tokenParameters
if ($accessToken.Token -is [System.Security.SecureString]) {
$tokenCredential = [System.Net.NetworkCredential]::new('', $accessToken.Token)
$BearerToken = $tokenCredential.Password
}
else {
$BearerToken = [string] $accessToken.Token
}
if ([string]::IsNullOrWhiteSpace($BearerToken)) {
throw "Azure sign-in succeeded, but no access token was returned for '$TokenResourceUrl'."
}
# The API's APIM issuer check requires App-Tenant-Id to be the GUID that
# issued the token, not the domain from the URL.
try {
$jwtParts = $BearerToken.Split('.')
$payloadSeg = $jwtParts[1].Replace('-','+').Replace('_','/')
switch ($payloadSeg.Length % 4) { 2 { $payloadSeg += '==' } 3 { $payloadSeg += '=' } }
$claims = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($payloadSeg)) | ConvertFrom-Json
if ($claims.tid) { $AppTenantId = $claims.tid }
} catch {
Write-Verbose "Could not decode tid from JWT: $_"
}
# ---------------------------------------------------------------------------
# 3. Build and send the request.
# ---------------------------------------------------------------------------
$payload = [ordered]@{
search = $Query
count = 'true'
filter = "armProvider eq '$ArmProvider' and locale eq '$Locale'"
queryType = 'semantic'
semanticConfiguration = 'semantic'
}
$headers = @{
'Accept' = '*/*'
'User-Data-Boundary' = 'Global'
}
$headers['Authorization'] = "Bearer $BearerToken"
if ($AppTenantId) {
$headers['App-Tenant-Id'] = $AppTenantId
}
Write-Verbose "POST $ApiUrl"
Write-Verbose ($payload | ConvertTo-Json -Depth 10)
$response = Invoke-RestMethod -Uri $ApiUrl -Method Post -Headers $headers -ContentType 'application/json' -Body ($payload | ConvertTo-Json -Depth 10)
if ($Raw) {
$response | ConvertTo-Json -Depth 20
return
}
# ---------------------------------------------------------------------------
# 3. Find menu id(s) in the response (schema-tolerant recursive search).
# ---------------------------------------------------------------------------
function Find-MenuIdCandidates {
param(
[Parameter(Mandatory)] $Object,
[string[]] $Names = @('menuId', 'menuID', 'menu_id', 'bladeName', 'blade')
)
$found = New-Object System.Collections.Generic.List[object]
function Recurse($obj) {
if ($null -eq $obj) { return }
if ($obj -is [string]) { return }
if ($obj -is [System.Collections.IEnumerable]) {
foreach ($item in $obj) { Recurse $item }
return
}
if ($obj -is [System.Management.Automation.PSCustomObject]) {
$menuValue = $null
foreach ($name in $Names) {
$prop = $obj.PSObject.Properties[$name]
if ($prop -and -not [string]::IsNullOrWhiteSpace([string]$prop.Value)) {
$menuValue = $prop.Value
break
}
}
if ($menuValue) {
$title = $null
foreach ($titleName in @('title', 'name', 'displayName', 'label')) {
$titleProp = $obj.PSObject.Properties[$titleName]
if ($titleProp) { $title = $titleProp.Value; break }
}
$score = $null
$scoreProp = $obj.PSObject.Properties['score']
if ($scoreProp) { $score = $scoreProp.Value }
$found.Add([PSCustomObject]@{ MenuId = $menuValue; Title = $title; Score = $score })
}
foreach ($prop in $obj.PSObject.Properties) {
Recurse $prop.Value
}
}
}
Recurse $Object
return $found
}
$candidates = Find-MenuIdCandidates -Object $response
if (-not $candidates -or $candidates.Count -eq 0) {
Write-Warning 'No menu id found in the API response with the known property names (menuId, menuID, menu_id, bladeName, blade). Dumping raw response for inspection:'
$response | ConvertTo-Json -Depth 20
return
}
# ---------------------------------------------------------------------------
# 4. Rebuild the portal link for every candidate menu id.
# ---------------------------------------------------------------------------
$results = foreach ($candidate in $candidates) {
[PSCustomObject]@{
Title = $candidate.Title
Score = $candidate.Score
MenuId = $candidate.MenuId
PortalLink = "$prefix/resource$resourceId/$($candidate.MenuId)"
}
}
$results | Format-Table -AutoSize -Wrap
Write-Host ''
Write-Host 'Best match portal link:' -ForegroundColor Green
Write-Host $results[0].PortalLink
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/aks-skills/skills/azure-search-nav · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from .github/plugins/aks-skills/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-search-nav.zip