Connect Azure¶
An Engineer or Admin can configure the Azure connector from the guided setup without a human handoff. It assumes 1 subscription. You create an app registration and service principal; Venturi’s workload identity federates into it using the issuer and subject values Venturi provides in guided setup. No customer-side workload identity is required.
No long-lived cloud credentials by default: the federated identity credential holds no secret. The client-secret fallback is the exception. See Docs authority & product state.
Access boundary¶
The module creates a Microsoft Entra ID app registration and service principal with these subscription-scope read roles:
- Reader for resource inventory.
- Cost Management Reader for billed cost.
- Monitoring Reader for Azure Monitor metrics and logs.
The APIM and Event Hubs telemetry grants are off by default. Enable each grant together with its resource ID:
enable_apim_accesswithapim_assigns Log Analytics Reader on the workspace that receives your API Management language-model logs.log_ analytics_ workspace_ id enable_withevent_ hub_ access event_assigns Azure Event Hubs Data Receiver on the namespace that receives Azure OpenAI diagnostic logs.hub_ namespace_ id
If you enable a grant without its resource ID, the module assigns the role at subscription scope instead.
Content-bearing logs. Read access to a log source is not storage of its
content. Event Hubs diagnostic streams can carry request or response detail when
that diagnostic category is enabled, and the Reader and Monitoring Reader roles
can query Log Analytics workspace tables in the subscription, including API
Management language-model logs that hold prompts and completions when message
logging is enabled. Scope the Event Hubs grant to the named namespace and the
Log Analytics grant to the named workspace. Workspace scoping limits only the
added Log Analytics Reader grant: subscription-scope Reader and Monitoring
Reader can still query the API Management language-model log table, so for Log
Analytics the in-memory field allowlist and non-persistence below are the
controlling boundary. Azure Monitor protected tables, a Microsoft preview
feature, can close that path on your side: a table set to Protected returns no
rows to standard read roles such as Reader, Monitoring Reader, and Log Analytics
Reader. For any record that still contains content, the collector applies a
field allowlist in memory, discards prompt and completion fields before a record
is materialized, and never persists, logs, retries, or exports them. The
normalized InvocationEvent schema has no field for prompt or completion text.
These setup permissions are separate from the standing read-only identity:
- A human setup operator needs permission to register applications and their service principals in Microsoft Entra ID, such as the Application Developer role, plus Owner, User Access Administrator, or Role Based Access Control Administrator on the subscription for the role assignments.
- A CI principal needs the Microsoft Graph application permission
Application.with admin consent, plus the same subscription role-assignment authority. With that permission, the CI principal can manage only objects it owns, so it must be an owner of both the app registration and the service principal.ReadWrite. OwnedBy - Global Administrator alone does not grant Azure RBAC on the subscription. A Global Administrator can elevate access to User Access Administrator at root scope, assign the subscription role, and then remove the elevated access.
- The module pins the
azurermprovider at~> 3.80, which by default tries to register every Azure resource provider it supports that is not yet registered on the subscription. Owner can register resource providers; User Access Administrator and Role Based Access Control Administrator cannot. If the setup identity is not Owner, setARM_beforeSKIP_ PROVIDER_ REGISTRATION=true terraform init. The module’s own resources are Microsoft Entra ID objects and role assignments, which need no additional resource provider.
Steps¶
1. Generate configuration¶
Role: Engineer or Admin in Venturi; authorized Microsoft Entra ID and Azure RBAC setup principal for the apply.
Select Azure in guided setup. The tenant flow generates the exact issuer,
subject, connector ID, and pinned Terraform artifact in-product. Download the
pinned Terraform artifact and extract it: the extracted
infra/ directory holds the module, backend.tf,
terraform., and scripts/verify.sh. Save the generated values
below as infra/, run every command on this
page from that directory, and keep it, with its Terraform state or backend
configuration, for rotation and removal.
subscription_id = "00000000-0000-0000-0000-000000000000"
display_name = "venturi-readonly-integration"
use_federated_credential = true
federated_credential_issuer = "<generated in guided setup>"
federated_credential_subject = "<generated exact subject>"
enable_apim_access = false
apim_log_analytics_workspace_id = ""
enable_event_hub_access = false
event_hub_namespace_id = ""
display_name is the display name of the app registration the module creates;
the module default is venturi-readonly-integration.
2. Deploy and prove the boundary¶
Role: the setup operator for the apply; a separate identity that holds only
Reader on the subscription for verify.sh.
cd infra/azure/onboarding
az login
export SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"
az account set --subscription "$SUBSCRIPTION_ID"
# Only if the setup identity is not Owner on the subscription:
export ARM_SKIP_PROVIDER_REGISTRATION=true
terraform init
terraform plan
terraform apply
CLIENT_ID="$(terraform output -raw client_id)"
OBJECT_ID="$(terraform output -raw object_id)"
export CLIENT_ID OBJECT_ID
This is the only terraform apply in the setup path; the
Deploy step later activates the connector without applying the
module again. Capture the outputs before you switch identity, as the last 3
lines do: a Reader-only identity cannot read Terraform state kept in the
azurerm backend.
Then sign in to az as the Reader-only identity, select the same subscription,
and run the checks:
az login
az account set --subscription "$SUBSCRIPTION_ID"
./scripts/verify.sh
az role assignment list --assignee "$OBJECT_ID" --all --output table
The script confirms the service principal, its subscription-scope role
allowlist, the federated credential, and the password-credential count. Its
write probes run as the identity signed in to az, not as the service
principal. From the Reader-only identity they are always denied, so they test
only that identity. The service principal’s boundary is proven by the --all
listing, which shows its role assignments at every scope, and by automated
credential validation. The script’s role allowlist is not that proof: it also
accepts roles the module never grants, including Reader and Data Access, which
can read and write all data in a storage account through its account keys, and
Billing Reader, and it checks subscription scope only. The listing must show
only Reader, Cost Management Reader, and Monitoring Reader on the subscription,
plus Log Analytics Reader on the named workspace and Azure Event Hubs Data
Receiver on the named namespace when you enable those grants.
See
Verify & confirm
for the exact checks. Connector activation performs positive Cost Management,
Resource Graph, Monitor, and enabled scoped telemetry reads through federation.
3. Register outputs and validate¶
Enter the client ID, object ID, tenant ID, subscription ID, and optional scoped resource IDs in the connector step. Automated credential validation checks the issuer and subject match, federation exchange, positive reads, and write denial.
4. Inspect evidence¶
Open the readiness result and /invocations. Inspect coverage, unknown identity
or cost dimensions, and freshness before enabling exports.
Client-secret fallback¶
Use the fallback only when your tenant cannot accept the federated credential.
Set use_; the module then creates a client secret
that expires 90 days after creation instead of a federated credential.
The secret value is not a Terraform output, but Terraform stores it in plain
text in the state file. Keep state in a remote backend with encryption at rest
and restricted access: uncomment and configure the azurerm backend block in
backend.tf before terraform init, or use an equivalent encrypted,
access-restricted backend. If state already exists locally, configure the
backend and run terraform init -migrate-state. Never commit
terraform.tfstate to source control.
Read the value only to enter it in the Azure connector step of guided setup, which stores it as an encrypted credential reference. Never send it by email, chat, or support ticket.
terraform show -json | python3 -c 'import json, sys; resources = json.load(sys.stdin)["values"]["root_module"]["resources"]; print(next(r["values"]["value"] for r in resources if r["type"] == "azuread_application_password"))'
Renew the secret before it expires; see Rotate & offboard.
Success evidence¶
terraform applysucceeds with a federated identity credential, or with 1 client secret in the client-secret fallback.verify.shexits0with noSECorMISSlines; with APIM access enabled, norole: Log Analytics Readerline; and an--alllisting that shows only Reader, Cost Management Reader, and Monitoring Reader on the subscription, plus Log Analytics Reader on the named workspace and Azure Event Hubs Data Receiver on the named namespace when those grants are enabled. In the client-secret fallback, aMISSline for the absent federated credential is expected; resolve every otherMISS.- Connector positive reads pass.
- Automated credential validation accepts the exact issuer and subject.
- The 1st Azure-backed
AttributionRecordappears in/invocations.
Recovery¶
| Failure | Action |
|---|---|
AADSTS70021 |
Compare the exact generated issuer, subject, and audience (api://) with the federated credential, then reapply. |
| Cost Management returns empty | For an Enterprise Agreement, an Enterprise admin must enable AO view charges on the billing account (enrollment) before subscription-scope roles can see costs. Otherwise, confirm the billing scope that holds the charges: an EA billing account, department, or enrollment account; a Microsoft Customer Agreement billing account, billing profile, or invoice section; or a CSP billing account, billing profile, or customer. |
| Azure OpenAI evidence is missing | Identify whether telemetry flows through API Management or Event Hubs, then enable the matching grant with its workspace or namespace resource ID and apply. |
| Terraform application setup fails | For terraform apply failures on azuread_application or azuread_, use a setup operator who can register applications, such as the Application Developer role, or a CI principal with Application., admin consent, and ownership of both the app registration and the service principal. |
| Role assignment fails | Use a setup identity with Owner, User Access Administrator, or Role Based Access Control Administrator on the subscription. Global Administrator alone does not grant subscription RBAC; elevate access, assign the role, then remove the elevation. |
terraform plan or apply fails while registering Azure resource providers |
The setup identity cannot register resource providers, which User Access Administrator and Role Based Access Control Administrator cannot do. Set ARM_, then run terraform init and terraform plan again. |
The --all listing shows a role other than the access boundary, such as Reader and Data Access or Billing Reader |
Security finding, even when verify.sh prints the role as OK. Disable the connector, remove the assignment, and rerun the checks. |
verify.sh reports write probes SUCCEEDED |
The script ran under a privileged az session. Under Owner, the role-assignment and resource-group probes succeed: delete the venturi-verify-must-fail-<timestamp> resource group. Under User Access Administrator or Role Based Access Control Administrator, only the role-assignment probe succeeds and no resource group is created. In both cases the script removes the Contributor assignment; confirm with the --all listing, then rerun as the Reader-only identity. |
verify.sh prints role: Log Analytics Reader |
APIM access is enabled with an empty apim_, so the role sits at subscription scope and reaches every workspace. Set the workspace resource ID, apply, and rerun the checks. |
| Freshness becomes stale | Check the readiness probe and latest Monitor or billing timestamp. |
Support is an optional escalation path after the failed probe and correlation ID are available.
Rotate or revoke¶
The module models 1 federated identity credential, so subject or issuer rotation is a coordinated cutover. Review it at least every 12 months:
- In
Integrations / Azure, generate the next exact subject, and issuer if it changes. - Set
federated_andcredential_ subject federated_incredential_ issuer terraform.tfvars, then runterraform planandterraform apply. The credential now accepts only the new values. - Immediately activate the new values in the connector settings and rerun automated credential validation. Venturi cannot exchange tokens between the apply and this step, so complete steps 2 and 3 together.
- Rerun the checks in step 2 as the Reader-only identity.
The client-secret fallback renews through its own cutover; see Rotate & offboard.
To revoke access, disable the connector in
Integrations / Azure, then run:
Confirm the application, service principal, federated credential or client secret, and role assignments are absent and record the revocation result.
Reference¶
- Verify the trust boundary
- Trust & security
- Rotate & offboard
- Microsoft: Application.ReadWrite.OwnedBy
- Microsoft: Elevate access to manage all Azure subscriptions and management groups
- Microsoft: Manage access to Log Analytics workspaces
- Microsoft: Configure protected tables in Azure Monitor Logs
- HashiCorp: azuread_application
- HashiCorp: AzureRM provider 3.x arguments, including
skip_provider_ registration - Microsoft: Azure resource providers and types
- Microsoft: Log token usage, prompts, and completions for language model APIs
- Microsoft: Understand and work with Cost Management scopes
Sign in to send feedback. Opens in a new tab.
Copy page details
Docs feedback: Connect Azure
Page: https://docs.venturi.systems/onboarding/azure/