Skip to content

Commit 4efbfb1

Browse files
Fix Azure Functions + Service Bus documentation to use managed identity (#906)
* Initial plan * Update Azure Functions and Service Bus docs for managed identity - Add Flex Consumption plan examples with managed identity - Show AzureWebJobsStorage__accountName pattern - Add Service Bus trigger with managed identity examples - Include Python v2 model examples - Update SDK patterns to prefer managed identity - Add comprehensive role assignment examples Co-authored-by: kvenkatrajan <102772054+kvenkatrajan@users.noreply.github.com> * Complete managed identity documentation update - All reference files validated successfully - Code review completed with no issues - Security scan confirms documentation-only changes Co-authored-by: kvenkatrajan <102772054+kvenkatrajan@users.noreply.github.com> * Add integration test for Python Function App with Service Bus trigger - Added test in azure-functions-deploy test group - Tests full end-to-end flow: azure-prepare, azure-validate, azure-deploy - Validates deployment links are generated - Uses the exact prompt from the reported issue Co-authored-by: kvenkatrajan <102772054+kvenkatrajan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kvenkatrajan <102772054+kvenkatrajan@users.noreply.github.com>
1 parent 1bf24c4 commit 4efbfb1

File tree

6 files changed

+448
-8
lines changed

6 files changed

+448
-8
lines changed

plugin/skills/azure-prepare/references/services/functions/bicep.md

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,124 @@
11
# Functions Bicep Patterns
22

3-
## Basic Resource
3+
## Flex Consumption (Recommended)
4+
5+
**Use Flex Consumption for new deployments with managed identity (no connection strings).**
6+
7+
```bicep
8+
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
9+
name: '${resourcePrefix}func${uniqueHash}'
10+
location: location
11+
sku: { name: 'Standard_LRS' }
12+
kind: 'StorageV2'
13+
properties: {
14+
minimumTlsVersion: 'TLS1_2'
15+
allowBlobPublicAccess: false
16+
allowSharedKeyAccess: false // Enforce managed identity
17+
}
18+
}
19+
20+
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
21+
parent: storageAccount
22+
name: 'default'
23+
}
24+
25+
resource deploymentContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
26+
parent: blobService
27+
name: 'deploymentpackage'
28+
}
29+
30+
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
31+
name: 'appi-${uniqueHash}'
32+
location: location
33+
kind: 'web'
34+
properties: {
35+
Application_Type: 'web'
36+
}
37+
}
38+
39+
resource functionAppPlan 'Microsoft.Web/serverfarms@2024-04-01' = {
40+
name: 'plan-${uniqueHash}'
41+
location: location
42+
sku: {
43+
name: 'FC1'
44+
tier: 'FlexConsumption'
45+
}
46+
properties: {
47+
reserved: true
48+
}
49+
}
50+
51+
resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
52+
name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
53+
location: location
54+
kind: 'functionapp,linux'
55+
identity: {
56+
type: 'SystemAssigned'
57+
}
58+
properties: {
59+
serverFarmId: functionAppPlan.id
60+
httpsOnly: true
61+
functionAppConfig: {
62+
deployment: {
63+
storage: {
64+
type: 'blobContainer'
65+
value: '${storageAccount.properties.primaryEndpoints.blob}deploymentpackage'
66+
authentication: {
67+
type: 'SystemAssignedIdentity'
68+
}
69+
}
70+
}
71+
scaleAndConcurrency: {
72+
maximumInstanceCount: 100
73+
instanceMemoryMB: 2048
74+
}
75+
runtime: {
76+
name: 'python' // or 'node', 'dotnet-isolated'
77+
version: '3.11'
78+
}
79+
}
80+
siteConfig: {
81+
appSettings: [
82+
{
83+
name: 'AzureWebJobsStorage__accountName'
84+
value: storageAccount.name
85+
}
86+
{
87+
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
88+
value: appInsights.properties.ConnectionString
89+
}
90+
{
91+
name: 'FUNCTIONS_EXTENSION_VERSION'
92+
value: '~4'
93+
}
94+
{
95+
name: 'FUNCTIONS_WORKER_RUNTIME'
96+
value: 'python'
97+
}
98+
]
99+
}
100+
}
101+
}
102+
103+
// Grant Function App access to Storage for runtime
104+
resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
105+
name: guid(storageAccount.id, functionApp.id, 'Storage Blob Data Owner')
106+
scope: storageAccount
107+
properties: {
108+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
109+
principalId: functionApp.identity.principalId
110+
principalType: 'ServicePrincipal'
111+
}
112+
}
113+
```
114+
115+
> 💡 **Key Points:**
116+
> - Use `AzureWebJobsStorage__accountName` instead of connection string
117+
> - Set `allowSharedKeyAccess: false` for enhanced security
118+
> - Use `SystemAssignedIdentity` for deployment authentication
119+
> - Grant `Storage Blob Data Owner` role for full access to blobs, queues, and tables
120+
121+
## Consumption Plan (Legacy)
4122

5123
```bicep
6124
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
@@ -38,6 +156,68 @@ resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
38156
}
39157
```
40158

159+
## Service Bus Integration (Managed Identity)
160+
161+
```bicep
162+
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' existing = {
163+
name: serviceBusNamespaceName
164+
}
165+
166+
resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
167+
// ... (Function App definition from above)
168+
properties: {
169+
// ... (other properties)
170+
siteConfig: {
171+
appSettings: [
172+
// Storage with managed identity
173+
{
174+
name: 'AzureWebJobsStorage__accountName'
175+
value: storageAccount.name
176+
}
177+
// Service Bus with managed identity
178+
{
179+
name: 'SERVICEBUS__fullyQualifiedNamespace'
180+
value: '${serviceBusNamespace.name}.servicebus.windows.net'
181+
}
182+
{
183+
name: 'SERVICEBUS_QUEUE_NAME'
184+
value: serviceBusQueueName
185+
}
186+
// Other settings...
187+
]
188+
}
189+
}
190+
}
191+
192+
// Grant Service Bus Data Receiver role for triggers
193+
resource serviceBusReceiverRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
194+
name: guid(serviceBusNamespace.id, functionApp.id, 'Azure Service Bus Data Receiver')
195+
scope: serviceBusNamespace
196+
properties: {
197+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')
198+
principalId: functionApp.identity.principalId
199+
principalType: 'ServicePrincipal'
200+
}
201+
}
202+
203+
// Grant Service Bus Data Sender role (if function sends messages)
204+
resource serviceBusSenderRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
205+
name: guid(serviceBusNamespace.id, functionApp.id, 'Azure Service Bus Data Sender')
206+
scope: serviceBusNamespace
207+
properties: {
208+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')
209+
principalId: functionApp.identity.principalId
210+
principalType: 'ServicePrincipal'
211+
}
212+
}
213+
```
214+
215+
> 💡 **Key Points:**
216+
> - Use `SERVICEBUS__fullyQualifiedNamespace` (double underscore) for managed identity
217+
> - Grant `Service Bus Data Receiver` role for reading messages
218+
> - Grant `Service Bus Data Sender` role for sending messages (if needed)
219+
> - Role assignments automatically enable connection via managed identity
220+
41221
## Premium Plan (No Cold Starts)
42222

43223
```bicep

plugin/skills/azure-prepare/references/services/functions/triggers.md

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,107 @@ module.exports = async function (context, req) {
2323

2424
Cron format: `{second} {minute} {hour} {day} {month} {day-of-week}`
2525

26-
## Queue Trigger (Service Bus)
26+
## Service Bus Trigger (Managed Identity)
27+
28+
### Python (v2 model)
29+
30+
```python
31+
import azure.functions as func
32+
import logging
33+
34+
app = func.FunctionApp()
35+
36+
@app.service_bus_queue_trigger(
37+
arg_name="msg",
38+
queue_name="orders",
39+
connection="SERVICEBUS" # References SERVICEBUS__fullyQualifiedNamespace
40+
)
41+
def process_queue_message(msg: func.ServiceBusMessage):
42+
logging.info('Processing Service Bus message: %s', msg.get_body().decode('utf-8'))
43+
# Process the message
44+
```
45+
46+
**Required app settings:**
47+
- `SERVICEBUS__fullyQualifiedNamespace`: `<namespace>.servicebus.windows.net`
48+
- Function App must have `Azure Service Bus Data Receiver` role on namespace
49+
50+
### Python (v1 model)
51+
52+
```json
53+
// function.json
54+
{
55+
"bindings": [{
56+
"name": "msg",
57+
"type": "serviceBusTrigger",
58+
"queueName": "orders",
59+
"connection": "SERVICEBUS"
60+
}]
61+
}
62+
```
63+
64+
```python
65+
# __init__.py
66+
import logging
67+
import azure.functions as func
68+
69+
def main(msg: func.ServiceBusMessage):
70+
logging.info('Processing message: %s', msg.get_body().decode('utf-8'))
71+
```
72+
73+
### Node.js
74+
75+
```json
76+
// function.json
77+
{
78+
"bindings": [{
79+
"name": "message",
80+
"type": "serviceBusTrigger",
81+
"queueName": "orders",
82+
"connection": "SERVICEBUS"
83+
}]
84+
}
85+
```
86+
87+
```javascript
88+
// index.js
89+
module.exports = async function (context, message) {
90+
context.log('Processing message:', message);
91+
};
92+
```
93+
94+
### .NET (Isolated)
95+
96+
```csharp
97+
[Function("ServiceBusProcessor")]
98+
public void Run(
99+
[ServiceBusTrigger("orders", Connection = "SERVICEBUS")]
100+
ServiceBusReceivedMessage message,
101+
FunctionContext context)
102+
{
103+
var logger = context.GetLogger("ServiceBusProcessor");
104+
logger.LogInformation($"Processing message: {message.Body}");
105+
}
106+
```
107+
108+
> 💡 **Managed Identity Configuration:**
109+
> - Connection name (e.g., `SERVICEBUS`) maps to `<name>__fullyQualifiedNamespace` app setting
110+
> - Use double underscore (`__`) to signal managed identity authentication
111+
> - No connection strings needed when proper RBAC roles are assigned
112+
113+
## Service Bus Topic Trigger
114+
115+
```python
116+
@app.service_bus_topic_trigger(
117+
arg_name="msg",
118+
topic_name="events",
119+
subscription_name="processor",
120+
connection="SERVICEBUS"
121+
)
122+
def process_topic_message(msg: func.ServiceBusMessage):
123+
logging.info('Processing topic message: %s', msg.get_body().decode('utf-8'))
124+
```
125+
126+
## Queue Trigger (Legacy - Connection String)
27127

28128
```json
29129
// function.json
@@ -37,6 +137,10 @@ Cron format: `{second} {minute} {hour} {day} {month} {day-of-week}`
37137
}
38138
```
39139

140+
**App setting:** `ServiceBusConnection`: `Endpoint=sb://...`
141+
142+
> ⚠️ **Use managed identity instead** for new deployments (see above)
143+
40144
## Blob Trigger
41145

