Skip to content

Commit 359ce78

Browse files
njuCZkatbyte
andauthored
"azurerm_role_assignment" supports property "delegated_managed_identity_resource_id" (#11848)
* "azurerm_role_assignment" supports property "delegated_managed_identity_resource_id" * update * update Co-authored-by: kt <[email protected]>
1 parent 802edbf commit 359ce78

File tree

5 files changed

+213
-18
lines changed

5 files changed

+213
-18
lines changed

azurerm/internal/services/authorization/parse/role_assignment.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ type RoleAssignmentId struct {
1212
ResourceGroup string
1313
ManagementGroup string
1414
Name string
15+
TenantId string
1516
}
1617

17-
func NewRoleAssignmentID(subscriptionId, resourceGroup, managementGroup, name string) (*RoleAssignmentId, error) {
18+
func NewRoleAssignmentID(subscriptionId, resourceGroup, managementGroup, name, tenantId string) (*RoleAssignmentId, error) {
1819
if subscriptionId == "" && resourceGroup == "" && managementGroup == "" {
1920
return nil, fmt.Errorf("one of subscriptionId, resourceGroup, or managementGroup must be provided")
2021
}
@@ -36,10 +37,13 @@ func NewRoleAssignmentID(subscriptionId, resourceGroup, managementGroup, name st
3637
ResourceGroup: resourceGroup,
3738
ManagementGroup: managementGroup,
3839
Name: name,
40+
TenantId: tenantId,
3941
}, nil
4042
}
4143

42-
func (id RoleAssignmentId) ID() string {
44+
// in general case, the id format does not change
45+
// for cross tenant scenario, add the tenantId info
46+
func (id RoleAssignmentId) AzureResourceID() string {
4347
if id.ManagementGroup != "" {
4448
fmtString := "/providers/Microsoft.Management/managementGroups/%s/providers/Microsoft.Authorization/roleAssignments/%s"
4549
return fmt.Sprintf(fmtString, id.ManagementGroup, id.Name)
@@ -54,13 +58,30 @@ func (id RoleAssignmentId) ID() string {
5458
return fmt.Sprintf(fmtString, id.SubscriptionID, id.Name)
5559
}
5660

61+
func (id RoleAssignmentId) ID() string {
62+
return ConstructRoleAssignmentId(id.AzureResourceID(), id.TenantId)
63+
}
64+
65+
func ConstructRoleAssignmentId(azureResourceId, tenantId string) string {
66+
if tenantId == "" {
67+
return azureResourceId
68+
}
69+
return fmt.Sprintf("%s|%s", azureResourceId, tenantId)
70+
}
71+
5772
func RoleAssignmentID(input string) (*RoleAssignmentId, error) {
5873
if len(input) == 0 {
5974
return nil, fmt.Errorf("Role Assignment ID is empty string")
6075
}
6176

6277
roleAssignmentId := RoleAssignmentId{}
6378

79+
parts := strings.Split(input, "|")
80+
if len(parts) == 2 {
81+
roleAssignmentId.TenantId = parts[1]
82+
input = parts[0]
83+
}
84+
6485
switch {
6586
case strings.HasPrefix(input, "/subscriptions/"):
6687
id, err := azure.ParseAzureResourceID(input)

azurerm/internal/services/authorization/parse/role_assignment_test.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,66 @@ func TestRoleAssignmentIDFormatter(t *testing.T) {
1414
ResourceGroup string
1515
ManagementGroup string
1616
Name string
17+
TenantId string
1718
Expected string
1819
}{
1920
{
2021
SubscriptionId: "",
2122
ResourceGroup: "",
2223
ManagementGroup: "",
2324
Name: "23456781-2349-8764-5631-234567890121",
25+
TenantId: "",
2426
},
2527
{
2628
SubscriptionId: "12345678-1234-9876-4563-123456789012",
2729
ResourceGroup: "group1",
2830
ManagementGroup: "managementGroup1",
2931
Name: "23456781-2349-8764-5631-234567890121",
32+
TenantId: "",
3033
},
3134
{
3235
SubscriptionId: "12345678-1234-9876-4563-123456789012",
3336
ResourceGroup: "",
3437
ManagementGroup: "managementGroup1",
3538
Name: "23456781-2349-8764-5631-234567890121",
39+
TenantId: "",
3640
},
3741
{
3842
SubscriptionId: "12345678-1234-9876-4563-123456789012",
3943
ResourceGroup: "",
4044
ManagementGroup: "",
4145
Name: "23456781-2349-8764-5631-234567890121",
46+
TenantId: "",
4247
Expected: "/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
4348
},
4449
{
4550
SubscriptionId: "12345678-1234-9876-4563-123456789012",
4651
ResourceGroup: "group1",
4752
ManagementGroup: "",
4853
Name: "23456781-2349-8764-5631-234567890121",
54+
TenantId: "",
4955
Expected: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
5056
},
5157
{
5258
SubscriptionId: "",
5359
ResourceGroup: "",
5460
ManagementGroup: "12345678-1234-9876-4563-123456789012",
5561
Name: "23456781-2349-8764-5631-234567890121",
62+
TenantId: "",
5663
Expected: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
5764
},
65+
{
66+
SubscriptionId: "",
67+
ResourceGroup: "",
68+
ManagementGroup: "12345678-1234-9876-4563-123456789012",
69+
Name: "23456781-2349-8764-5631-234567890121",
70+
TenantId: "34567812-3456-7653-6742-345678901234",
71+
Expected: "/providers/Microsoft.Management/managementGroups/12345678-1234-9876-4563-123456789012/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121|34567812-3456-7653-6742-345678901234",
72+
},
5873
}
5974
for _, v := range testData {
6075
t.Logf("testing %+v", v)
61-
actual, err := NewRoleAssignmentID(v.SubscriptionId, v.ResourceGroup, v.ManagementGroup, v.Name)
76+
actual, err := NewRoleAssignmentID(v.SubscriptionId, v.ResourceGroup, v.ManagementGroup, v.Name, v.TenantId)
6277
if err != nil {
6378
if v.Expected == "" {
6479
continue
@@ -151,6 +166,16 @@ func TestRoleAssignmentID(t *testing.T) {
151166
Name: "23456781-2349-8764-5631-234567890121",
152167
},
153168
},
169+
{
170+
Input: "/providers/Microsoft.Management/managementGroups/managementGroup1/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121|34567812-3456-7653-6742-345678901234",
171+
Expected: &RoleAssignmentId{
172+
SubscriptionID: "",
173+
ResourceGroup: "",
174+
ManagementGroup: "managementGroup1",
175+
Name: "23456781-2349-8764-5631-234567890121",
176+
TenantId: "34567812-3456-7653-6742-345678901234",
177+
},
178+
},
154179
}
155180

156181
for _, v := range testData {

azurerm/internal/services/authorization/role_assignment_resource.go

+65-15
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"time"
99

1010
"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2020-04-01-preview/authorization"
11+
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-11-01/subscriptions"
1112
"github.com/hashicorp/go-uuid"
1213
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
1314
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
1415
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
16+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/authorization/parse"
1517
billingValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/billing/validate"
1618
managementGroupValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/validate"
1719
resourceValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/resource/validate"
@@ -96,6 +98,13 @@ func resourceArmRoleAssignment() *pluginsdk.Resource {
9698
Computed: true,
9799
},
98100

101+
"delegated_managed_identity_resource_id": {
102+
Type: pluginsdk.TypeString,
103+
Optional: true,
104+
ForceNew: true,
105+
ValidateFunc: azure.ValidateResourceID,
106+
},
107+
99108
"description": {
100109
Type: pluginsdk.TypeString,
101110
Optional: true,
@@ -128,6 +137,8 @@ func resourceArmRoleAssignment() *pluginsdk.Resource {
128137
func resourceArmRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interface{}) error {
129138
roleAssignmentsClient := meta.(*clients.Client).Authorization.RoleAssignmentsClient
130139
roleDefinitionsClient := meta.(*clients.Client).Authorization.RoleDefinitionsClient
140+
subscriptionClient := meta.(*clients.Client).Subscription.Client
141+
subscriptionId := meta.(*clients.Client).Account.SubscriptionId
131142
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
132143
defer cancel()
133144

@@ -163,7 +174,17 @@ func resourceArmRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interface{}
163174
name = uuid
164175
}
165176

166-
existing, err := roleAssignmentsClient.Get(ctx, scope, name, "")
177+
tenantId := ""
178+
delegatedManagedIdentityResourceID := d.Get("delegated_managed_identity_resource_id").(string)
179+
if len(delegatedManagedIdentityResourceID) > 0 {
180+
var err error
181+
tenantId, err = getTenantIdBySubscriptionId(ctx, subscriptionClient, subscriptionId)
182+
if err != nil {
183+
return err
184+
}
185+
}
186+
187+
existing, err := roleAssignmentsClient.Get(ctx, scope, name, tenantId)
167188
if err != nil {
168189
if !utils.ResponseWasNotFound(existing.Response) {
169190
return fmt.Errorf("Error checking for presence of existing Role Assignment ID for %q (Scope %q): %+v", name, scope, err)
@@ -182,6 +203,10 @@ func resourceArmRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interface{}
182203
},
183204
}
184205

206+
if len(delegatedManagedIdentityResourceID) > 0 {
207+
properties.RoleAssignmentProperties.DelegatedManagedIdentityResourceID = utils.String(delegatedManagedIdentityResourceID)
208+
}
209+
185210
condition := d.Get("condition").(string)
186211
conditionVersion := d.Get("condition_version").(string)
187212

@@ -197,19 +222,19 @@ func resourceArmRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interface{}
197222
properties.RoleAssignmentProperties.PrincipalType = authorization.ServicePrincipal
198223
}
199224

200-
if err := pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), retryRoleAssignmentsClient(d, scope, name, properties, meta)); err != nil {
225+
if err := pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), retryRoleAssignmentsClient(d, scope, name, properties, meta, tenantId)); err != nil {
201226
return err
202227
}
203228

204-
read, err := roleAssignmentsClient.Get(ctx, scope, name, "")
229+
read, err := roleAssignmentsClient.Get(ctx, scope, name, tenantId)
205230
if err != nil {
206231
return err
207232
}
208233
if read.ID == nil {
209234
return fmt.Errorf("Cannot read Role Assignment ID for %q (Scope %q)", name, scope)
210235
}
211236

212-
d.SetId(*read.ID)
237+
d.SetId(parse.ConstructRoleAssignmentId(*read.ID, tenantId))
213238
return resourceArmRoleAssignmentRead(d, meta)
214239
}
215240

@@ -219,7 +244,11 @@ func resourceArmRoleAssignmentRead(d *pluginsdk.ResourceData, meta interface{})
219244
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
220245
defer cancel()
221246

222-
resp, err := client.GetByID(ctx, d.Id(), "")
247+
id, err := parse.RoleAssignmentID(d.Id())
248+
if err != nil {
249+
return err
250+
}
251+
resp, err := client.GetByID(ctx, id.AzureResourceID(), id.TenantId)
223252
if err != nil {
224253
if utils.ResponseWasNotFound(resp.Response) {
225254
log.Printf("[DEBUG] Role Assignment ID %q was not found - removing from state", d.Id())
@@ -237,6 +266,7 @@ func resourceArmRoleAssignmentRead(d *pluginsdk.ResourceData, meta interface{})
237266
d.Set("role_definition_id", props.RoleDefinitionID)
238267
d.Set("principal_id", props.PrincipalID)
239268
d.Set("principal_type", props.PrincipalType)
269+
d.Set("delegated_managed_identity_resource_id", props.DelegatedManagedIdentityResourceID)
240270
d.Set("description", props.Description)
241271
d.Set("condition", props.Condition)
242272
d.Set("condition_version", props.ConditionVersion)
@@ -267,7 +297,7 @@ func resourceArmRoleAssignmentDelete(d *pluginsdk.ResourceData, meta interface{}
267297
return err
268298
}
269299

270-
resp, err := client.Delete(ctx, id.scope, id.name, "")
300+
resp, err := client.Delete(ctx, id.scope, id.name, id.tenantId)
271301
if err != nil {
272302
if !utils.ResponseWasNotFound(resp.Response) {
273303
return err
@@ -277,7 +307,7 @@ func resourceArmRoleAssignmentDelete(d *pluginsdk.ResourceData, meta interface{}
277307
return nil
278308
}
279309

280-
func retryRoleAssignmentsClient(d *pluginsdk.ResourceData, scope string, name string, properties authorization.RoleAssignmentCreateParameters, meta interface{}) func() *pluginsdk.RetryError {
310+
func retryRoleAssignmentsClient(d *pluginsdk.ResourceData, scope string, name string, properties authorization.RoleAssignmentCreateParameters, meta interface{}, tenantId string) func() *pluginsdk.RetryError {
281311
return func() *pluginsdk.RetryError {
282312
roleAssignmentsClient := meta.(*clients.Client).Authorization.RoleAssignmentsClient
283313
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
@@ -306,7 +336,7 @@ func retryRoleAssignmentsClient(d *pluginsdk.ResourceData, scope string, name st
306336
Target: []string{
307337
"ready",
308338
},
309-
Refresh: roleAssignmentCreateStateRefreshFunc(ctx, roleAssignmentsClient, *resp.ID),
339+
Refresh: roleAssignmentCreateStateRefreshFunc(ctx, roleAssignmentsClient, *resp.ID, tenantId),
310340
MinTimeout: 5 * time.Second,
311341
ContinuousTargetOccurence: 5,
312342
Timeout: d.Timeout(pluginsdk.TimeoutCreate),
@@ -321,27 +351,36 @@ func retryRoleAssignmentsClient(d *pluginsdk.ResourceData, scope string, name st
321351
}
322352

323353
type roleAssignmentId struct {
324-
scope string
325-
name string
354+
scope string
355+
name string
356+
tenantId string
326357
}
327358

328359
func parseRoleAssignmentId(input string) (*roleAssignmentId, error) {
329-
segments := strings.Split(input, "/providers/Microsoft.Authorization/roleAssignments/")
360+
tenantId := ""
361+
segments := strings.Split(input, "|")
362+
if len(segments) == 2 {
363+
tenantId = segments[1]
364+
input = segments[0]
365+
}
366+
367+
segments = strings.Split(input, "/providers/Microsoft.Authorization/roleAssignments/")
330368
if len(segments) != 2 {
331369
return nil, fmt.Errorf("Expected Role Assignment ID to be in the format `{scope}/providers/Microsoft.Authorization/roleAssignments/{name}` but got %q", input)
332370
}
333371

334372
// /{scope}/providers/Microsoft.Authorization/roleAssignments/{roleAssignmentName}
335373
id := roleAssignmentId{
336-
scope: strings.TrimPrefix(segments[0], "/"),
337-
name: segments[1],
374+
scope: strings.TrimPrefix(segments[0], "/"),
375+
name: segments[1],
376+
tenantId: tenantId,
338377
}
339378
return &id, nil
340379
}
341380

342-
func roleAssignmentCreateStateRefreshFunc(ctx context.Context, client *authorization.RoleAssignmentsClient, roleID string) pluginsdk.StateRefreshFunc {
381+
func roleAssignmentCreateStateRefreshFunc(ctx context.Context, client *authorization.RoleAssignmentsClient, roleID string, tenantId string) pluginsdk.StateRefreshFunc {
343382
return func() (interface{}, string, error) {
344-
resp, err := client.GetByID(ctx, roleID, "")
383+
resp, err := client.GetByID(ctx, roleID, tenantId)
345384
if err != nil {
346385
if utils.ResponseWasNotFound(resp.Response) {
347386
return resp, "pending", nil
@@ -351,3 +390,14 @@ func roleAssignmentCreateStateRefreshFunc(ctx context.Context, client *authoriza
351390
return resp, "ready", nil
352391
}
353392
}
393+
394+
func getTenantIdBySubscriptionId(ctx context.Context, client *subscriptions.Client, subscriptionId string) (string, error) {
395+
resp, err := client.Get(ctx, subscriptionId)
396+
if err != nil {
397+
return "", fmt.Errorf("get tenant Id by Subscription %s: %+v", subscriptionId, err)
398+
}
399+
if resp.TenantID == nil {
400+
return "", fmt.Errorf("tenant Id is nil by Subscription %s: %+v", subscriptionId, resp)
401+
}
402+
return *resp.TenantID, nil
403+
}

0 commit comments

Comments
 (0)