{"record":{"id":"9be540dddb293e12","repo":"microsoft/aspire","slug":"ownerresource-and-identityresource-must-both-be-null-for","errorCode":null,"errorMessage":"'ownerResource' and 'identityResource' must both be null (for global role assignments) or both be non-null (for targeted role assignments).","messagePattern":"'ownerResource' and 'identityResource' must both be null \\(for global role assignments\\) or both be non-null \\(for targeted role assignments\\)\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Azure/AzureRoleAssignmentResource.cs","lineNumber":59,"sourceCode":"    /// <remarks>\n    /// This is the resource on which <c>WithRoleAssignments</c> was called. Its managed identity\n    /// is exposed via <see cref=\"IdentityResource\"/>.\n    /// When <c>WithRoleAssignments</c> is called using an <see cref=\"AzureUserAssignedIdentityResource\"/>,\n    /// OwnerResource and IdentityResource are the same.\n    /// </remarks>\n    public IResource? OwnerResource { get; } = ValidateOwnerAndIdentity(ownerResource, identityResource);\n\n    /// <summary>\n    /// Gets the user-assigned managed identity whose principal receives the role assignments,\n    /// or <see langword=\"null\"/> for global role assignments that are granted to the deployment principal.\n    /// </summary>\n    public AzureUserAssignedIdentityResource? IdentityResource { get; } = identityResource;\n\n    private static IResource? ValidateOwnerAndIdentity(IResource? ownerResource, AzureUserAssignedIdentityResource? identityResource)\n    {\n        if ((ownerResource is null) != (identityResource is null))\n        {\n            throw new ArgumentException(\n                $\"'{nameof(ownerResource)}' and '{nameof(identityResource)}' must both be null (for global role assignments) or both be non-null (for targeted role assignments).\",\n                ownerResource is null ? nameof(ownerResource) : nameof(identityResource));\n        }\n\n        return ownerResource;\n    }\n}\n","sourceCodeStart":41,"sourceCodeEnd":67,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Azure/AzureRoleAssignmentResource.cs#L41-L67","documentation":"AzureRoleAssignmentResource enforces that role assignments are either global (attached to no owner resource) or targeted (attached to a specific owner resource). ValidateOwnerAndIdentity throws ArgumentException when exactly one of ownerResource/identityResource is null, because the constructor factory APIs require the pair to be consistent. The exception names whichever argument is null to point at the mismatched half.","triggerScenarios":"Calling the AzureRoleAssignmentResource constructor (or WithRoleAssignments-style APIs that feed it) with an ownerResource but a null identityResource, or vice versa.","commonSituations":"Hand-constructing role assignment resources in custom extension methods; refactoring code that passes an identity only conditionally; mixing the global API surface (no owner) with an identity parameter by mistake.","solutions":["Ensure both ownerResource and identityResource are provided together for targeted role assignments.","If the role assignment should be global, pass null for both arguments.","Check custom wrapper/extension methods that may pass the identity conditionally and make the null-ness symmetric."],"exampleFix":"// before\nvar roles = new AzureRoleAssignmentResource(\"roles\", scopeGroup, ownerResource, null);\n// after\nvar roles = new AzureRoleAssignmentResource(\"roles\", scopeGroup, ownerResource, identityResource);","handlingStrategy":"validation","validationCode":"if ((ownerResource is null) != (identityResource is null))\n{\n    throw new ArgumentException(\"ownerResource and identityResource must both be null or both non-null.\");\n}","typeGuard":"bool isValidPair = (ownerResource is null && identityResource is null) || (ownerResource is not null && identityResource is not null);","tryCatchPattern":"try { /* construct role assignment */ } catch (ArgumentException ex) when (ex.Message.Contains(\"ownerResource\")) { /* fix call site: pass both or neither */ }","preventionTips":["Always obtain the identity from the same builder call chain that produces the owner resource.","Never construct AzureRoleAssignmentResource directly; use the public WithRoleAssignments extension APIs."],"tags":["azure","role-assignment","argument-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}