Get Role definition function
Objective
Provide a new function named roleDefinition that fetches and returns role definition details as a JToken. The design aligns with existing helper patterns, mirroring other metadata-driven retrieval (e.g., GetProvider).
Pros • Improves readability in code and reduces confusion with unreadable GUIDs. • Lowers the risk of accidentally using the wrong GUID, enhancing security clarity. • Matches real-world naming conventions and organizational policies more intuitively. • Allows distinct handling of built-in vs. custom roles
Cons • Requires reliable name→GUID resolution, adding some potential performance overhead. • Historical role definitions might be deleted or recreated, causing mismatches if the roleName changes. • Could complicate compatibility when referencing older or custom roles in large environments.
Function signature
Gets the role definition.
<param name="additionalInfo">The additional info of errors.</param>
<param name="parameters">The function parameters (must include scope or definition ID).</param>
<param name="functionExpression">Used for logging or property inspection.</param>
<returns>A JToken containing the role definition.</returns>
private JToken GetRoleDefinition(
TemplateErrorAdditionalInfo additionalInfo,
JToken[] parameters,
FunctionExpression functionExpression)
Parameters: • additionalInfo: Propagates line/position data for error tracing. • parameters: Must have at least one string-based parameter (e.g., scope, role definition ID). • functionExpression: Optional context for logging or property lookups.
Return: A JToken (JSON object) describing the role definition.
Switch Statement Hook
case CoreConstants.RoleDefinitionFunction:
return GetRoleDefinition(additionalInfo, EnsureValueTypeParameters(), functionExpression);
Implementation Steps
1. Parameter Validation Throw an ExpressionException if fewer than one parameter is provided or if basic type checks fail.
2. Fetch Logic • Parse the scope or role definition ID from parameters[0].
Use either: • A direct HTTP GET (e.g., to Azure Resource Manager). • A user-defined delegate to fetch the data. • Convert the result to JToken before returning.
3. Logging and Metrics • Optionally record function usage via TemplateMetricsRecorder?.RecordFunctionUsage(...). • Log function expression properties if desired (similar to GetProvider).**
Error Handling • Throw ExpressionException when parameters are invalid or unavailable. • Wrap unexpected internal errors in InvalidOperationException with the function name. • Support short-circuiting logic if needed (although typically not relevant for roleDefinition).
Example Usage A template snippet calling roleDefinition might look like:
{
"name": "[roleDefinition(resourceId('subscriptions', '00000000-0000-0000-0000-000000000000'))]"
}
definition retrieves the role definition and returns it as JSON.
Testing • Unit Tests: Validate parameter parsing, error handling, and correct JSON shapes. • Integration Tests: Verify real or mocked HTTP responses if external calls are made.
Additional consideration
Proposed solution 1 In many environments, referencing a role definition by GUID is less transparent and has the potential for security oversights if the GUID is incorrect. The proposed idea is to support lookups based on roleName (plus an optional type) so that calls in ARM/Bicep templates can look like:
resource roleDefBuiltIn 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
roleName: 'Reader'
type: 'builtIn'
}
To enable this, wecan extend the existing GetRoleDefinition logic to include a parameter for roleName and (optionally) type (e.g., builtin, customRole). Internally, we can resolve the name by searching existing role definitions in the relevant scope—either via a built-in REST method or a delegate that fetches the correct object. This approach gives the user a more readable setup and helps reduce the chance of referencing the wrong GUID.
However, theres a few issues following this approach-
-
Alignment with ARM API: • The roleName property is not part of the actual PUT request for the Microsoft.Authorization/roleDefinitions resource. • Bicep templates aim to stay close to the actual API structure, avoiding abstractions that deviate from the underlying REST API.
-
Avoiding Special Cases: • Special handling for specific resources is discouraged unless absolutely necessary. • This ensures consistency across all resources and avoids introducing exceptions that complicate the language.
Proposed solution 2
resource roleDefBuiltIn 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
name: foo('Owner')
scope: tenant()
}
Introduce a function TBD- (foo()) that resolves a roleName to its corresponding GUID. The function can be used in the name property of the roleDefinitions resource, ensuring the template remains aligned with the API.
The function could return either: • A single string (the GUID). • An object containing the GUID and potentially other metadata (e.g., name, type). If the function returns an object, users would need to access the guid property explicitly (e.g., foo('Owner').guid).
Pros and Cons of Return Types
Single String (GUID) Pros: • Simple and intuitive for users. • Directly usable in the name property without additional syntax. • Minimal changes to existing workflows.
Cons: • Limited flexibility if additional metadata (e.g., type, name) is needed in the future. • May require introducing a new function later if more data is required.
Object (e.g., { guid: ..., name: ..., type: ... }) Pros: • Extensible: Can include additional metadata (e.g., type, name) without introducing new functions. • Provides more context for advanced scenarios.
Cons: • Slightly more complex for users (e.g., foo('Owner').guid instead of foo('Owner')). • May introduce unnecessary complexity if only the GUID is needed in most cases.
Also fixes #1895
Do we have any news on this one?