@@ -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.
3441type 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