Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new resource for azurerm_synapse_role_assignment #8863

Merged
merged 4 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions azurerm/internal/clients/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ func Build(ctx context.Context, builder ClientBuilder) (*Client, error) {
return nil, err
}

// Synapse Endpoints
synapseAuth, err := builder.AuthConfig.GetAuthorizationToken(sender, oauthConfig, env.ResourceIdentifiers.Synapse)
if err != nil {
return nil, err
}

// Key Vault Endpoints
keyVaultAuth := builder.AuthConfig.BearerAuthorizerCallback(sender, oauthConfig)

Expand All @@ -111,6 +117,7 @@ func Build(ctx context.Context, builder ClientBuilder) (*Client, error) {
ResourceManagerAuthorizer: auth,
ResourceManagerEndpoint: endpoint,
StorageAuthorizer: storageAuth,
SynapseAuthorizer: synapseAuth,
SkipProviderReg: builder.SkipProviderRegistration,
DisableCorrelationRequestID: builder.DisableCorrelationRequestID,
DisableTerraformPartnerID: builder.DisableTerraformPartnerID,
Expand Down
1 change: 1 addition & 0 deletions azurerm/internal/common/client_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type ClientOptions struct {
ResourceManagerAuthorizer autorest.Authorizer
ResourceManagerEndpoint string
StorageAuthorizer autorest.Authorizer
SynapseAuthorizer autorest.Authorizer

SkipProviderReg bool
DisableCorrelationRequestID bool
Expand Down
15 changes: 15 additions & 0 deletions azurerm/internal/services/synapse/client/client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package client

import (
"fmt"

"github.com/Azure/azure-sdk-for-go/services/preview/synapse/2020-02-01-preview/accesscontrol"
"github.com/Azure/azure-sdk-for-go/services/preview/synapse/mgmt/2019-06-01-preview/synapse"
"github.com/Azure/go-autorest/autorest"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common"
)

Expand All @@ -12,6 +16,8 @@ type Client struct {
SqlPoolTransparentDataEncryptionClient *synapse.SQLPoolTransparentDataEncryptionsClient
WorkspaceClient *synapse.WorkspacesClient
WorkspaceAadAdminsClient *synapse.WorkspaceAadAdminsClient

synapseAuthorizer autorest.Authorizer
}

func NewClient(o *common.ClientOptions) *Client {
Expand Down Expand Up @@ -41,5 +47,14 @@ func NewClient(o *common.ClientOptions) *Client {
SqlPoolTransparentDataEncryptionClient: &sqlPoolTransparentDataEncryptionClient,
WorkspaceClient: &workspaceClient,
WorkspaceAadAdminsClient: &workspaceAadAdminsClient,

synapseAuthorizer: o.SynapseAuthorizer,
}
}

func (client Client) AccessControlClient(workspaceName, synapseEndpointSuffix string) *accesscontrol.BaseClient {
endpoint := fmt.Sprintf("https://%s.%s", workspaceName, synapseEndpointSuffix)
accessControlClient := accesscontrol.New(endpoint)
accessControlClient.Client.Authorizer = client.synapseAuthorizer
return &accessControlClient
}
28 changes: 28 additions & 0 deletions azurerm/internal/services/synapse/parse/synapse_role_assignment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package parse

import (
"fmt"
"strings"
)

type SynapseRoleAssignmentId struct {
Workspace *SynapseWorkspaceId
Id string
}

func SynapseRoleAssignmentID(input string) (*SynapseRoleAssignmentId, error) {
segments := strings.Split(input, "|")
if len(segments) != 2 {
return nil, fmt.Errorf("expected an ID in the format `{workspaceId}|{id} but got %q", input)
}

workspaceId, err := SynapseWorkspaceID(segments[0])
if err != nil {
return nil, err
}

return &SynapseRoleAssignmentId{
Workspace: workspaceId,
Id: segments[1],
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package parse

import (
"testing"
)

func TestSynapseRoleAssignmentID(t *testing.T) {
testData := []struct {
Name string
Input string
Expected *SynapseRoleAssignmentId
}{
{
Name: "Empty",
Input: "",
Expected: nil,
},
{
Name: "Missing Synapse Role Assignment ID part",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1",
Expected: nil,
},
{
Name: "Missing Synapse Workspace ID part",
Input: "00000000",
Expected: nil,
},
{
Name: "synapse Role Assignment ID",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1|00000000",
Expected: &SynapseRoleAssignmentId{
Workspace: &SynapseWorkspaceId{
ResourceGroup: "resourceGroup1",
Name: "workspace1",
},
Id: "00000000",
},
},
{
Name: "Wrong Casing",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/Workspaces/workspace1|00000000",
Expected: nil,
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q..", v.Name)

actual, err := SynapseRoleAssignmentID(v.Input)
if err != nil {
if v.Expected == nil {
continue
}
t.Fatalf("Expected a value but got an error: %s", err)
}

if actual.Workspace.ResourceGroup != v.Expected.Workspace.ResourceGroup {
t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.Workspace.ResourceGroup, actual.Workspace.ResourceGroup)
}

if actual.Workspace.Name != v.Expected.Workspace.Name {
t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.Workspace.Name, actual.Workspace.Name)
}

if actual.Id != v.Expected.Id {
t.Fatalf("Expected %q but got %q for Name", v.Expected.Id, actual.Id)
}
}
}
9 changes: 5 additions & 4 deletions azurerm/internal/services/synapse/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource {
// SupportedResources returns the supported Resources supported by this Service
func (r Registration) SupportedResources() map[string]*schema.Resource {
return map[string]*schema.Resource{
"azurerm_synapse_firewall_rule": resourceArmSynapseFirewallRule(),
"azurerm_synapse_spark_pool": resourceArmSynapseSparkPool(),
"azurerm_synapse_sql_pool": resourceArmSynapseSqlPool(),
"azurerm_synapse_workspace": resourceArmSynapseWorkspace(),
"azurerm_synapse_firewall_rule": resourceArmSynapseFirewallRule(),
"azurerm_synapse_role_assignment": resourceArmSynapseRoleAssignment(),
"azurerm_synapse_spark_pool": resourceArmSynapseSparkPool(),
"azurerm_synapse_sql_pool": resourceArmSynapseSqlPool(),
"azurerm_synapse_workspace": resourceArmSynapseWorkspace(),
}
}
191 changes: 191 additions & 0 deletions azurerm/internal/services/synapse/synapse_role_assignment_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package synapse

import (
"context"
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/synapse/2020-02-01-preview/accesscontrol"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/synapse/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/synapse/validate"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmSynapseRoleAssignment() *schema.Resource {
return &schema.Resource{
Create: resourceArmSynapseRoleAssignmentCreate,
Read: resourceArmSynapseRoleAssignmentRead,
Delete: resourceArmSynapseRoleAssignmentDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.SynapseRoleAssignmentID(id)
return err
}),

Schema: map[string]*schema.Schema{
"synapse_workspace_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.SynapseWorkspaceID,
},

"principal_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.IsUUID,
},

"role_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
njuCZ marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
}

func resourceArmSynapseRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) error {
synapseClient := meta.(*clients.Client).Synapse
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()
environment := meta.(*clients.Client).Account.Environment

workspaceId, err := parse.SynapseWorkspaceID(d.Get("synapse_workspace_id").(string))
if err != nil {
return err
}
principalID := d.Get("principal_id").(string)
roleName := d.Get("role_name").(string)

client := synapseClient.AccessControlClient(workspaceId.Name, environment.SynapseEndpointSuffix)
roleId, err := getRoleIdByName(ctx, client, roleName)
if err != nil {
return err
}

// check exist
listResp, err := client.GetRoleAssignments(ctx, roleId, principalID, "")
if err != nil {
if !utils.ResponseWasNotFound(listResp.Response) {
return fmt.Errorf("checking for present of existing Synapse Role Assignment (workspace %q): %+v", workspaceId.Name, err)
}
}
if listResp.Value != nil && len(*listResp.Value) != 0 {
existing := (*listResp.Value)[0]
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_synapse_role_assignment", *existing.ID)
}
}

// create
roleAssignment := accesscontrol.RoleAssignmentOptions{
RoleID: utils.String(roleId),
PrincipalID: utils.String(principalID),
}
resp, err := client.CreateRoleAssignment(ctx, roleAssignment)
if err != nil {
return fmt.Errorf("creating Synapse RoleAssignment %q: %+v", roleName, err)
}

if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("empty or nil ID returned for Synapse RoleAssignment %q", roleName)
}

id := fmt.Sprintf("%s|%s", workspaceId.String(), *resp.ID)
d.SetId(id)
return resourceArmSynapseRoleAssignmentRead(d, meta)
}

