Least Privileged Access Required to View Azure App Service Log Stream: Complete Guide
The Log Stream in the Azure portal is greyed out — but az webapp log tail works fine from the CLI. This guide explains why, covers every permission path, and gives you the minimum viable custom role to unblock the portal UI without granting Contributor.
Not Enough
Why Log Stream Requires Write Access — The Kudu Architecture
Every Azure App Service has two websites. The first is your application — the one your users access. The second is the Kudu SCM site — a management companion app that Azure creates automatically and that runs at https://<your-app-name>.scm.azurewebsites.net. Kudu handles deployments, provides a web-based console, enables log file access, and most relevantly, serves the Log Stream endpoint at /api/logstream.
When you open Log Stream in the Azure portal, the portal navigates you to the Kudu SCM endpoint. This is not an Azure Monitor query against a Log Analytics Workspace — it is a live HTTP streaming connection to the Kudu app. Kudu enforces its own authentication, which ties back to Azure RBAC. Kudu requires that the authenticated identity has write-level access to the App Service resource — specifically the Microsoft.Web/sites/publish/Action permission (for Kudu access via Entra ID authentication) or the Microsoft.Web/sites/write permission (which gates the Log Stream portal panel itself).
This is a deliberate design decision by Azure, not an oversight. Kudu provides capabilities far beyond just reading logs — it gives access to the file system, a remote shell console, process monitoring, and deployment management. Requiring write-level permissions before granting any Kudu access prevents read-only users from accidentally performing potentially destructive operations through the Kudu interface, or having their tokens used for deployment operations without explicit intent.
Built-In Role Comparison — What Each Role Can and Cannot Do
This table covers every common built-in role and what it allows for each of the three App Service log access methods: portal Log Stream, CLI log tail, and Log Analytics query.
| Role | Portal Log Stream | CLI az webapp log tail | Log Analytics query | Recommended? |
|---|---|---|---|---|
| Owner | ✓ Works | ✓ Works | ✓ Works | No — far too broad |
| Contributor | ✓ Works | ✓ Works | ✓ Works | ⚠ Broader than needed |
| Website Contributor ← recommended built-in | ✓ Works | ✓ Works | ⚠ Only with workspace perms | ✓ Best built-in option |
| Reader | ✗ Greyed out | ⚠ Depends on credential | ⚠ Only with workspace perms | No — insufficient |
| Monitoring Reader | ✗ Greyed out | ✗ Unauthorized | ✓ Works | No — no Kudu access |
| Monitoring Contributor | ✗ Greyed out | ✗ Unauthorized | ✓ Works | No — no Kudu access |
| Log Analytics Reader | ✗ Greyed out | ✗ Unauthorized | ✓ Works | No — no Kudu access |
| Custom Role (log-stream-only) ← most minimal | ✓ Works | ✓ Works | ⚠ Add LA perms if needed | ✓ Truly minimal |
Solution 1: Assign Website Contributor Scoped to the App Service Resource
The Website Contributor built-in role is the least privileged built-in role that includes the write-level permissions Kudu requires. It includes Microsoft.Web/sites/write, Microsoft.Web/sites/publish/Action, and the ability to manage App Service configuration — without granting the ability to create or delete other resource types.
The critical detail: scope this role assignment to the App Service resource level — not the resource group or subscription. Assigning Website Contributor at the resource group level gives write access to every App Service in that group. Scoping to the specific App Service resource limits the access to exactly the one app that needs log access.
Open IAM on the App Service resource — not the resource group
Navigate to your App Service in the Azure portal. In the left-hand navigation menu, select Access control (IAM). This opens the RBAC management panel scoped specifically to this App Service resource. Click Add → Add role assignment.
If you navigate to IAM from the resource group instead, the assignment will apply to all resources in the group. Make sure the scope shown in the breadcrumb confirms you are at the App Service resource level, not the resource group.
Search for and select Website Contributor
In the Role tab, type Website Contributor in the search box. Select the Website Contributor role from the results. Click Next to proceed to the Members tab.
Select the user and assign the role
On the Members tab, click Select members and search for the user account, group, or service principal that needs log access. Select them and click Select. Click Review + assign, review the scope (it should show your App Service resource, not the resource group), and click Review + assign again to confirm.
The role assignment typically takes effect within a few minutes. The user may need to sign out and sign back in to the Azure portal for the updated permissions to appear in their session.
Verify the assignment worked — check effective permissions
After assigning the role, verify the user now has the necessary permissions before asking them to test. In the App Service IAM blade, click Check access → search for the user → click on their name → select Current role assignments to confirm Website Contributor is listed. Alternatively click Effective permissions and filter for Microsoft.Web/sites/write — it should now appear.
RESOURCE_GROUP="your-resource-group"
APP_NAME="your-app-service-name"
USER_EMAIL="user@yourdomain.com"
# Build the App Service resource ID for correct scope APP_RESOURCE_ID="/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${APP_NAME}"
# Assign Website Contributor scoped to the specific App Service (not RG) az role assignment create \
--assignee "${USER_EMAIL}" \
--role "Website Contributor" \
--scope "${APP_RESOURCE_ID}"
# Verify the assignment az role assignment list \
--assignee "${USER_EMAIL}" \
--scope "${APP_RESOURCE_ID}" \
--output table
Solution 2: Create a Custom Role with Only the Permissions Log Stream Actually Needs
Website Contributor includes permissions beyond what Log Stream strictly requires — it also grants the ability to manage deployments, configure app settings, modify SSL certificates, and several other operations. If your security posture requires truly minimum permissions, create a custom role that includes only what is needed for Log Stream access and nothing more.
The minimum set of permissions for portal Log Stream access:
- Microsoft.Web/sites/write — required to unlock the Log Stream panel in the Azure portal
- Microsoft.Web/sites/publish/Action — required to authenticate to the Kudu SCM endpoint via Entra ID
- Microsoft.Web/sites/read — required to read the App Service resource in the portal
- Microsoft.Web/sites/config/read — required to read App Service configuration (needed for the Log Stream panel to load)
- Microsoft.Resources/subscriptions/resourceGroups/read — required to navigate to the resource in the portal
Optionally, if you also want the user to see the diagnostic settings toggle and understand what logging is configured:
- Microsoft.Insights/diagnosticSettings/read
- Microsoft.Web/sites/diagnostics/read
"Name": "App Service Log Stream Viewer",
"IsCustom": true,
"Description": "View live Log Stream via Kudu in Azure App Service portal. Cannot modify app configuration, deployments, or settings.",
"Actions": [
// Minimum needed to unlock Log Stream panel and authenticate to Kudu
"Microsoft.Web/sites/read",
"Microsoft.Web/sites/write", // Unlocks Log Stream panel in portal UI
"Microsoft.Web/sites/publish/Action", // Authenticates to Kudu SCM via Entra ID
"Microsoft.Web/sites/config/read", // Reads app config (needed by portal panel)
// Navigation / portal UX
"Microsoft.Resources/subscriptions/resourceGroups/read",
// Optional: see diagnostic settings configuration
"Microsoft.Insights/diagnosticSettings/read",
"Microsoft.Web/sites/diagnostics/read"
],
"NotActions": [
// Explicitly exclude all write operations on config, SSL, slots, scale
"Microsoft.Web/sites/config/write",
"Microsoft.Web/sites/slots/*",
"Microsoft.Web/sites/start/Action",
"Microsoft.Web/sites/stop/Action",
"Microsoft.Web/sites/restart/Action",
"Microsoft.Web/sites/delete"
],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/YOUR-SUBSCRIPTION-ID"
]
}
Save the JSON above as a file and create the custom role via CLI
Save the JSON definition above as logstream-role.json. Update YOUR-SUBSCRIPTION-ID with your actual subscription ID. Then create the custom role using the Azure CLI command below. Custom roles are available in the subscription within a few minutes of creation.
# Step 2: Verify the role was created az role definition list --name "App Service Log Stream Viewer" --output table
# Step 3: Assign the custom role to the user, scoped to the specific App Service APP_RESOURCE_ID="/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${APP_NAME}"
az role assignment create \
--assignee "${USER_EMAIL}" \
--role "App Service Log Stream Viewer" \
--scope "${APP_RESOURCE_ID}"
# Step 4: Verify effective permissions include sites/write and publish/Action az role assignment list \
--assignee "${USER_EMAIL}" \
--scope "${APP_RESOURCE_ID}" \
--include-inherited \
--output table
Solution 3: Route Logs Through Log Analytics — Works with Reader Access
If giving write-level access to the App Service is not acceptable for your security posture, there is a legitimate workaround: route App Service logs to a Log Analytics workspace and let the user query logs there. Log Analytics queries work with the Reader role on the resource plus Log Analytics Reader on the workspace. No Kudu access needed, no write permissions needed.
The trade-off: Log Analytics is not a true live stream. There is typically a 1–5 minute ingestion delay. For debugging active issues, this delay is acceptable for most use cases. For absolute real-time stdout visibility during a deployment, it is not.
Enable diagnostic settings to send App Service logs to Log Analytics
Navigate to your App Service → Monitoring → Diagnostic settings → Add diagnostic setting. Select the log categories you need: AppServiceConsoleLogs (stdout/stderr from your app — the equivalent of what Log Stream shows), AppServiceHTTPLogs (HTTP access logs), and AppServiceAppLogs (application-level logs). Under Destination details, select Send to Log Analytics workspace and choose or create a workspace. Click Save.
Assign Log Analytics Reader to the user on the workspace
Navigate to the Log Analytics workspace used above. Select Access control (IAM) → Add role assignment. Assign Log Analytics Reader to the user who needs log access. This allows them to run KQL queries against all tables in the workspace. Combined with Reader on the App Service resource (for navigation in the portal), this gives full log visibility without any write access to the App Service.
Query console logs in Log Analytics — equivalent to Log Stream output
The user can now query App Service logs directly in the Log Analytics workspace, or from the App Service's own Logs blade in the portal (which uses resource-context mode and does not require Kudu). The queries below return the equivalent data to what Log Stream shows.
| where TimeGenerated > ago(30m)
| project TimeGenerated, ResultDescription, Level
| order by TimeGenerated desc
// Live-like view: auto-refreshes every 30 seconds in Log Analytics AppServiceConsoleLogs
| where TimeGenerated > ago(5m)
| project TimeGenerated, ResultDescription
| order by TimeGenerated asc
// HTTP access logs (equivalent to web server log stream) AppServiceHTTPLogs
| where TimeGenerated > ago(30m)
| project TimeGenerated, CsMethod, CsUriStem, ScStatus, TimeTaken
| order by TimeGenerated desc
// Application-level errors and exceptions AppServiceAppLogs
| where Level == "Error" or Level == "Warning"
| where TimeGenerated > ago(1h)
| project TimeGenerated, Level, Message, ExceptionClass, ExceptionMessage
| order by TimeGenerated desc
How to Check What Permissions You Currently Have
Before requesting an administrator to assign a new role, check your current effective permissions. This helps you understand exactly what is missing and present a precise ask to your admin.
# Check current role assignments on the App Service az role assignment list \
--assignee "${MY_USER}" \
--scope "${APP_RESOURCE_ID}" \
--include-inherited \
--query "[].{Role:roleDefinitionName, Scope:scope}" \
--output table
# Specifically check if you have the two key permissions for Log Stream # If either returns empty, you do not have that permission az role definition list \
--query "[?contains(permissions[0].actions, 'Microsoft.Web/sites/write')].name" \
--output table
# Quick test: does az webapp log tail work with your current credential? az webapp log tail \
--name "${APP_NAME}" \
--resource-group "${RESOURCE_GROUP}"
# If this works but portal Log Stream does not → you need write permission on the resource