Skip to content

Commit 59d2289

Browse files
Merge pull request openshift#8645 from jhixson74/master_azure_identity
CORS-3435: Create user assigned identity for Azure VM's
2 parents 490865b + 26f5bad commit 59d2289

File tree

66 files changed

+21421
-33
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+21421
-33
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
1010
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0
1111
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
12+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2
1213
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1
1314
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
1415
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6Z
11741174
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
11751175
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
11761176
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
1177+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 h1:qiir/pptnHqp6hV8QwV+IExYIf6cPsXBfUDUXQ27t2Y=
1178+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2/go.mod h1:jVRrRDLCOuif95HDYC23ADTMlvahB7tMdl519m9Iyjc=
11771179
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ=
11781180
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA=
11791181
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4=

pkg/asset/machines/azure/azuremachines.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ func GenerateMachines(platform *azure.Platform, pool *types.MachinePool, userDat
4545
return nil, fmt.Errorf("failed to create machineapi.TagSpecifications from UserTags: %w", err)
4646
}
4747

48+
userAssignedIdentityID := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.ManagedIdentity/userAssignedIdentities/%s-identity", subscriptionID, resourceGroup, clusterID)
49+
4850
var image *capz.Image
4951
osImage := mpool.OSImage
5052
galleryName := strings.ReplaceAll(clusterID, "-", "_")
@@ -141,6 +143,12 @@ func GenerateMachines(platform *azure.Platform, pool *types.MachinePool, userDat
141143
AcceleratedNetworking: ptr.To(mpool.VMNetworkingType == azure.AcceleratedNetworkingEnabled),
142144
},
143145
},
146+
Identity: capz.VMIdentityUserAssigned,
147+
UserAssignedIdentities: []capz.UserAssignedIdentity{
148+
{
149+
ProviderID: userAssignedIdentityID,
150+
},
151+
},
144152
},
145153
}
146154
azureMachine.SetGroupVersionKind(capz.GroupVersion.WithKind("AzureMachine"))
@@ -202,6 +210,12 @@ func GenerateMachines(platform *azure.Platform, pool *types.MachinePool, userDat
202210
AllocatePublicIP: false,
203211
AdditionalCapabilities: additionalCapabilities,
204212
SecurityProfile: securityProfile,
213+
Identity: capz.VMIdentityUserAssigned,
214+
UserAssignedIdentities: []capz.UserAssignedIdentity{
215+
{
216+
ProviderID: userAssignedIdentityID,
217+
},
218+
},
205219
},
206220
}
207221
bootstrapAzureMachine.SetGroupVersionKind(capz.GroupVersion.WithKind("AzureMachine"))

pkg/infrastructure/azure/azure.go

