Compare commits

...

7 Commits

Author SHA1 Message Date
066033bd50 docs(neovim): fix broken rpc.connect example, document stdio-bridge fix
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m30s
2026-08-14 17:05:03 +02:00
71738d856e Fix: include azure_roles.json in wheel package data
The pyproject.toml packaging config only included .py files, so
azure_roles.json (682 Azure role names) never made it into the built
wheel/Docker image. This silently broke the 'roles: [...]' array
completion feature in production even though the code and local dev
worked fine (repo files were always on disk locally).

Root-caused via a raw WebSocket JSON-RPC test against production that
returned 0 completion items for a roles array, despite the same logic
returning 682 items when exercised directly against the local module
in this repo.
2026-08-14 17:04:41 +02:00
4e957133d0 feat(catalog): sync 5 new Bicep modules; fix docs for broken /data hot-reload
All checks were successful
Build and Deploy iLSP / test (push) Successful in 25s
Build and Deploy iLSP / build-and-deploy (push) Successful in 3m13s
- Refreshed bicep_modules_catalog.json from ACR (27 -> 32 modules): adds
  elasticpool, managedidentity, signalr, testmodule, virtualnetworks.
- Corrected README/EDITOR_SETUP: production ilsp.nomad has no /data volume
  mount (the Nomad client disables raw Docker bind mounts), so
  scripts/push_catalogs.sh + POST /reload silently no-op in prod. Catalog
  updates require commit + push (Gitea Actions rebuild + Nomad redeploy).
  /data hot-reload still works for local dev via `make run-with-data`.
2026-08-14 12:04:33 +02:00
Henrik Jess Nielsen
b8eb1d8085 feat(bicep): add all 682 Azure roles from rbaclookup module
All checks were successful
Build and Deploy iLSP / test (push) Successful in 23s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m32s
Replaces hardcoded 38 roles with complete list extracted from
bicep/lookup/rbaclookup:2.x module.

Changes:
- Add scripts/extract_roles_from_rbaclookup.py to parse rbacLookup.bicep
- Generate ilsp/bicep_lsp/azure_roles.json with 682 role names
- Load roles dynamically in modules.py from JSON file
- Now supports ALL Azure built-in roles for autocomplete

Benefits:
- Complete Azure RBAC coverage (682 vs 38 roles)
- Easy to update when new roles are added to rbaclookup module
- Cleaner code (no giant hardcoded list in modules.py)

Usage to update roles:
  python3 scripts/extract_roles_from_rbaclookup.py /path/to/rbacLookup.bicep

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-19 14:27:00 +02:00
Henrik Jess Nielsen
578f88a0e8 fix(bicep): support multi-line array completion for roles and other enums
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m35s
Adds support for autocomplete in multi-line array syntax like:

    roles: [
      'KEY_VAULT_  ← cursor triggers completion here
    ]

