@@ -10,14 +10,13 @@ import (
1010 "net/http"
1111 "strings"
1212
13- "azureaiagent/internal/exterrors"
14-
1513 "github.com/Azure/azure-sdk-for-go/sdk/azcore"
1614 "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
1715 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3"
1816 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry"
1917 "github.com/azure/azure-dev/cli/azd/pkg/azdext"
2018 "github.com/azure/azure-dev/cli/azd/pkg/graphsdk"
19+ "github.com/azure/azure-dev/cli/azd/pkg/output"
2120)
2221
2322const (
@@ -93,7 +92,8 @@ var sufficientRoleAssignWriteRoles = []string{
9392// - Container Registry Tasks Contributor OR Container Registry Repository Contributor
9493// on the ACR (to build images via remote build and push container images)
9594//
96- // Returns nil if all checks pass, or a structured error with suggestions on failure.
95+ // Missing roles are reported as warnings rather than errors so that deployment can proceed.
96+ // The developer may need to obtain the missing roles separately for full functionality.
9797func CheckDeveloperRBAC (ctx context.Context , azdClient * azdext.AzdClient ) error {
9898 azdEnvClient := azdClient .Environment ()
9999 cEnvResponse , err := azdEnvClient .GetCurrent (ctx , & azdext.EmptyRequest {})
@@ -179,25 +179,21 @@ func CheckDeveloperRBAC(ctx context.Context, azdClient *azdext.AzdClient) error
179179 "Azure AI User → Foundry Project" , info .ProjectScope ,
180180 armauthorization .PrincipalTypeUser ,
181181 ); assignErr != nil {
182- // Only treat 403 as a hard RBAC failure — transient errors (throttling, network) are non-blocking.
182+ // Warn rather than fail hard on 403 — deployment can proceed, but the developer
183+ // may not be able to interact with agents until this role is assigned.
183184 if respErr , ok := errors.AsType [* azcore.ResponseError ](assignErr ); ok &&
184185 respErr .StatusCode == http .StatusForbidden {
185- return exterrors .Auth (
186- exterrors .CodeDeveloperMissingAIUserRole ,
187- fmt .Sprintf (
188- "your identity (%s) does not have the 'Azure AI User' role on the Foundry Project %s/%s " +
189- "and auto-assign was denied: %s" ,
190- userProfile .DisplayName , info .AccountName , info .ProjectName , assignErr ,
191- ),
192- fmt .Sprintf (
193- "ask a subscription Owner or User Access Administrator to assign the 'Azure AI User' role " +
194- "to your identity on the Foundry Project scope:\n " +
195- " az role assignment create --assignee %s --role \" Azure AI User\" --scope %q" ,
196- principalID , info .ProjectScope ,
197- ),
198- )
186+ fmt .Printf ("%s\n " , output .WithWarningFormat (
187+ "Your identity (%s) does not have the 'Azure AI User' role on the Foundry Project %s/%s " +
188+ "and auto-assign was denied.\n " +
189+ " Ask a subscription Owner or User Access Administrator to assign the role:\n " +
190+ " az role assignment create --assignee %s --role \" Azure AI User\" --scope %q" ,
191+ userProfile .DisplayName , info .AccountName , info .ProjectName ,
192+ principalID , info .ProjectScope ,
193+ ))
194+ } else {
195+ fmt .Printf (" ⚠ Azure AI User auto-assign failed (non-auth error): %s — continuing\n " , assignErr )
199196 }
200- fmt .Printf (" ⚠ Azure AI User auto-assign failed (non-auth error): %s — continuing\n " , assignErr )
201197 } else {
202198 fmt .Println (" ✓ Azure AI User auto-assigned to developer identity" )
203199 }
@@ -212,29 +208,23 @@ func CheckDeveloperRBAC(ctx context.Context, azdClient *azdext.AzdClient) error
212208 if err != nil {
213209 fmt .Printf (" ⚠ Could not check role-assignment-write capability: %s\n " , err )
214210 } else if ! hasRoleWrite {
215- return exterrors .Auth (
216- exterrors .CodeDeveloperMissingRoleAssignWriteRole ,
217- fmt .Sprintf (
218- "your identity (%s) does not have the permission to write role assignments on the " +
219- "Foundry Project %s/%s — this is required for the postdeploy step to assign " +
220- "'Azure AI User' to agent service principals" ,
221- userProfile .DisplayName , info .AccountName , info .ProjectName ,
222- ),
223- fmt .Sprintf (
224- "ask a subscription Owner or User Access Administrator to assign one of these roles " +
225- "to your identity on the Foundry Project scope:\n " +
226- " • Owner\n " +
227- " • User Access Administrator\n " +
228- " • Role Based Access Control Administrator\n " +
229- " • Azure AI Project Manager\n " +
230- " • Azure AI Account Owner\n \n " +
231- " az role assignment create --assignee %s " +
232- "--role \" Role Based Access Control Administrator\" --scope %q\n \n " +
233- "Alternatively, if role assignments are managed externally:\n " +
234- " AZD_AGENT_SKIP_ROLE_ASSIGNMENTS=true" ,
235- principalID , info .ProjectScope ,
236- ),
237- )
211+ // Warn rather than fail hard: deployment can still proceed, but the postdeploy
212+ // step that assigns 'Azure AI User' to agent service principals may return 403.
213+ // Write with warning color so it appears as a yellow warning, not a red error.
214+ fmt .Printf ("%s\n " , output .WithWarningFormat (
215+ "Role assignment write not available on Foundry Project %s/%s.\n " +
216+ " The postdeploy step will attempt to assign 'Azure AI User' to agent service principals,\n " +
217+ " but may fail with a 403. To grant this permission, assign one of these roles:\n " +
218+ " • Owner\n " +
219+ " • User Access Administrator\n " +
220+ " • Role Based Access Control Administrator\n " +
221+ " • Azure AI Project Manager\n " +
222+ " • Azure AI Account Owner\n " +
223+ " az role assignment create --assignee %s " +
224+ "--role \" Role Based Access Control Administrator\" --scope %q\n " +
225+ " Or, if roles are managed externally: AZD_AGENT_SKIP_ROLE_ASSIGNMENTS=true" ,
226+ info .AccountName , info .ProjectName , principalID , info .ProjectScope ,
227+ ))
238228 } else {
239229 fmt .Println (" ✓ Role assignment write on Foundry Project" )
240230 }
@@ -276,43 +266,33 @@ func CheckDeveloperRBAC(ctx context.Context, azdClient *azdext.AzdClient) error
276266 if ! hasACRAccess {
277267 acrName := strings .TrimSuffix (normalizeLoginServer (acrEndpoint ), ".azurecr.io" )
278268 if isAbac {
279- return exterrors .Auth (
280- exterrors .CodeDeveloperMissingACRRole ,
281- fmt .Sprintf (
282- "your identity (%s) does not have the required role on the ABAC-mode Container Registry '%s' " +
283- "to push container images" ,
284- userProfile .DisplayName , acrName ,
285- ),
286- fmt .Sprintf (
287- "ask a subscription Owner or User Access Administrator to assign one of these roles " +
288- "to your identity on the Container Registry scope:\n " +
289- " • Owner (broad access)\n " +
290- " • Container Registry Repository Writer (ABAC push)\n " +
291- " • Container Registry Repository Contributor (superset of Writer)\n \n " +
292- " az role assignment create --assignee %s " +
293- "--role \" Container Registry Repository Writer\" --scope %q" ,
294- principalID , acrResourceID ,
295- ),
296- )
297- }
298- return exterrors .Auth (
299- exterrors .CodeDeveloperMissingACRRole ,
300- fmt .Sprintf (
301- "your identity (%s) does not have the required role on the Container Registry '%s' " +
302- "to build and push container images" ,
269+ fmt .Printf ("%s\n " , output .WithWarningFormat (
270+ "Your identity (%s) does not have the required role on the ABAC-mode Container Registry '%s' " +
271+ "to push container images.\n " +
272+ " Ask a subscription Owner or User Access Administrator to assign one of these roles:\n " +
273+ " • Owner (broad access)\n " +
274+ " • Container Registry Repository Writer (ABAC push)\n " +
275+ " • Container Registry Repository Contributor (superset of Writer)\n \n " +
276+ " az role assignment create --assignee %s " +
277+ "--role \" Container Registry Repository Writer\" --scope %q" ,
303278 userProfile .DisplayName , acrName ,
304- ),
305- fmt .Sprintf (
306- "ask a subscription Owner or User Access Administrator to assign one of these roles " +
307- "to your identity on the Container Registry scope:\n " +
308- " • Owner or Contributor (broad access)\n " +
309- " • AcrPush (push and pull images)\n " +
310- " • Container Registry Tasks Contributor (remote build)\n " +
311- " • Container Registry Repository Contributor (repository operations)\n \n " +
312- " az role assignment create --assignee %s --role \" AcrPush\" --scope %q" ,
313279 principalID , acrResourceID ,
314- ),
315- )
280+ ))
281+ } else {
282+ fmt .Printf ("%s\n " , output .WithWarningFormat (
283+ "Your identity (%s) does not have the required role on the Container Registry '%s' " +
284+ "to build and push container images.\n " +
285+ " Ask a subscription Owner or User Access Administrator to assign one of these roles:\n " +
286+ " • Owner or Contributor (broad access)\n " +
287+ " • AcrPush (push and pull images)\n " +
288+ " • Container Registry Tasks Contributor (remote build)\n " +
289+ " • Container Registry Repository Contributor (repository operations)\n \n " +
290+ " az role assignment create --assignee %s --role \" AcrPush\" --scope %q" ,
291+ userProfile .DisplayName , acrName ,
292+ principalID , acrResourceID ,
293+ ))
294+ }
295+ return nil
316296 }
317297
318298 if isAbac {
0 commit comments