func resourceArmSynapseRoleAssignmentRead(d *schema.ResourceData, meta interface{}) error {
synapseClient := meta.(*clients.Client).Synapse
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()
environment := meta.(*clients.Client).Account.Environment

id, err := parse.SynapseRoleAssignmentID(d.Id())
if err != nil {
return err
}

client := synapseClient.AccessControlClient(id.Workspace.Name, environment.SynapseEndpointSuffix)
resp, err := client.GetRoleAssignmentByID(ctx, id.Id)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] synapse role assignment %q does not exist - removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("retrieving Synapse RoleAssignment (Resource Group %q): %+v", id.Workspace.Name, err)
}

if resp.RoleID != nil {
role, err := client.GetRoleDefinitionByID(ctx, *resp.RoleID)
if err != nil {
return fmt.Errorf("retrieving role definition by ID %q: %+v", *resp.RoleID, err)
}
d.Set("role_name", role.Name)
}

d.Set("synapse_workspace_id", id.Workspace.String())
d.Set("principal_id", resp.PrincipalID)
return nil
}

func resourceArmSynapseRoleAssignmentDelete(d *schema.ResourceData, meta interface{}) error {
synapseClient := meta.(*clients.Client).Synapse
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()
environment := meta.(*clients.Client).Account.Environment

id, err := parse.SynapseRoleAssignmentID(d.Id())
if err != nil {
return err
}

client := synapseClient.AccessControlClient(id.Workspace.Name, environment.SynapseEndpointSuffix)
if _, err := client.DeleteRoleAssignmentByID(ctx, id.Id); err != nil {
return fmt.Errorf("deleting Synapse RoleAssignment %q (workspace %q): %+v", id, id.Workspace.Name, err)
}

return nil
}

func getRoleIdByName(ctx context.Context, client *accesscontrol.BaseClient, roleName string) (string, error) {
resp, err := client.GetRoleDefinitions(ctx)
if err != nil {
return "", fmt.Errorf("listing synapse role definitions %+v", err)
}

var availableRoleName []string
for resp.NotDone() {
for _, role := range resp.Values() {
if role.Name != nil {
if *role.Name == roleName && role.ID != nil {
return *role.ID, nil
}
availableRoleName = append(availableRoleName, *role.Name)
}
}
if err := resp.NextWithContext(ctx); err != nil {
return "", fmt.Errorf("retrieving next page of synapse role definitions: %+v", err)
}
}
return "", fmt.Errorf("role name %q invalid. Available role names are %q", roleName, strings.Join(availableRoleName, ","))
}
Loading