Previously only worked on same line as opening bracket:
    roles: ['KEY_VAULT_  ← only this worked

Changes:
- Walk backwards up to 10 lines to find array opening (e.g. "roles: [")
- Detect if cursor is inside array based on indentation and quotes
- Stop lookback if closing bracket found (not in array anymore)
- Add test case for nested multi-line array completion
- Improve lsp_bridge.py error handling with traceback logging
- Add lsp_bridge_debug.sh wrapper for easier IntelliJ debugging
- Update EDITOR_SETUP.md with correct IntelliJ LSP4IJ config

Fixes autocomplete for deeply nested structures like:
    assignments: [{ roles: ['APP_CONFIGURATION_...'] }]

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-19 13:51:17 +02:00
Henrik Jess Nielsen
d4cb9ae8fb Roles
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 48s
2026-05-19 11:46:54 +02:00
Henrik Jess Nielsen
aa37c259ad Roles
All checks were successful
Build and Deploy iLSP / test (push) Successful in 23s
Build and Deploy iLSP / build-and-deploy (push) Successful in 3m13s
2026-05-19 10:28:22 +02:00
11 changed files with 4268 additions and 48 deletions

View File

@@ -14,17 +14,49 @@ supports LSP WebSocket transport.
## IntelliJ IDEA setup (LSP4IJ) ## IntelliJ IDEA setup (LSP4IJ)
Install the [LSP4IJ](https://plugins.jetbrains.com/plugin/23257-lsp4ij) plugin, then: Install the [LSP4IJ](https://plugins.jetbrains.com/plugin/23257-lsp4ij) plugin, then add language servers:
### Bicep
1. **Settings → Languages & Frameworks → LSP → Language Servers → +** 1. **Settings → Languages & Frameworks → LSP → Language Servers → +**
- Name: `iLSP Bicep` - Name: `iLSP Bicep`
- Server type: `WebSocket` - Server type: `Command` (not WebSocket — see note below)
- URL: `wss://ilsp.i80.dk/bicep` - Command: `/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/bicep`
- Arguments: (leave blank)
- File pattern: `*.bicep;*.bicepparam`
2. **Add a file-type mapping** under the new server entry: ### YAML (Azure DevOps + GitHub Actions)
- File type: `Bicep` (or pattern `*.bicep`)
3. **Disable Azure Toolkit Bicep completions** — this is critical if you have the 2. **Settings → Languages & Frameworks → LSP → Language Servers → +**
- Name: `iLSP YAML`
- Server type: `Command`
- Command: `/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/yaml`
- Arguments: (leave blank)
- File pattern: `*.yaml;*.yml;azure-pipelines.yml`
### Python (optional)
3. **Settings → Languages & Frameworks → LSP → Language Servers → +**
- Name: `iLSP Python`
- Server type: `Command`
- Command: `/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/python`
- Arguments: (leave blank)
- File pattern: `*.py`
> **Note**: LSP4IJ's WebSocket mode doesn't handle LSP Content-Length framing correctly,
> causing "starting..." to hang or "Stream closed" errors. Use `lsp_bridge_debug.sh` wrapper
> which calls `lsp_bridge.py` and logs errors to `/tmp/lsp_bridge_debug.log` for debugging.
### Troubleshooting
If the language server doesn't start, check the debug log:
```bash
tail -f /tmp/lsp_bridge_debug.log
```
### Disable conflicting plugins
**Disable Azure Toolkit Bicep completions** — this is critical if you have the
Azure Toolkit plugin installed. Without this step, IDEA merges completions from Azure Toolkit plugin installed. Without this step, IDEA merges completions from
both LSP4IJ and Azure Toolkit, resulting in noisy suggestions (`resource`, both LSP4IJ and Azure Toolkit, resulting in noisy suggestions (`resource`,
`projectName`, Bicep schema types, etc.) appearing alongside iLSP results. `projectName`, Bicep schema types, etc.) appearing alongside iLSP results.
@@ -43,13 +75,20 @@ Install the [LSP4IJ](https://plugins.jetbrains.com/plugin/23257-lsp4ij) plugin,
## Neovim setup ## Neovim setup
> **Important**: `vim.lsp.rpc.connect(url)` does **not** speak WebSocket — it's a raw
> TCP/pipe connector and will fail with `ENOENT` on a `wss://` URL (verified). Use
> `cmd = { ".../scripts/lsp_bridge_debug.sh", "wss://..." }` instead (stdio ↔
> WebSocket bridge, same one IntelliJ/LSP4IJ uses). Confirmed working end-to-end
> (32 module completions returned) with the config below.
```lua ```lua
-- In your LSP config (e.g. ~/.config/nvim/lua/lsp.lua) -- In your LSP config (e.g. ~/.config/nvim/lua/lsp.lua)
local BRIDGE = "/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh"
-- Bicep -- Bicep
vim.lsp.start({ vim.lsp.start({
name = "ilsp-bicep", name = "ilsp-bicep",
cmd = vim.lsp.rpc.connect("wss://ilsp.i80.dk/bicep"), cmd = { BRIDGE, "wss://ilsp.i80.dk/bicep" },
root_dir = vim.fs.dirname(vim.fs.find({ "bicepconfig.json", ".git" }, { upward = true })[1]), root_dir = vim.fs.dirname(vim.fs.find({ "bicepconfig.json", ".git" }, { upward = true })[1]),
filetypes = { "bicep" }, filetypes = { "bicep" },
}) })
@@ -57,7 +96,7 @@ vim.lsp.start({
-- YAML (pipeline files) -- YAML (pipeline files)
vim.lsp.start({ vim.lsp.start({
name = "ilsp-yaml", name = "ilsp-yaml",
cmd = vim.lsp.rpc.connect("wss://ilsp.i80.dk/yaml"), cmd = { BRIDGE, "wss://ilsp.i80.dk/yaml" },
root_dir = vim.fs.dirname(vim.fs.find({ ".git" }, { upward = true })[1]), root_dir = vim.fs.dirname(vim.fs.find({ ".git" }, { upward = true })[1]),
filetypes = { "yaml" }, filetypes = { "yaml" },
}) })
@@ -65,7 +104,7 @@ vim.lsp.start({
-- Python -- Python
vim.lsp.start({ vim.lsp.start({
name = "ilsp-python", name = "ilsp-python",
cmd = vim.lsp.rpc.connect("wss://ilsp.i80.dk/python"), cmd = { BRIDGE, "wss://ilsp.i80.dk/python" },
root_dir = vim.fs.dirname(vim.fs.find({ "pyproject.toml", ".git" }, { upward = true })[1]), root_dir = vim.fs.dirname(vim.fs.find({ "pyproject.toml", ".git" }, { upward = true })[1]),
filetypes = { "python" }, filetypes = { "python" },
}) })
@@ -150,12 +189,20 @@ python3 scripts/sync_pipeline_templates.py # → pipeline_templates_catalog.js
python3 scripts/sync_iac_module_sources.py # → iac_source_catalog.json (from DevOpsMCP) python3 scripts/sync_iac_module_sources.py # → iac_source_catalog.json (from DevOpsMCP)
python3 scripts/sync_bicep_modules.py # → bicep_modules_catalog.json python3 scripts/sync_bicep_modules.py # → bicep_modules_catalog.json
# 2. Push and deploy # 2. Commit + push — Gitea Actions builds the image and Nomad redeploys it
bash scripts/push_catalogs.sh git add bicep_modules_catalog.json iac_source_catalog.json pipeline_templates_catalog.json
git commit -m "chore: refresh catalogs"
git push
``` ```
The `/reload` endpoint on the running service reloads catalogs from volume-mounted > **Note**: `scripts/push_catalogs.sh` and the `/reload` endpoint only take effect if
files if present, so you can update without a full redeploy for pipeline templates. > `/data` is volume-mounted into the running container. **Production (`ilsp.nomad`)
> currently has no `/data` mount** — the underlying Nomad client has raw Docker bind
> mounts disabled (`volumes are not enabled`), so `push_catalogs.sh` silently pushes
> to a path the container never sees, and `/reload` is a no-op there. `/data` only
> works for **local dev** via `make run-with-data`. In production, catalog updates
> always require a commit + redeploy (as above) until the client is configured with
> a proper Nomad `host_volume`.
--- ---

View File

@@ -31,9 +31,14 @@ make health # curl health endpoint
## Updating catalogs ## Updating catalogs
Catalogs are baked into the Docker image at build time — commit + push to update
production (see [EDITOR_SETUP.md](EDITOR_SETUP.md#updating-catalogs) for the full
workflow and a note on why `push_catalogs.sh` / `/reload` don't take effect in prod).
```bash ```bash
python3 scripts/sync_pipeline_templates.py # scan AzDO + GHA template repos python3 scripts/sync_pipeline_templates.py # scan AzDO + GHA template repos
bash scripts/push_catalogs.sh # scp to server + /reload python3 scripts/sync_bicep_modules.py # from DevOpsMCP repo — needs az CLI + ACR access
git add *_catalog.json && git commit -m "chore: refresh catalogs" && git push
``` ```
## Deploy ## Deploy

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,684 @@
[
"ACCESS_REVIEW_OPERATOR_SERVICE_ROLE",
"ACRDELETE",
"ACRIMAGESIGNER",
"ACRPULL",
"ACRPUSH",
"ACRQUARANTINEREADER",
"ACRQUARANTINEWRITER",
"ADVISOR_RECOMMENDATIONS_CONTRIBUTOR_ASSESSMENTS_AND_REVIEWS",
"ADVISOR_REVIEWS_CONTRIBUTOR",
"ADVISOR_REVIEWS_READER",
"AGENTLESS_SCANNING_FOR_SERVERLESS_SCANNER_SERVICE_ROLE",
"AGFOOD_PLATFORM_DATASET_ADMIN",
"AGFOOD_PLATFORM_SENSOR_PARTNER_CONTRIBUTOR",
"AGFOOD_PLATFORM_SERVICE_ADMIN",
"AGFOOD_PLATFORM_SERVICE_CONTRIBUTOR",
"AGFOOD_PLATFORM_SERVICE_READER",
"ANYBUILD_BUILDER",
"API_MANAGEMENT_DEVELOPER_PORTAL_CONTENT_EDITOR",
"API_MANAGEMENT_SERVICE_CONTRIBUTOR",
"API_MANAGEMENT_SERVICE_OPERATOR_ROLE",
"API_MANAGEMENT_SERVICE_READER_ROLE",
"API_MANAGEMENT_SERVICE_WORKSPACE_API_DEVELOPER",
"API_MANAGEMENT_SERVICE_WORKSPACE_API_PRODUCT_MANAGER",
"API_MANAGEMENT_WORKSPACE_API_DEVELOPER",
"API_MANAGEMENT_WORKSPACE_API_PRODUCT_MANAGER",
"API_MANAGEMENT_WORKSPACE_CONTRIBUTOR",
"API_MANAGEMENT_WORKSPACE_READER",
"APPGW_FOR_CONTAINERS_CONFIGURATION_MANAGER",
"APPLICATION_GROUP_CONTRIBUTOR",
"APPLICATION_INSIGHTS_COMPONENT_CONTRIBUTOR",
"APPLICATION_INSIGHTS_SNAPSHOT_DEBUGGER",
"APP_COMPLIANCE_AUTOMATION_ADMINISTRATOR",
"APP_COMPLIANCE_AUTOMATION_READER",
"APP_CONFIGURATION_CONTRIBUTOR",
"APP_CONFIGURATION_DATA_OWNER",
"APP_CONFIGURATION_DATA_READER",
"APP_CONFIGURATION_DATA_SAS_USER",
"APP_CONFIGURATION_READER",
"APP_SERVICE_ENVIRONMENT_CONTRIBUTOR",
"ARC_GATEWAY_MANAGER",
"ATTESTATION_CONTRIBUTOR",
"ATTESTATION_READER",
"AUTOMATION_CONTRIBUTOR",
"AUTOMATION_JOB_OPERATOR",
"AUTOMATION_OPERATOR",
"AUTOMATION_RUNBOOK_OPERATOR",
"AUTONOMOUS_DEVELOPMENT_PLATFORM_DATA_CONTRIBUTOR_PREVIEW",
"AUTONOMOUS_DEVELOPMENT_PLATFORM_DATA_OWNER_PREVIEW",
"AUTONOMOUS_DEVELOPMENT_PLATFORM_DATA_READER_PREVIEW",
"AUTO_ACTIONS_CONTRIBUTOR",
"AVERE_CONTRIBUTOR",
"AVERE_OPERATOR",
"AVS_ON_FLEET_VIS_ROLE",
"AVS_ORCHESTRATOR_ROLE",
"AZUREML_COMPUTE_OPERATOR",
"AZUREML_DATA_SCIENTIST",
"AZUREML_METRICS_WRITER_PREVIEW",
"AZUREML_REGISTRY_USER",
"AZURE_AI_ACCOUNT_OWNER",
"AZURE_AI_ADMINISTRATOR",
"AZURE_AI_DEVELOPER",
"AZURE_AI_ENTERPRISE_NETWORK_CONNECTION_APPROVER",
"AZURE_AI_INFERENCE_DEPLOYMENT_OPERATOR",
"AZURE_AI_PROJECT_MANAGER",
"AZURE_AI_SAFETY_EVALUATOR",
"AZURE_AI_USER",
"AZURE_API_CENTER_COMPLIANCE_MANAGER",
"AZURE_API_CENTER_CREDENTIAL_ACCESS_READER",
"AZURE_API_CENTER_DATA_READER",
"AZURE_API_CENTER_SERVICE_CONTRIBUTOR",
"AZURE_API_CENTER_SERVICE_READER",
"AZURE_ARC_ENABLED_KUBERNETES_CLUSTER_USER_ROLE",
"AZURE_ARC_KUBERNETES_ADMIN",
"AZURE_ARC_KUBERNETES_CLUSTER_ADMIN",
"AZURE_ARC_KUBERNETES_VIEWER",
"AZURE_ARC_KUBERNETES_WRITER",
"AZURE_ARC_SCVMM_ADMINISTRATOR_ROLE",
"AZURE_ARC_SCVMM_PRIVATE_CLOUDS_ONBOARDING",
"AZURE_ARC_SCVMM_PRIVATE_CLOUD_USER",
"AZURE_ARC_SCVMM_VM_CONTRIBUTOR",
"AZURE_ARC_VMWARE_ADMINISTRATOR_ROLE",
"AZURE_ARC_VMWARE_PRIVATE_CLOUDS_ONBOARDING",
"AZURE_ARC_VMWARE_PRIVATE_CLOUD_USER",
"AZURE_ARC_VMWARE_VM_CONTRIBUTOR",
"AZURE_AUTOMANAGE_CONTRIBUTOR",
"AZURE_BACKUP_SNAPSHOT_CONTRIBUTOR",
"AZURE_BATCH_ACCOUNT_CONTRIBUTOR",
"AZURE_BATCH_ACCOUNT_READER",
"AZURE_BATCH_DATA_CONTRIBUTOR",
"AZURE_BATCH_JOB_SUBMITTER",
"AZURE_BATCH_SERVICE_ORCHESTRATION_ROLE",
"AZURE_BOT_SERVICE_CONTRIBUTOR_ROLE",
"AZURE_CENTER_FOR_SAP_SOLUTIONS_ADMINISTRATOR",
"AZURE_CENTER_FOR_SAP_SOLUTIONS_MANAGEMENT_ROLE",
"AZURE_CENTER_FOR_SAP_SOLUTIONS_READER",
"AZURE_CENTER_FOR_SAP_SOLUTIONS_SERVICE_ROLE",
"AZURE_CENTER_FOR_SAP_SOLUTIONS_SERVICE_ROLE_FOR_MANAGEMENT",
"AZURE_CONNECTED_MACHINE_ONBOARDING",
"AZURE_CONNECTED_MACHINE_RESOURCE_ADMINISTRATOR",
"AZURE_CONNECTED_MACHINE_RESOURCE_MANAGER",
"AZURE_CONNECTED_SQL_SERVER_ONBOARDING",
"AZURE_CONTAINERAPPS_SESSION_EXECUTOR",
"AZURE_CONTAINER_INSTANCES_CONTRIBUTOR_ROLE",
"AZURE_CONTAINER_REGISTRY_SECURE_SUPPLY_CHAIN_OPERATOR_SERVICE_ROLE",
"AZURE_CONTAINER_STORAGE_CONTRIBUTOR",
"AZURE_CONTAINER_STORAGE_OPERATOR",
"AZURE_CONTAINER_STORAGE_OWNER",
"AZURE_CUSTOMER_LOCKBOX_APPROVER_FOR_SUBSCRIPTION",
"AZURE_DEPLOYMENT_STACK_CONTRIBUTOR",
"AZURE_DEPLOYMENT_STACK_OWNER",
"AZURE_DEVICE_ONBOARDING_DISCOVERY_CONTRIBUTOR",
"AZURE_DEVICE_UPDATE_AGENT",
"AZURE_DIGITAL_TWINS_DATA_OWNER",
"AZURE_DIGITAL_TWINS_DATA_READER",
"AZURE_EDGE_HARDWARE_CENTER_ADMINISTRATOR",
"AZURE_EDGE_ON_SITE_DEPLOYMENT_ENGINEER",
"AZURE_EVENT_HUBS_DATA_OWNER",
"AZURE_EVENT_HUBS_DATA_RECEIVER",
"AZURE_EVENT_HUBS_DATA_SENDER",
"AZURE_EXTENSION_FOR_SQL_SERVER_DEPLOYMENT",
"AZURE_FILE_SYNC_ADMINISTRATOR",
"AZURE_FILE_SYNC_READER",
"AZURE_FRONT_DOOR_DOMAIN_CONTRIBUTOR",
"AZURE_FRONT_DOOR_DOMAIN_READER",
"AZURE_FRONT_DOOR_PROFILE_READER",
"AZURE_FRONT_DOOR_SECRET_CONTRIBUTOR",
"AZURE_FRONT_DOOR_SECRET_READER",
"AZURE_HYBRID_DATABASE_ADMINISTRATOR_READ_ONLY_SERVICE_ROLE",
"AZURE_IMPACT_INSIGHT_READER",
"AZURE_KUBERNETES_FLEET_MANAGER_CONTRIBUTOR_ROLE",
"AZURE_KUBERNETES_FLEET_MANAGER_HUB_AGENT_ROLE",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_ADMIN",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_CLUSTER_ADMIN",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_CLUSTER_READER",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_CLUSTER_WRITER",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_READER",
"AZURE_KUBERNETES_FLEET_MANAGER_RBAC_WRITER",
"AZURE_KUBERNETES_SERVICE_AGENT_POOL_MANAGER_ROLE",
"AZURE_KUBERNETES_SERVICE_ARC_CLUSTER_ADMIN_ROLE",
"AZURE_KUBERNETES_SERVICE_ARC_CLUSTER_USER_ROLE",
"AZURE_KUBERNETES_SERVICE_ARC_CONTRIBUTOR_ROLE",
"AZURE_KUBERNETES_SERVICE_CLUSTER_ADMIN_ROLE",
"AZURE_KUBERNETES_SERVICE_CLUSTER_MONITORING_USER",
"AZURE_KUBERNETES_SERVICE_CLUSTER_USER_ROLE",
"AZURE_KUBERNETES_SERVICE_CONTRIBUTOR_ROLE",
"AZURE_KUBERNETES_SERVICE_HYBRID_CLUSTER_ADMIN_ROLE",
"AZURE_KUBERNETES_SERVICE_HYBRID_CLUSTER_USER_ROLE",
"AZURE_KUBERNETES_SERVICE_HYBRID_CONTRIBUTOR_ROLE",
"AZURE_KUBERNETES_SERVICE_POLICY_ADD_ON_DEPLOYMENT",
"AZURE_KUBERNETES_SERVICE_RBAC_ADMIN",
"AZURE_KUBERNETES_SERVICE_RBAC_CLUSTER_ADMIN",
"AZURE_KUBERNETES_SERVICE_RBAC_READER",
"AZURE_KUBERNETES_SERVICE_RBAC_WRITER",
"AZURE_MACHINE_LEARNING_WORKSPACE_CONNECTION_SECRETS_READER",
"AZURE_MANAGED_GRAFANA_WORKSPACE_CONTRIBUTOR",
"AZURE_MAPS_CONTRIBUTOR",
"AZURE_MAPS_DATA_CONTRIBUTOR",
"AZURE_MAPS_DATA_READER",
"AZURE_MAPS_DATA_READ_AND_BATCH_ROLE",
"AZURE_MAPS_SEARCH_AND_RENDER_DATA_READER",
"AZURE_MESSAGING_CATALOG_DATA_OWNER",
"AZURE_MESSAGING_CONNECTORS_OWNER",
"AZURE_MONITOR_DASHBOARDS_WITH_GRAFANA_CONTRIBUTOR",
"AZURE_PROGRAMMABLE_CONNECTIVITY_GATEWAY_DATAPLANE_USER",
"AZURE_PROGRAMMABLE_CONNECTIVITY_GATEWAY_USER",
"AZURE_RED_HAT_OPENSHIFT_CLOUD_CONTROLLER_MANAGER",
"AZURE_RED_HAT_OPENSHIFT_CLUSTER_INGRESS_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_DISK_STORAGE_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_FEDERATED_CREDENTIAL",
"AZURE_RED_HAT_OPENSHIFT_FILE_STORAGE_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_HOSTED_CONTROL_PLANES_CLUSTER_API_PROVIDER",
"AZURE_RED_HAT_OPENSHIFT_HOSTED_CONTROL_PLANES_CONTROL_PLANE_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_HOSTED_CONTROL_PLANES_SERVICE_MANAGED_IDENTITY",
"AZURE_RED_HAT_OPENSHIFT_IMAGE_REGISTRY_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_MACHINE_API_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_NETWORK_OPERATOR",
"AZURE_RED_HAT_OPENSHIFT_SERVICE_OPERATOR",
"AZURE_RELAY_LISTENER",
"AZURE_RELAY_OWNER",
"AZURE_RELAY_SENDER",
"AZURE_RESOURCE_BRIDGE_DEPLOYMENT_ROLE",
"AZURE_RESOURCE_NOTIFICATIONS_SYSTEM_TOPICS_SUBSCRIBER",
"AZURE_SERVICE_BUS_DATA_OWNER",
"AZURE_SERVICE_BUS_DATA_RECEIVER",
"AZURE_SERVICE_BUS_DATA_SENDER",
"AZURE_SPHERE_CONTRIBUTOR",
"AZURE_SPHERE_OWNER",
"AZURE_SPHERE_PUBLISHER",
"AZURE_SPHERE_READER",
"AZURE_SPRING_APPS_APPLICATION_CONFIGURATION_SERVICE_CONFIG_FILE_PATTERN_READER_ROLE",
"AZURE_SPRING_APPS_APPLICATION_CONFIGURATION_SERVICE_LOG_READER_ROLE",
"AZURE_SPRING_APPS_CONNECT_ROLE",
"AZURE_SPRING_APPS_JOB_EXECUTION_INSTANCE_LIST_ROLE",
"AZURE_SPRING_APPS_JOB_LOG_READER_ROLE",
"AZURE_SPRING_APPS_MANAGED_COMPONENTS_LOG_READER_ROLE",
"AZURE_SPRING_APPS_REMOTE_DEBUGGING_ROLE",
"AZURE_SPRING_APPS_SPRING_CLOUD_CONFIG_SERVER_LOG_READER_ROLE",
"AZURE_SPRING_APPS_SPRING_CLOUD_GATEWAY_LOG_READER_ROLE",
"AZURE_SPRING_CLOUD_CONFIG_SERVER_CONTRIBUTOR",
"AZURE_SPRING_CLOUD_CONFIG_SERVER_READER",
"AZURE_SPRING_CLOUD_DATA_READER",
"AZURE_SPRING_CLOUD_SERVICE_REGISTRY_CONTRIBUTOR",
"AZURE_SPRING_CLOUD_SERVICE_REGISTRY_READER",
"AZURE_STACK_HCI_ADMINISTRATOR",
"AZURE_STACK_HCI_CONNECTED_INFRAVMS",
"AZURE_STACK_HCI_DEVICE_MANAGEMENT_ROLE",
"AZURE_STACK_HCI_EDGE_MACHINE_CONTRIBUTOR_ROLE",
"AZURE_STACK_HCI_VM_CONTRIBUTOR",
"AZURE_STACK_HCI_VM_READER",
"AZURE_STACK_REGISTRATION_OWNER",
"AZURE_USAGE_BILLING_DATA_SENDER",
"AZURE_VM_MANAGED_IDENTITIES_RESTORE_CONTRIBUTOR",
"BACKUP_CONTRIBUTOR",
"BACKUP_MUA_ADMIN",
"BACKUP_MUA_OPERATOR",
"BACKUP_OPERATOR",
"BACKUP_READER",
"BAYER_AG_POWERED_SERVICES_CROP_ID_SOLUTION_USER_ROLE",
"BAYER_AG_POWERED_SERVICES_CWUM_SOLUTION",
"BAYER_AG_POWERED_SERVICES_FIELD_IMAGERY_SOLUTION_SERVICE_ROLE",
"BAYER_AG_POWERED_SERVICES_GDU_SOLUTION",
"BAYER_AG_POWERED_SERVICES_HISTORICAL_WEATHER_DATA_SOLUTION_USER_ROLE",
"BAYER_AG_POWERED_SERVICES_IMAGERY_SOLUTION",
"BAYER_AG_POWERED_SERVICES_SMART_BOUNDARY_SOLUTION_USER_ROLE",
"BILLING_READER",
"BIZTALK_CONTRIBUTOR",
"BLOCKCHAIN_MEMBER_NODE_ACCESS_PREVIEW",
"BLUEPRINT_CONTRIBUTOR",
"BLUEPRINT_OPERATOR",
"CARBON_OPTIMIZATION_READER",
"CDN_ENDPOINT_CONTRIBUTOR",
"CDN_ENDPOINT_READER",
"CDN_PROFILE_CONTRIBUTOR",
"CDN_PROFILE_READER",
"CHAOS_STUDIO_EXPERIMENT_CONTRIBUTOR",
"CHAOS_STUDIO_OPERATOR",
"CHAOS_STUDIO_READER",
"CHAOS_STUDIO_TARGET_CONTRIBUTOR",
"CLASSIC_NETWORK_CONTRIBUTOR",
"CLASSIC_STORAGE_ACCOUNT_CONTRIBUTOR",
"CLASSIC_STORAGE_ACCOUNT_KEY_OPERATOR_SERVICE_ROLE",
"CLASSIC_VIRTUAL_MACHINE_CONTRIBUTOR",
"CLEARDB_MYSQL_DB_CONTRIBUTOR",
"CLOUDTEST_CONTRIBUTOR_ROLE",
"COGNITIVE_SEARCH_SERVERLESS_DATA_CONTRIBUTOR_DEPRECATED",
"COGNITIVE_SEARCH_SERVERLESS_DATA_READER_DEPRECATED",
"COGNITIVE_SERVICES_CONTRIBUTOR",
"COGNITIVE_SERVICES_CUSTOM_VISION_CONTRIBUTOR",
"COGNITIVE_SERVICES_CUSTOM_VISION_DEPLOYMENT",
"COGNITIVE_SERVICES_CUSTOM_VISION_LABELER",
"COGNITIVE_SERVICES_CUSTOM_VISION_READER",
"COGNITIVE_SERVICES_CUSTOM_VISION_TRAINER",
"COGNITIVE_SERVICES_DATA_CONTRIBUTOR_PREVIEW",
"COGNITIVE_SERVICES_DATA_READER",
"COGNITIVE_SERVICES_FACE_CONTRIBUTOR",
"COGNITIVE_SERVICES_FACE_RECOGNIZER",
"COGNITIVE_SERVICES_IMMERSIVE_READER_USER",
"COGNITIVE_SERVICES_LANGUAGE_OWNER",
"COGNITIVE_SERVICES_LANGUAGE_READER",
"COGNITIVE_SERVICES_LANGUAGE_WRITER",
"COGNITIVE_SERVICES_LUIS_OWNER",
"COGNITIVE_SERVICES_LUIS_READER",
"COGNITIVE_SERVICES_LUIS_WRITER",
"COGNITIVE_SERVICES_METRICS_ADVISOR_ADMINISTRATOR",
"COGNITIVE_SERVICES_METRICS_ADVISOR_USER",
"COGNITIVE_SERVICES_OPENAI_CONTRIBUTOR",
"COGNITIVE_SERVICES_OPENAI_USER",
"COGNITIVE_SERVICES_QNA_MAKER_EDITOR",
"COGNITIVE_SERVICES_QNA_MAKER_READER",
"COGNITIVE_SERVICES_SPEECH_CONTRIBUTOR",
"COGNITIVE_SERVICES_SPEECH_USER",
"COGNITIVE_SERVICES_USAGES_READER",
"COGNITIVE_SERVICES_USER",
"COLLABORATIVE_DATA_CONTRIBUTOR",
"COLLABORATIVE_RUNTIME_OPERATOR",
"COMMUNICATION_AND_EMAIL_SERVICE_OWNER",
"COMMUNITY_CONTRIBUTOR_ROLE",
"COMMUNITY_OWNER_ROLE",
"COMMUNITY_READER_ROLE",
"COMPUTE_DIAGNOSTICS_ROLE",
"COMPUTE_FLEET_CONTRIBUTOR",
"COMPUTE_GALLERY_ARTIFACTS_PUBLISHER",
"COMPUTE_GALLERY_IMAGE_READER",
"COMPUTE_GALLERY_SHARING_ADMIN",
"COMPUTE_RECOMMENDATIONS_ROLE",
"CONNECTED_CLUSTER_MANAGED_IDENTITY_CHECKACCESS_READER",
"CONNECTOR_READER",
"CONTAINERAPP_READER",
"CONTAINER_APPS_CONNECTEDENVIRONMENTS_CONTRIBUTOR",
"CONTAINER_APPS_CONNECTEDENVIRONMENTS_READER",
"CONTAINER_APPS_CONTRIBUTOR",
"CONTAINER_APPS_JOBS_CONTRIBUTOR",
"CONTAINER_APPS_JOBS_OPERATOR",
"CONTAINER_APPS_JOBS_READER",
"CONTAINER_APPS_MANAGEDENVIRONMENTS_CONTRIBUTOR",
"CONTAINER_APPS_MANAGEDENVIRONMENTS_READER",
"CONTAINER_APPS_OPERATOR",
"CONTAINER_APPS_SESSIONPOOLS_CONTRIBUTOR",
"CONTAINER_APPS_SESSIONPOOLS_READER",
"CONTAINER_INSTANCES_CONTRIBUTOR",
"CONTAINER_REGISTRY_CACHE_RULE_ADMINISTRATOR",
"CONTAINER_REGISTRY_CACHE_RULE_READER",
"CONTAINER_REGISTRY_CONFIGURATION_READER_AND_DATA_ACCESS_CONFIGURATION_READER",
"CONTAINER_REGISTRY_CONTRIBUTOR_AND_DATA_ACCESS_CONFIGURATION_ADMINISTRATOR",
"CONTAINER_REGISTRY_CREDENTIAL_SET_ADMINISTRATOR",
"CONTAINER_REGISTRY_CREDENTIAL_SET_READER",
"CONTAINER_REGISTRY_DATA_IMPORTER_AND_DATA_READER",
"CONTAINER_REGISTRY_REPOSITORY_CATALOG_LISTER",
"CONTAINER_REGISTRY_REPOSITORY_CONTRIBUTOR",
"CONTAINER_REGISTRY_REPOSITORY_READER",
"CONTAINER_REGISTRY_REPOSITORY_WRITER",
"CONTAINER_REGISTRY_TASKS_CONTRIBUTOR",
"CONTAINER_REGISTRY_TRANSFER_PIPELINE_CONTRIBUTOR",
"CONTRIBUTOR",
"COSMOSBACKUPOPERATOR",
"COSMOSRESTOREOPERATOR",
"COSMOS_DB_ACCOUNT_READER_ROLE",
"COSMOS_DB_OPERATOR",
"COST_MANAGEMENT_CONTRIBUTOR",
"COST_MANAGEMENT_READER",
"CROSSCONNECTIONMANAGER",
"CROSSCONNECTIONREADER",
"CUSTOM_AUTOMATION_ROLE_ROOT",
"DATA_BOUNDARY_TENANT_ADMINISTRATOR",
"DATA_BOX_CONTRIBUTOR",
"DATA_BOX_READER",
"DATA_FACTORY_CONTRIBUTOR",
"DATA_LABELING_LABELER",
"DATA_LAKE_ANALYTICS_DEVELOPER",
"DATA_OPERATOR_FOR_MANAGED_DISKS",
"DATA_PURGER",
"DEDICATED_HOST_CONTRIBUTOR_ROLE",
"DEFENDER_CSPM_STORAGE_DATA_SCANNER",
"DEFENDER_CSPM_STORAGE_SCANNER_OPERATOR",
"DEFENDER_FOR_STORAGE_DATA_SCANNER",
"DEFENDER_FOR_STORAGE_SCANNER_OPERATOR",
"DEFENDER_KUBERNETES_AGENT_OPERATOR",
"DEID_BATCH_DATA_OWNER",
"DEID_BATCH_DATA_READER",
"DEID_DATA_OWNER",
"DEID_REALTIME_DATA_USER",
"DEPLOYMENT_ENVIRONMENTS_READER",
"DEPLOYMENT_ENVIRONMENTS_USER",
"DESKTOP_VIRTUALIZATION_APPLICATION_GROUP_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_APPLICATION_GROUP_READER",
"DESKTOP_VIRTUALIZATION_APP_ATTACH_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_HOST_POOL_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_HOST_POOL_READER",
"DESKTOP_VIRTUALIZATION_POWER_ON_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_POWER_ON_OFF_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_READER",
"DESKTOP_VIRTUALIZATION_SESSION_HOST_OPERATOR",
"DESKTOP_VIRTUALIZATION_USER",
"DESKTOP_VIRTUALIZATION_USER_SESSION_OPERATOR",
"DESKTOP_VIRTUALIZATION_VIRTUAL_MACHINE_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_WORKSPACE_CONTRIBUTOR",
"DESKTOP_VIRTUALIZATION_WORKSPACE_READER",
"DEVCENTER_DEV_BOX_USER",
"DEVCENTER_PROJECT_ADMIN",
"DEVICE_PROVISIONING_SERVICE_DATA_CONTRIBUTOR",
"DEVICE_PROVISIONING_SERVICE_DATA_READER",
"DEVICE_UPDATE_ADMINISTRATOR",
"DEVICE_UPDATE_CONTENT_ADMINISTRATOR",
"DEVICE_UPDATE_CONTENT_READER",
"DEVICE_UPDATE_DEPLOYMENTS_ADMINISTRATOR",
"DEVICE_UPDATE_DEPLOYMENTS_READER",
"DEVICE_UPDATE_READER",
"DEVTEST_LABS_USER",
"DICOM_DATA_OWNER",
"DICOM_DATA_READER",
"DISK_BACKUP_READER",
"DISK_ENCRYPTION_SET_OPERATOR_FOR_MANAGED_DISKS",
"DISK_POOL_OPERATOR",
"DISK_RESTORE_OPERATOR",
"DISK_SNAPSHOT_CONTRIBUTOR",
"DNS_RESOLVER_CONTRIBUTOR",
"DNS_ZONE_CONTRIBUTOR",
"DOCUMENTDB_ACCOUNT_CONTRIBUTOR",
"DOMAIN_SERVICES_CONTRIBUTOR",
"DOMAIN_SERVICES_READER",
"DURABLE_TASK_DATA_CONTRIBUTOR",
"DURABLE_TASK_DATA_READER",
"DURABLE_TASK_WORKER",
"EDGE_MANAGEMENT_COPILOT_USER",
"ELASTIC_SAN_NETWORK_ADMIN",
"ELASTIC_SAN_OWNER",
"ELASTIC_SAN_READER",
"ELASTIC_SAN_SNAPSHOT_EXPORTER",
"ELASTIC_SAN_VOLUME_GROUP_OWNER",
"ELASTIC_SAN_VOLUME_IMPORTER",
"ENCLAVE_APPROVER_ROLE",
"ENCLAVE_CONTRIBUTOR_ROLE",
"ENCLAVE_OWNER_ROLE",
"ENCLAVE_READER_ROLE",
"EVENTGRID_CONTRIBUTOR",
"EVENTGRID_DATA_CONTRIBUTOR",
"EVENTGRID_DATA_RECEIVER",
"EVENTGRID_DATA_SENDER",
"EVENTGRID_EVENTSUBSCRIPTION_CONTRIBUTOR",
"EVENTGRID_EVENTSUBSCRIPTION_READER",
"EVENTGRID_TOPICSPACES_PUBLISHER",
"EVENTGRID_TOPICSPACES_SUBSCRIBER",
"EXPERIMENTATION_ADMINISTRATOR",
"EXPERIMENTATION_CONTRIBUTOR",
"EXPERIMENTATION_METRIC_CONTRIBUTOR",
"EXPERIMENTATION_READER",
"FHIR_DATA_CONTRIBUTOR",
"FHIR_DATA_CONVERTER",
"FHIR_DATA_EXPORTER",
"FHIR_DATA_IMPORTER",
"FHIR_DATA_READER",
"FHIR_DATA_WRITER",
"FHIR_SMART_USER",
"FIRMWARE_ANALYSIS_ADMIN",
"FLUX_CONFIGURATIONS_CONTRIBUTOR",
"GEOCATALOG_ADMINISTRATOR",
"GEOCATALOG_READER",
"GITOPS_LZ_LIST_ACTIONS",
"GRAFANA_ADMIN",
"GRAFANA_EDITOR",
"GRAFANA_LIMITED_VIEWER",
"GRAFANA_VIEWER",
"GRAPH_OWNER",
"GROUPQUOTA_READER",
"GROUPQUOTA_REQUEST_OPERATOR",
"GUEST_CONFIGURATION_RESOURCE_CONTRIBUTOR",
"HDINSIGHT_CLUSTER_ADMIN",
"HDINSIGHT_CLUSTER_OPERATOR",
"HDINSIGHT_DOMAIN_SERVICES_CONTRIBUTOR",
"HDINSIGHT_ON_AKS_CLUSTER_ADMIN",
"HDINSIGHT_ON_AKS_CLUSTER_OPERATOR",
"HDINSIGHT_ON_AKS_CLUSTER_POOL_ADMIN",
"HEALTHCARE_AGENT_ADMIN",
"HEALTHCARE_AGENT_EDITOR",
"HEALTHCARE_AGENT_READER",
"HEALTH_SAFEGUARDS_DATA_USER",
"HIERARCHY_SETTINGS_ADMINISTRATOR",
"HYBRIDCOMPUTE_MACHINE_LISTACCESSDETAILS_ACTION_IN_BUILT_ROLE",
"HYBRID_SERVER_ONBOARDING",
"HYBRID_SERVER_RESOURCE_ADMINISTRATOR",
"IMPACT_READER",
"IMPACT_REPORTER",
"INTEGRATION_SERVICE_ENVIRONMENT_CONTRIBUTOR",
"INTEGRATION_SERVICE_ENVIRONMENT_DEVELOPER",
"INTELLIGENT_SYSTEMS_ACCOUNT_CONTRIBUTOR",
"IOT_HUB_DATA_CONTRIBUTOR",
"IOT_HUB_DATA_READER",
"IOT_HUB_REGISTRY_CONTRIBUTOR",
"IOT_HUB_TWIN_CONTRIBUTOR",
"IPAM_POOL_USER",
"ISSUE_CONTRIBUTOR",
"KEY_VAULT_ADMINISTRATOR",
"KEY_VAULT_CERTIFICATES_OFFICER",
"KEY_VAULT_CERTIFICATE_USER",
"KEY_VAULT_CONTRIBUTOR",
"KEY_VAULT_CRYPTO_OFFICER",
"KEY_VAULT_CRYPTO_SERVICE_ENCRYPTION_USER",
"KEY_VAULT_CRYPTO_SERVICE_RELEASE_USER",
"KEY_VAULT_CRYPTO_USER",
"KEY_VAULT_DATA_ACCESS_ADMINISTRATOR",
"KEY_VAULT_PURGE_OPERATOR",
"KEY_VAULT_READER",
"KEY_VAULT_SECRETS_OFFICER",
"KEY_VAULT_SECRETS_USER",
"KNOWLEDGE_CONSUMER",
"KOSTNER_DFS_BASE_ROLE",
"KUBERNETESRUNTIME_LOAD_BALANCER_CONTRIBUTOR_ROLE",
"KUBERNETES_AGENTLESS_OPERATOR",
"KUBERNETES_AGENT_OPERATOR",
"KUBERNETES_AGENT_SUBSCRIPTION_LEVEL_OPERATOR",
"KUBERNETES_CLUSTER_AZURE_ARC_ONBOARDING",
"KUBERNETES_EXTENSION_CONTRIBUTOR",
"KUBERNETES_NAMESPACE_USER",
"KUBERNETES_RUNTIME_STORAGE_CLASS_CONTRIBUTOR_ROLE",
"LAB_ASSISTANT",
"LAB_CONTRIBUTOR",
"LAB_CREATOR",
"LAB_OPERATOR",
"LAB_SERVICES_CONTRIBUTOR",
"LAB_SERVICES_READER",
"LANDING_ZONE_ACCOUNT_OWNER",
"LANDING_ZONE_ACCOUNT_READER",
"LANDING_ZONE_MANAGEMENT_OWNER",
"LANDING_ZONE_MANAGEMENT_READER",
"LOAD_TEST_CONTRIBUTOR",
"LOAD_TEST_OWNER",
"LOAD_TEST_READER",
"LOCALNGFIREWALLADMINISTRATOR_ROLE",
"LOCALRULESTACKSADMINISTRATOR_ROLE",
"LOCKS_CONTRIBUTOR",
"LOGIC_APPS_STANDARD_CONTRIBUTOR_PREVIEW",
"LOGIC_APPS_STANDARD_DEVELOPER_PREVIEW",
"LOGIC_APPS_STANDARD_OPERATOR_PREVIEW",
"LOGIC_APPS_STANDARD_READER_PREVIEW",
"LOGIC_APP_CONTRIBUTOR",
"LOGIC_APP_OPERATOR",
"LOG_ANALYTICS_CONTRIBUTOR",
"LOG_ANALYTICS_READER",
"MANAGED_APPLICATION_CONTRIBUTOR_ROLE",
"MANAGED_APPLICATION_OPERATOR_ROLE",
"MANAGED_APPLICATION_PUBLISHER_OPERATOR",
"MANAGED_HSM_CONTRIBUTOR",
"MANAGED_IDENTITY_CONTRIBUTOR",
"MANAGED_IDENTITY_FEDERATED_IDENTITY_CREDENTIAL_CONTRIBUTOR",
"MANAGED_IDENTITY_OPERATOR",
"MANAGED_SERVICES_REGISTRATION_ASSIGNMENT_DELETE_ROLE",
"MANAGEMENT_GROUP_CONTRIBUTOR",
"MANAGEMENT_GROUP_READER",
"MEDIA_SERVICES_ACCOUNT_ADMINISTRATOR",
"MEDIA_SERVICES_LIVE_EVENTS_ADMINISTRATOR",
"MEDIA_SERVICES_MEDIA_OPERATOR",
"MEDIA_SERVICES_POLICY_ADMINISTRATOR",
"MEDIA_SERVICES_STREAMING_ENDPOINTS_ADMINISTRATOR",
"MICROSOFT_EDGE_WINFIELDS_FEDERATED_SUBSCRIPTION_READ_ACCESS_ROLE",
"MICROSOFT_KUBERNETES_CONNECTED_CLUSTER_ROLE",
"MICROSOFT_POWERBI_TENANT_OPERATIONS_ROLE",
"MICROSOFT_SENTINEL_AUTOMATION_CONTRIBUTOR",
"MICROSOFT_SENTINEL_BUSINESS_APPLICATIONS_AGENT_OPERATOR",
"MICROSOFT_SENTINEL_CONTRIBUTOR",
"MICROSOFT_SENTINEL_PLAYBOOK_OPERATOR",
"MICROSOFT_SENTINEL_READER",
"MICROSOFT_SENTINEL_RESPONDER",
"MONITORING_CONTRIBUTOR",
"MONITORING_DATA_READER",
"MONITORING_METRICS_PUBLISHER",
"MONITORING_READER",
"MYSQL_BACKUP_AND_EXPORT_OPERATOR",
"NETWORK_CONTRIBUTOR",
"NEW_RELIC_APM_ACCOUNT_CONTRIBUTOR",
"NEXUS_NETWORK_FABRIC_SERVICE_READER",
"NEXUS_NETWORK_FABRIC_SERVICE_WRITER",
"OBJECT_ANCHORS_ACCOUNT_OWNER",
"OBJECT_ANCHORS_ACCOUNT_READER",
"OBJECT_UNDERSTANDING_ACCOUNT_OWNER",
"OBJECT_UNDERSTANDING_ACCOUNT_READER",
"ONLINE_EXPERIMENTATION_CONTRIBUTOR",
"ONLINE_EXPERIMENTATION_DATA_OWNER",
"ONLINE_EXPERIMENTATION_DATA_READER",
"ONLINE_EXPERIMENTATION_READER",
"OPENCOST_RATE_CARD_READER",
"OPERATOR_NEXUS_COMPUTE_CONTRIBUTOR_ROLE_PREVIEW",
"OPERATOR_NEXUS_KEY_VAULT_WRITER_SERVICE_ROLE_PREVIEW",
"OPERATOR_NEXUS_OWNER_PREVIEW",
"ORACLE_DATABASE_AUTONOMOUS_DATABASE_ADMINISTRATOR",
"ORACLE_DATABASE_EXADATA_INFRASTRUCTURE_ADMINISTRATOR_BUILT_IN_ROLE",
"ORACLE_DATABASE_EXASCALE_STORAGE_VAULT_ADMINISTRATOR",
"ORACLE_DATABASE_EXASCALE_VMCLUSTER_ADMINISTRATOR",
"ORACLE_DATABASE_OWNER_BUILT_IN_ROLE",
"ORACLE_DATABASE_READER_BUILT_IN_ROLE",
"ORACLE_DATABASE_VMCLUSTER_ADMINISTRATOR_BUILT_IN_ROLE",
"ORACLE_SUBSCRIPTIONS_MANAGER_BUILT_IN_ROLE",
"OWNER",
"PLAYFAB_CONTRIBUTOR",
"PLAYFAB_READER",
"POLICY_INSIGHTS_DATA_WRITER_PREVIEW",
"PORTAL_DASHBOARD_WRITER_SERVICE_ROLE",
"POSTGRESQL_FLEXIBLE_SERVER_LONG_TERM_RETENTION_BACKUP_ROLE",
"POWER_PLATFORM_ACCOUNT_CONTRIBUTOR",
"POWER_PLATFORM_ENTERPRISE_POLICY_CONTRIBUTOR",
"PRIVATE_DNS_ZONE_CONTRIBUTOR",
"PROCUREMENT_CONTRIBUTOR",
"PROJECT_BABYLON_DATA_CURATOR",
"PROJECT_BABYLON_DATA_READER",
"PROJECT_BABYLON_DATA_SOURCE_ADMINISTRATOR",
"PROVIDERHUB_CONTRIBUTOR",
"PROVIDERHUB_READER",
"QUANTUM_WORKSPACE_DATA_CONTRIBUTOR",
"QUOTA_REQUEST_OPERATOR",
"READER",
"READER_AND_DATA_ACCESS",
"REDIS_CACHE_CONTRIBUTOR",
"REMOTE_RENDERING_ADMINISTRATOR",
"REMOTE_RENDERING_CLIENT",
"RESERVATION_PURCHASER",
"RESOURCE_POLICY_CONTRIBUTOR",
"ROLE_BASED_ACCESS_CONTROL_ADMINISTRATOR",
"SAAS_HUB_CONTRIBUTOR",
"SAVINGS_PLAN_PURCHASER",
"SCHEDULED_EVENTS_CONTRIBUTOR",
"SCHEDULED_PATCHING_CONTRIBUTOR",
"SCHEDULER_JOB_COLLECTIONS_CONTRIBUTOR",
"SCHEMA_REGISTRY_CONTRIBUTOR",
"SCHEMA_REGISTRY_READER",
"SEARCH_INDEX_DATA_CONTRIBUTOR",
"SEARCH_INDEX_DATA_READER",
"SEARCH_PARAMETER_MANAGER",
"SEARCH_SERVICE_CONTRIBUTOR",
"SECRETS_STORE_EXTENSION_OWNER",
"SECURE_SCORE_READER",
"SECURITY_ADMIN",
"SECURITY_ASSESSMENT_CONTRIBUTOR",
"SECURITY_DETONATION_CHAMBER_PUBLISHER",
"SECURITY_DETONATION_CHAMBER_READER",
"SECURITY_DETONATION_CHAMBER_SUBMISSION_MANAGER",
"SECURITY_DETONATION_CHAMBER_SUBMITTER",
"SECURITY_MANAGER_LEGACY",
"SECURITY_READER",
"SERVICES_HUB_OPERATOR",
"SERVICE_CONNECTOR_CONTRIBUTOR",
"SERVICE_FABRIC_CLUSTER_CONTRIBUTOR",
"SERVICE_FABRIC_MANAGED_CLUSTER_CONTRIBUTOR",
"SERVICE_GROUP_READER",
"SIGNALR_ACCESSKEY_READER",
"SIGNALR_APP_SERVER",
"SIGNALR_REST_API_OWNER",
"SIGNALR_REST_API_READER",
"SIGNALR_SERVICE_OWNER",
"SIGNALR_WEB_PUBSUB_CONTRIBUTOR",
"SITE_RECOVERY_CONTRIBUTOR",
"SITE_RECOVERY_OPERATOR",
"SITE_RECOVERY_READER",
"SPATIALMAPSACCOUNTS_ACCOUNT_OWNER",
"SPATIAL_ANCHORS_ACCOUNT_CONTRIBUTOR",
"SPATIAL_ANCHORS_ACCOUNT_OWNER",
"SPATIAL_ANCHORS_ACCOUNT_READER",
"SQLDB_MIGRATION_ROLE",
"SQLMI_MIGRATION_ROLE",
"SQLVM_MIGRATION_ROLE",
"SQL_DB_CONTRIBUTOR",
"SQL_MANAGED_INSTANCE_CONTRIBUTOR",
"SQL_SECURITY_MANAGER",
"SQL_SERVER_CONTRIBUTOR",
"SSH_PUBLICKEYS_CONTRIBUTOR_ROLE",
"SSH_PUBLICKEYS_READER_ROLE",
"STANDBY_CONTAINER_GROUP_POOL_CONTRIBUTOR",
"STORAGE_ACCOUNT_BACKUP_CONTRIBUTOR",
"STORAGE_ACCOUNT_CONTRIBUTOR",
"STORAGE_ACCOUNT_ENCRYPTION_SCOPE_CONTRIBUTOR_ROLE",
"STORAGE_ACCOUNT_KEY_OPERATOR_SERVICE_ROLE",
"STORAGE_BLOB_DATA_CONTRIBUTOR",
"STORAGE_BLOB_DATA_OWNER",
"STORAGE_BLOB_DATA_READER",
"STORAGE_BLOB_DELEGATOR",
"STORAGE_FILE_DATA_PRIVILEGED_CONTRIBUTOR",
"STORAGE_FILE_DATA_PRIVILEGED_READER",
"STORAGE_FILE_DATA_SMB_SHARE_CONTRIBUTOR",
"STORAGE_FILE_DATA_SMB_SHARE_ELEVATED_CONTRIBUTOR",
"STORAGE_FILE_DATA_SMB_SHARE_READER",
"STORAGE_QUEUE_DATA_CONTRIBUTOR",
"STORAGE_QUEUE_DATA_MESSAGE_PROCESSOR",
"STORAGE_QUEUE_DATA_MESSAGE_SENDER",
"STORAGE_QUEUE_DATA_READER",
"STORAGE_TABLE_DATA_CONTRIBUTOR",
"STORAGE_TABLE_DATA_READER",
"STREAM_ANALYTICS_CONTRIBUTOR",
"STREAM_ANALYTICS_QUERY_TESTER",
"STREAM_ANALYTICS_READER",
"SUBSCRIPTION_CREATOR",
"SUBSCRIPTION_CREATOR_OLD",
"SUPPORT_REQUEST_CONTRIBUTOR",
"TAG_CONTRIBUTOR",
"TAG_READER",
"TEMPLATE_SPEC_CONTRIBUTOR",
"TEMPLATE_SPEC_READER",
"TEST_BASE_READER",
"TOOLCHAINORCHESTRATOR_ADMIN_ROLE",
"TOOLCHAINORCHESTRATOR_VIEWER_ROLE",
"TRAFFIC_MANAGER_CONTRIBUTOR",
"TRANSPARENCY_LOGS_OWNER",
"TRUSTED_SIGNING_CERTIFICATE_PROFILE_SIGNER",
"TRUSTED_SIGNING_IDENTITY_VERIFIER",
"USAGE_BILLING_CONTRIBUTOR",
"USER_ACCESS_ADMINISTRATOR",
"VIDEO_INDEXER_RESTRICTED_VIEWER",
"VIRTUAL_MACHINE_ADMINISTRATOR_LOGIN",
"VIRTUAL_MACHINE_CONTRIBUTOR",
"VIRTUAL_MACHINE_DATA_ACCESS_ADMINISTRATOR_PREVIEW",
"VIRTUAL_MACHINE_LOCAL_USER_LOGIN",
"VIRTUAL_MACHINE_USER_LOGIN",
"VM_RESTORE_OPERATOR",
"VM_SCANNER_OPERATOR",
"WEBSITE_CONTRIBUTOR",
"WEB_PLAN_CONTRIBUTOR",
"WEB_PUBSUB_SERVICE_OWNER",
"WEB_PUBSUB_SERVICE_READER",
"WINDOWS_ADMIN_CENTER_ADMINISTRATOR_LOGIN",
"WORKBOOK_CONTRIBUTOR",
"WORKBOOK_READER",
"WORKLOADBUILDER_MIGRATION_AGENT_ROLE",
"WORKLOAD_ORCHESTRATION_IT_ADMIN",
"WORKLOAD_ORCHESTRATION_SOLUTION_EXTERNAL_VALIDATOR"
]

View File

@@ -18,8 +18,20 @@ from typing import Any
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Known Azure enum values not always captured in the catalog schema # Known Azure enum values not always captured in the catalog schema
def _load_azure_roles() -> list[str]:
"""Load Azure roles from azure_roles.json (generated from bicep/lookup/rbaclookup)."""
roles_file = pathlib.Path(__file__).parent / "azure_roles.json"
if roles_file.exists():
try:
return json.loads(roles_file.read_text())
except Exception as e:
logger.warning("Failed to load azure_roles.json: %s", e)
return []
_KNOWN_ENUMS: dict[str, list[str]] = { _KNOWN_ENUMS: dict[str, list[str]] = {
"principalType": ["User", "Group", "ServicePrincipal", "Device", "ForeignGroup"], "principalType": ["User", "Group", "ServicePrincipal", "Device", "ForeignGroup"],
"roles": _load_azure_roles(),
"roleDefinitionIds": _load_azure_roles(), # alias for roles
} }
# Catalog is baked into the image root at /bicep_modules_catalog.json # Catalog is baked into the image root at /bicep_modules_catalog.json
@@ -268,6 +280,56 @@ class BicepModuleCatalog:
iac_params = cls._iac_param_map(module_name) iac_params = cls._iac_param_map(module_name)
items = [] items = []
# Build snippet for full params block (shown first)
snippet_params = []
tabstop = 1
for param_name, param_info in ver_params.items():
iac = iac_params.get(param_name, {})
required = iac.get("required", False)
ptype = param_info.get("type", "any")
allowed = param_info.get("allowed", [])
# Include required params + first few optional params in snippet
if required or len(snippet_params) < 5:
if allowed:
# Enum: use placeholder with first allowed value
placeholder = f"'{allowed[0]}'"
elif ptype == "bool":
placeholder = "true"
elif ptype == "int":
placeholder = "0"
elif ptype == "array":
placeholder = "[]"
elif ptype == "object":
placeholder = "{{}}"
else:
placeholder = "''"
snippet_params.append(f" {param_name}: ${{{tabstop}:{placeholder}}}")
tabstop += 1
if snippet_params:
snippet_text = "\n" + "\n".join(snippet_params) + "\n"
required_count = sum(1 for p, i in ver_params.items()
if iac_params.get(p, {}).get("required", False))
items.append({
"label": "⚡ Fill params block",
"kind": 15, # Snippet
"detail": f"{len(snippet_params)} params ({required_count} required)",
"insertText": snippet_text,
"insertTextFormat": 2, # Snippet
"sortText": "0_lru_snippet_000",
"documentation": {
"kind": "markdown",
"value": (
f"**Fill params block**\n\n"
f"Inserts {len(snippet_params)} params for `{module_name}`.\n"
f"Use Tab to navigate between fields."
),
},
})
# Individual param completions
for param_name, param_info in ver_params.items(): for param_name, param_info in ver_params.items():
ptype = param_info.get("type", "any") ptype = param_info.get("type", "any")
allowed = param_info.get("allowed", []) allowed = param_info.get("allowed", [])

View File

@@ -142,6 +142,43 @@ class _ProxySession:
"has_open_quote": bool(value_m.group(2)), "has_open_quote": bool(value_m.group(2)),
} }
# Check if cursor is inside an array value for a param
# e.g. "roles: ['KEY_VAULT_" or "roles: [ '"
array_m = re.search(r"^\s*(\w+):\s*\[[^\]]*?('?)([^',\]]*)$", current)
if array_m and array_m.group(1) not in {"params", "name", "module", "resource"}:
return {
"type": "param_value",
"module": mod_name,
"version": mod_ver,
"param": array_m.group(1),
"has_open_quote": bool(array_m.group(2)),
}
# Check if cursor is inside a multi-line array element
# e.g. " 'APP_CONFIG" on line after "roles: ["
# Walk backwards to find the array opening
for lookback_idx in range(line_idx - 1, max(0, line_idx - 10), -1):
prev_line = lines[lookback_idx]
# Found array opening like "roles: [" or " roles: ["
array_open_m = re.match(r"^\s*(\w+):\s*\[$", prev_line.rstrip())
if array_open_m:
param_name = array_open_m.group(1)
if param_name not in {"params", "name", "module", "resource"}:
# Check if current line is inside the array (has quote or is indented)
if re.match(r"^\s+('?)([^',\]]*)\s*$", current):
has_quote = bool(re.match(r"^\s+'", current))
return {
"type": "param_value",
"module": mod_name,
"version": mod_ver,
"param": param_name,
"has_open_quote": has_quote,
}
break
# Stop if we hit a closing bracket (we're outside the array)
if "]" in prev_line:
break
return { return {
"type": "param", "type": "param",
"module": mod_name, "module": mod_name,

View File

@@ -24,6 +24,11 @@ ilsp = "ilsp.server:main"
where = ["."] where = ["."]
include = ["ilsp*"] include = ["ilsp*"]
[tool.setuptools.package-data]
"ilsp.bicep_lsp" = ["*.json"]
"ilsp.yaml_lsp" = ["*.json"]
"ilsp.python_lsp" = ["*.json"]
[project.optional-dependencies] [project.optional-dependencies]
dev = [ dev = [
"pytest>=8.0", "pytest>=8.0",

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""
Extract all Azure role names from rbacLookup.bicep and generate Python code
for _KNOWN_ENUMS in modules.py.
Usage:
python3 scripts/extract_roles_from_rbaclookup.py /path/to/rbacLookup.bicep
"""
import re
import sys
from pathlib import Path
def extract_role_names(bicep_file: Path) -> list[str]:
"""Extract all role names from the rbacLookup.bicep var roles = {...} block."""
content = bicep_file.read_text()
# Find the "var roles = {" block
roles_match = re.search(r'@export\(\)\s*var\s+roles\s*=\s*\{(.+?)\n\}', content, re.DOTALL)
if not roles_match:
raise ValueError("Could not find 'var roles = {' block in Bicep file")
roles_block = roles_match.group(1)
# Extract all role names (keys before the colon)
# Pattern: " ROLE_NAME: 'guid'"
role_names = re.findall(r'^\s+([A-Z_]+):', roles_block, re.MULTILINE)
return sorted(role_names)
def generate_python_code(role_names: list[str]) -> str:
"""Generate Python code for _KNOWN_ENUMS["roles"]."""
lines = ['_KNOWN_ENUMS = {']
lines.append(' "roles": [')
for role in role_names:
lines.append(f' "{role}",')
lines.append(' ],')
lines.append(' "roleDefinitionIds": [ # alias for roles')
for role in role_names:
lines.append(f' "{role}",')
lines.append(' ],')
lines.append('}')
return '\n'.join(lines)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: extract_roles_from_rbaclookup.py /path/to/rbacLookup.bicep", file=sys.stderr)
sys.exit(1)
bicep_path = Path(sys.argv[1])
if not bicep_path.exists():
print(f"Error: File not found: {bicep_path}", file=sys.stderr)
sys.exit(1)
try:
roles = extract_role_names(bicep_path)
print(f"# Extracted {len(roles)} roles from {bicep_path.name}")
print(f"# Generated: {Path(__file__).name}\n")
print(generate_python_code(roles))
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)

View File

@@ -17,6 +17,7 @@ frames straight to stdout.
import asyncio import asyncio
import ssl import ssl
import sys import sys
import traceback
import websockets import websockets
@@ -71,8 +72,9 @@ async def main(uri: str) -> None:
if msg is None: if msg is None:
break break
await ws.send(msg) await ws.send(msg)
except Exception: except Exception as e:
pass print(f"stdin_to_ws error: {e}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
async def ws_to_stdout() -> None: async def ws_to_stdout() -> None:
try: try:
@@ -80,8 +82,9 @@ async def main(uri: str) -> None:
data = frame if isinstance(frame, bytes) else frame.encode() data = frame if isinstance(frame, bytes) else frame.encode()
sys.stdout.buffer.write(data) sys.stdout.buffer.write(data)
sys.stdout.buffer.flush() sys.stdout.buffer.flush()
except Exception: except Exception as e:
pass print(f"ws_to_stdout error: {e}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
await asyncio.gather(stdin_to_ws(), ws_to_stdout()) await asyncio.gather(stdin_to_ws(), ws_to_stdout())
@@ -90,4 +93,9 @@ if __name__ == "__main__":
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: lsp_bridge.py <wss://...>", file=sys.stderr) print("Usage: lsp_bridge.py <wss://...>", file=sys.stderr)
sys.exit(1) sys.exit(1)
asyncio.run(main(sys.argv[1])) try:
asyncio.run(main(sys.argv[1]))
except Exception as e:
print(f"Fatal error: {e}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)

12
scripts/lsp_bridge_debug.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Wrapper for lsp_bridge.py that logs to a file for debugging IntelliJ issues
LOG_FILE="/tmp/lsp_bridge_debug.log"
echo "=== LSP Bridge started at $(date) ===" >> "$LOG_FILE"
echo "Args: $@" >> "$LOG_FILE"
echo "PWD: $(pwd)" >> "$LOG_FILE"
echo "Python: $(which python3)" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"
exec /opt/homebrew/bin/python3 /Users/lrihni/Projects/iLSP/scripts/lsp_bridge.py "$@" 2>> "$LOG_FILE"

View File

@@ -173,8 +173,12 @@ def test_param_completions_injected_on_param_context():
assert "roleDefinitionIds" in labels assert "roleDefinitionIds" in labels
assert "principalId" in labels assert "principalId" in labels
assert "principalType" in labels assert "principalType" in labels
assert items[0]["sortText"].startswith("0_lru_param_") # First item is now the snippet completion
assert items[0]["kind"] == 5 # Field assert items[0]["label"] == "⚡ Fill params block"
assert items[0]["kind"] == 15 # Snippet
# Second item should be a regular param
assert items[1]["sortText"].startswith("0_lru_param_")
assert items[1]["kind"] == 5 # Field
def test_param_completion_items_have_insert_text(): def test_param_completion_items_have_insert_text():
@@ -182,7 +186,12 @@ def test_param_completion_items_have_insert_text():
"roleassignments", versions=["1.1.x"], schema=_ROLEASSIGNMENTS_SCHEMA "roleassignments", versions=["1.1.x"], schema=_ROLEASSIGNMENTS_SCHEMA
)] )]
items = BicepModuleCatalog.param_completion_items("roleassignments", "1.1.x") items = BicepModuleCatalog.param_completion_items("roleassignments", "1.1.x")
for item in items: # First item should be the snippet completion
assert items[0]["label"] == "⚡ Fill params block"
assert items[0]["insertTextFormat"] == 2
assert "principalId" in items[0]["insertText"]
# Individual param items should have ": " suffix
for item in items[1:]:
assert item["insertText"].endswith(": ") assert item["insertText"].endswith(": ")
@@ -302,6 +311,48 @@ def test_detect_param_value_context_open_quote():
assert ctx["has_open_quote"] is True assert ctx["has_open_quote"] is True
def test_detect_param_value_context_in_array():
"""Cursor inside array value → param_value context."""
lines = [
"module myMod 'br/modules:roleassignments:1.1.x' = {",
" params: {",
" roles: ['KEY_VAULT_", # ← cursor inside array element
" }",
"}",
]
session = _make_session_with_doc(URI, lines)
# character = len(" roles: ['KEY_VAULT_") = 23
ctx = session._detect_context(URI, {"line": 2, "character": 23})
assert ctx["type"] == "param_value"
assert ctx["module"] == "roleassignments"
assert ctx["param"] == "roles"
assert ctx["has_open_quote"] is True
def test_detect_param_value_context_in_multiline_array():
"""Cursor in multi-line array element → param_value context."""
lines = [
"module myMod 'br/modules:roleassignments:2.0.x' = {",
" params: {",
" assignments: [",
" {",
" roles: [",
" 'APP_CONFIGURATION_", # ← cursor on separate line
" ]",
" }",
" ]",
" }",
"}",
]
session = _make_session_with_doc(URI, lines)
# character = len(" 'APP_CONFIGURATION_") = 27
ctx = session._detect_context(URI, {"line": 5, "character": 27})
assert ctx["type"] == "param_value"
assert ctx["module"] == "roleassignments"
assert ctx["param"] == "roles"
assert ctx["has_open_quote"] is True
def test_param_value_items_from_catalog_allowed(): def test_param_value_items_from_catalog_allowed():
"""environmentType completions come from catalog 'allowed' field.""" """environmentType completions come from catalog 'allowed' field."""
BicepModuleCatalog._modules = [_make_module( BicepModuleCatalog._modules = [_make_module(
@@ -353,6 +404,24 @@ def test_param_value_items_known_enum_fallback():
assert "User" in labels assert "User" in labels
def test_param_value_items_roles_enum():
"""roles parameter uses _KNOWN_ENUMS for Azure role completions."""
BicepModuleCatalog._modules = [_make_module(
"roleassignments",
versions=["1.1.x"],
schema={"1.1.x": {"parameters": {
"roles": {"type": "array"}, # no 'allowed' in catalog
}}},
)]
items = BicepModuleCatalog.param_value_completion_items(
"roleassignments", "1.1.x", "roles"
)
labels = [i["label"] for i in items]
assert "KEY_VAULT_SECRETS_USER" in labels
assert "STORAGE_BLOB_DATA_READER" in labels
assert "CONTRIBUTOR" in labels
def test_param_value_items_empty_for_free_string(): def test_param_value_items_empty_for_free_string():
"""A plain string param with no allowed values returns no completions.""" """A plain string param with no allowed values returns no completions."""
BicepModuleCatalog._modules = [_make_module( BicepModuleCatalog._modules = [_make_module(