/pkg/armhelpers/graph.go

https://github.com/Azure/acs-engine · Go · 154 lines · 114 code · 24 blank · 16 comment · 11 complexity · 1e5b7da1cfb3144b8d8514411a8306f4 MD5 · raw file

  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT license.
  3. package armhelpers
  4. import (
  5. "context"
  6. "fmt"
  7. "regexp"
  8. "time"
  9. "github.com/Azure/azure-sdk-for-go/services/authorization/mgmt/2015-07-01/authorization"
  10. "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
  11. "github.com/Azure/go-autorest/autorest"
  12. "github.com/Azure/go-autorest/autorest/date"
  13. "github.com/Azure/go-autorest/autorest/to"
  14. "github.com/satori/go.uuid"
  15. log "github.com/sirupsen/logrus"
  16. )
  17. const (
  18. // AADContributorRoleID is the role id that exists in every subscription for 'Contributor'
  19. AADContributorRoleID = "b24988ac-6180-42a0-ab88-20f7382dd24c"
  20. // AADRoleReferenceTemplate is a template for a roleDefinitionId
  21. AADRoleReferenceTemplate = "/subscriptions/%s/providers/Microsoft.Authorization/roleDefinitions/%s"
  22. // AADRoleResourceGroupScopeTemplate is a template for a roleDefinition scope
  23. AADRoleResourceGroupScopeTemplate = "/subscriptions/%s/resourceGroups/%s"
  24. )
  25. // CreateGraphApplication creates an application via the graphrbac client
  26. func (az *AzureClient) CreateGraphApplication(ctx context.Context, applicationCreateParameters graphrbac.ApplicationCreateParameters) (graphrbac.Application, error) {
  27. return az.applicationsClient.Create(ctx, applicationCreateParameters)
  28. }
  29. // DeleteGraphApplication deletes an application via the graphrbac client
  30. func (az *AzureClient) DeleteGraphApplication(ctx context.Context, applicationObjectID string) (result autorest.Response, err error) {
  31. return az.applicationsClient.Delete(ctx, applicationObjectID)
  32. }
  33. // CreateGraphPrincipal creates a service principal via the graphrbac client
  34. func (az *AzureClient) CreateGraphPrincipal(ctx context.Context, servicePrincipalCreateParameters graphrbac.ServicePrincipalCreateParameters) (graphrbac.ServicePrincipal, error) {
  35. return az.servicePrincipalsClient.Create(ctx, servicePrincipalCreateParameters)
  36. }
  37. // CreateRoleAssignment creates a role assignment via the authorization client
  38. func (az *AzureClient) CreateRoleAssignment(ctx context.Context, scope string, roleAssignmentName string, parameters authorization.RoleAssignmentCreateParameters) (authorization.RoleAssignment, error) {
  39. return az.authorizationClient.Create(ctx, scope, roleAssignmentName, parameters)
  40. }
  41. // DeleteRoleAssignmentByID deletes a roleAssignment via its unique identifier
  42. func (az *AzureClient) DeleteRoleAssignmentByID(ctx context.Context, roleAssignmentID string) (authorization.RoleAssignment, error) {
  43. return az.authorizationClient.DeleteByID(ctx, roleAssignmentID)
  44. }
  45. // ListRoleAssignmentsForPrincipal (e.g. a VM) via the scope and the unique identifier of the principal
  46. func (az *AzureClient) ListRoleAssignmentsForPrincipal(ctx context.Context, scope string, principalID string) (RoleAssignmentListResultPage, error) {
  47. page, err := az.authorizationClient.ListForScope(ctx, scope, fmt.Sprintf("principalId eq '%s'", principalID))
  48. return &page, err
  49. }
  50. // CreateApp is a simpler method for creating an application
  51. func (az *AzureClient) CreateApp(ctx context.Context, appName, appURL string, replyURLs *[]string, requiredResourceAccess *[]graphrbac.RequiredResourceAccess) (applicationResp graphrbac.Application, servicePrincipalObjectID, servicePrincipalClientSecret string, err error) {
  52. notBefore := time.Now()
  53. notAfter := time.Now().Add(10000 * 24 * time.Hour)
  54. startDate := date.Time{Time: notBefore}
  55. endDate := date.Time{Time: notAfter}
  56. servicePrincipalClientSecret = uuid.NewV4().String()
  57. log.Debugf("ad: creating application with name=%q identifierURL=%q", appName, appURL)
  58. applicationReq := graphrbac.ApplicationCreateParameters{
  59. AvailableToOtherTenants: to.BoolPtr(false),
  60. DisplayName: to.StringPtr(appName),
  61. Homepage: to.StringPtr(appURL),
  62. IdentifierUris: to.StringSlicePtr([]string{appURL}),
  63. ReplyUrls: replyURLs,
  64. PasswordCredentials: &[]graphrbac.PasswordCredential{
  65. {
  66. KeyID: to.StringPtr(uuid.NewV4().String()),
  67. StartDate: &startDate,
  68. EndDate: &endDate,
  69. Value: to.StringPtr(servicePrincipalClientSecret),
  70. },
  71. },
  72. RequiredResourceAccess: requiredResourceAccess,
  73. }
  74. applicationResp, err = az.CreateGraphApplication(ctx, applicationReq)
  75. if err != nil {
  76. return applicationResp, "", "", err
  77. }
  78. applicationID := to.String(applicationResp.AppID)
  79. log.Debugf("ad: creating servicePrincipal for applicationID: %q", applicationID)
  80. servicePrincipalReq := graphrbac.ServicePrincipalCreateParameters{
  81. AppID: applicationResp.AppID,
  82. AccountEnabled: to.BoolPtr(true),
  83. }
  84. servicePrincipalResp, err := az.servicePrincipalsClient.Create(ctx, servicePrincipalReq)
  85. if err != nil {
  86. return applicationResp, "", "", err
  87. }
  88. servicePrincipalObjectID = to.String(servicePrincipalResp.ObjectID)
  89. return applicationResp, servicePrincipalObjectID, servicePrincipalClientSecret, nil
  90. }
  91. // DeleteApp is a simpler method for deleting an application and the associated spn
  92. func (az *AzureClient) DeleteApp(ctx context.Context, applicationName, applicationObjectID string) (autorest.Response, error) {
  93. log.Debugf("ad: deleting application with name=%q", applicationName)
  94. return az.DeleteGraphApplication(ctx, applicationObjectID)
  95. }
  96. // CreateRoleAssignmentSimple is a wrapper around RoleAssignmentsClient.Create
  97. func (az *AzureClient) CreateRoleAssignmentSimple(ctx context.Context, resourceGroup, servicePrincipalObjectID string) error {
  98. roleAssignmentName := uuid.NewV4().String()
  99. roleDefinitionID := fmt.Sprintf(AADRoleReferenceTemplate, az.subscriptionID, AADContributorRoleID)
  100. scope := fmt.Sprintf(AADRoleResourceGroupScopeTemplate, az.subscriptionID, resourceGroup)
  101. roleAssignmentParameters := authorization.RoleAssignmentCreateParameters{
  102. Properties: &authorization.RoleAssignmentProperties{
  103. RoleDefinitionID: to.StringPtr(roleDefinitionID),
  104. PrincipalID: to.StringPtr(servicePrincipalObjectID),
  105. },
  106. }
  107. re := regexp.MustCompile(`(?i)status=(\d+)`)
  108. for {
  109. _, err := az.CreateRoleAssignment(
  110. ctx,
  111. scope,
  112. roleAssignmentName,
  113. roleAssignmentParameters,
  114. )
  115. if err != nil {
  116. match := re.FindStringSubmatch(err.Error())
  117. if match != nil && (match[1] == "403") {
  118. //insufficient permissions. stop now
  119. log.Debugf("Failed to create role assignment (will abort now): %q", err)
  120. return err
  121. }
  122. // TODO: Should we handle 409 errors as well here ?
  123. log.Debugf("Failed to create role assignment (will retry): %q", err)
  124. time.Sleep(3 * time.Second)
  125. continue
  126. }
  127. break
  128. }
  129. return nil
  130. }