Lines changed: 116 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import (
1111

1212
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
1313
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
14+
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3"
1415
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
1516
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi"
1617
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2"
1718
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
1819
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
1920
"github.com/coreos/stream-metadata-go/arch"
21+
"github.com/google/uuid"
2022
"github.com/sirupsen/logrus"
2123
"k8s.io/utils/ptr"
2224
capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
@@ -30,6 +32,11 @@ import (
3032
aztypes "github.com/openshift/installer/pkg/types/azure"
3133
)
3234

35+
const (
36+
retryTime = 10 * time.Second
37+
retryCount = 6
38+
)
39+
3340
// Provider implements Azure CAPI installation.
3441
type Provider struct {
3542
ResourceGroupName string
@@ -92,7 +99,7 @@ func (p *Provider) PreProvision(ctx context.Context, in clusterapi.PreProvisionI
9299
return fmt.Errorf("failed to get azure resource groups factory: %w", err)
93100
}
94101
resourceGroupsClient := resourcesClientFactory.NewResourceGroupsClient()
95-
resourceGroup, err := resourceGroupsClient.CreateOrUpdate(
102+
_, err = resourceGroupsClient.CreateOrUpdate(
96103
ctx,
97104
resourceGroupName,
98105
armresources.ResourceGroup{
@@ -105,9 +112,117 @@ func (p *Provider) PreProvision(ctx context.Context, in clusterapi.PreProvisionI
105112
if err != nil {
106113
return fmt.Errorf("error creating resource group %s: %w", resourceGroupName, err)
107114
}
115+
resourceGroup, err := resourceGroupsClient.Get(ctx, resourceGroupName, nil)
116+
if err != nil {
117+
return fmt.Errorf("error getting resource group %s: %w", resourceGroupName, err)
118+
}
119+
108120
logrus.Debugf("ResourceGroup.ID=%s", *resourceGroup.ID)
109121
p.ResourceGroupName = resourceGroupName
110122

123+
// Create user assigned identity
124+
userAssignedIdentityName := fmt.Sprintf("%s-identity", in.InfraID)
125+
armmsiClientFactory, err := armmsi.NewClientFactory(
126+
subscriptionID,
127+
tokenCredential,
128+
&arm.ClientOptions{
129+
ClientOptions: policy.ClientOptions{
130+
Cloud: cloudConfiguration,
131+
},
132+
},
133+
)
134+
if err != nil {
135+
return fmt.Errorf("failed to create armmsi client: %w", err)
136+
}
137+
_, err = armmsiClientFactory.NewUserAssignedIdentitiesClient().CreateOrUpdate(
138+
ctx,
139+
resourceGroupName,
140+
userAssignedIdentityName,
141+
armmsi.Identity{
142+
Location: ptr.To(platform.Region),
143+
Tags: tags,
144+
},
145+
nil,
146+
)
147+
if err != nil {
148+
return fmt.Errorf("failed to create user assigned identity %s: %w", userAssignedIdentityName, err)
149+
}
150+
userAssignedIdentity, err := armmsiClientFactory.NewUserAssignedIdentitiesClient().Get(
151+
ctx,
152+
resourceGroupName,
153+
userAssignedIdentityName,
154+
nil,
155+
)
156+
if err != nil {
157+
return fmt.Errorf("failed to get user assigned identity %s: %w", userAssignedIdentityName, err)
158+
}
159+
principalID := *userAssignedIdentity.Properties.PrincipalID
160+
161+
logrus.Debugf("UserAssignedIdentity.ID=%s", *userAssignedIdentity.ID)
162+
logrus.Debugf("PrinciapalID=%s", principalID)
163+
164+
clientFactory, err := armauthorization.NewClientFactory(
165+
subscriptionID,
166+
tokenCredential,
167+
&arm.ClientOptions{
168+
ClientOptions: policy.ClientOptions{
169+
Cloud: cloudConfiguration,
170+
},
171+
},
172+
)
173+
if err != nil {
174+
return fmt.Errorf("failed to create armauthorization client: %w", err)
175+
}
176+
177+
roleDefinitionsClient := clientFactory.NewRoleDefinitionsClient()
178+
179+
var contributor *armauthorization.RoleDefinition
180+
roleDefinitionsPager := roleDefinitionsClient.NewListPager(*resourceGroup.ID, nil)
181+
for roleDefinitionsPager.More() {
182+
roleDefinitionsList, err := roleDefinitionsPager.NextPage(ctx)
183+
if err != nil {
184+
return fmt.Errorf("failed to find any role definitions: %w", err)
185+
}
186+
for _, roleDefinition := range roleDefinitionsList.Value {
187+
if *roleDefinition.Properties.RoleName == "Contributor" {
188+
contributor = roleDefinition
189+
break
190+
}
191+
}
192+
}
193+
if contributor == nil {
194+
return fmt.Errorf("failed to find contributor definition")
195+
}
196+
197+
roleAssignmentsClient := clientFactory.NewRoleAssignmentsClient()
198+
scope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", subscriptionID, resourceGroupName)
199+
roleAssignmentUUID := uuid.New().String()
200+
201+
// XXX: Azure doesn't like creating an identity and immediately
202+
// creating a role assignment for the identity. There can be
203+
// replication delays. So, retry every 10 seconds for a minute until
204+
// the role assignment gets created.
205+
//
206+
// See https://aka.ms/docs-principaltype
207+
for i := 0; i < retryCount; i++ {
208+
_, err = roleAssignmentsClient.Create(ctx, scope, roleAssignmentUUID,
209+
armauthorization.RoleAssignmentCreateParameters{
210+
Properties: &armauthorization.RoleAssignmentProperties{
211+
PrincipalID: ptr.To(principalID),
212+
RoleDefinitionID: contributor.ID,
213+
},
214+
},
215+
nil,
216+
)
217+
if err == nil {
218+
break
219+
}
220+
time.Sleep(retryTime)
221+
}
222+
if err != nil {
223+
return fmt.Errorf("failed to create role assignment: %w", err)
224+
}
225+
111226
return nil
112227
}
113228

@@ -171,38 +286,6 @@ func (p *Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput
171286
storageURL := fmt.Sprintf("https://%s.blob.core.windows.net", storageAccountName)
172287
blobURL := fmt.Sprintf("%s/%s/%s", storageURL, containerName, blobName)
173288

174-
// Create user assigned identity
175-
userAssignedIdentityName := fmt.Sprintf("%s-identity", in.InfraID)
176-
armmsiClientFactory, err := armmsi.NewClientFactory(
177-
subscriptionID,
178-
tokenCredential,
179-
&arm.ClientOptions{
180-
ClientOptions: policy.ClientOptions{
181-
Cloud: cloudConfiguration,
182-
},
183-
},
184-
)
185-
if err != nil {
186-
return fmt.Errorf("failed to create armmsi client: %w", err)
187-
}
188-
userAssignedIdentity, err := armmsiClientFactory.NewUserAssignedIdentitiesClient().CreateOrUpdate(
189-
ctx,
190-
resourceGroupName,
191-
userAssignedIdentityName,
192-
armmsi.Identity{
193-
Location: ptr.To(platform.Region),
194-
Tags: tags,
195-
},
196-
nil,
197-
)
198-
if err != nil {
199-
return fmt.Errorf("failed to create user assigned identity %s: %w", userAssignedIdentityName, err)
200-
}
201-
principalID := *userAssignedIdentity.Properties.PrincipalID
202-
203-
logrus.Debugf("UserAssignedIdentity.ID=%s", *userAssignedIdentity.ID)
204-
logrus.Debugf("PrinciapalID=%s", principalID)
205-
206289
// Create storage account
207290
createStorageAccountOutput, err := CreateStorageAccount(ctx, &CreateStorageAccountInput{
208291
SubscriptionID: subscriptionID,

0 commit comments

Comments
 (0)