42146
```json

plugin/skills/azure-prepare/references/services/service-bus/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Enterprise messaging with queues and pub/sub topics.
1616
| Resource | Purpose |
1717
|----------|---------|
1818
| None required | Service Bus is self-contained |
19-
| Key Vault | Store connection strings |
19+
| Key Vault | Store connection strings (legacy) |
2020

2121
## SKU Selection
2222

@@ -28,6 +28,20 @@ Enterprise messaging with queues and pub/sub topics.
2828

2929
## Environment Variables
3030

31+
### Managed Identity (Recommended)
32+
33+
| Variable | Value |
34+
|----------|-------|
35+
| `SERVICEBUS__fullyQualifiedNamespace` | `<namespace>.servicebus.windows.net` |
36+
| `SERVICEBUS_NAMESPACE` | Namespace name (for SDK) |
37+
| `SERVICEBUS_QUEUE` | Queue name |
38+
39+
**Required RBAC roles:**
40+
- `Azure Service Bus Data Sender` (69a216fc-b8fb-44d8-bc22-1f3c2cd27a39) - for sending
41+
- `Azure Service Bus Data Receiver` (4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0) - for receiving
42+
43+
### Connection String (Legacy)
44+
3145
| Variable | Value |
3246
|----------|-------|
3347
| `SERVICEBUS_CONNECTION_STRING` | Connection string (Key Vault) |

0 commit comments

Comments
 (0)