Skip to content

Commit

Permalink
New resource: azurerm_synapse_workspace_aad_admin - supports settin…
Browse files Browse the repository at this point in the history
…g AAD admin for synapse workspaces with CMK (hashicorp#13600)

Fixes hashicorp#13195
  • Loading branch information
tism authored Nov 11, 2021
1 parent 57eefd3 commit 590f7fa
Show file tree
Hide file tree
Showing 11 changed files with 799 additions and 10 deletions.
75 changes: 75 additions & 0 deletions internal/services/synapse/parse/workspace_aad_admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package parse

// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten

import (
"fmt"
"strings"

"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
)

type WorkspaceAADAdminId struct {
SubscriptionId string
ResourceGroup string
WorkspaceName string
AdministratorName string
}

func NewWorkspaceAADAdminID(subscriptionId, resourceGroup, workspaceName, administratorName string) WorkspaceAADAdminId {
return WorkspaceAADAdminId{
SubscriptionId: subscriptionId,
ResourceGroup: resourceGroup,
WorkspaceName: workspaceName,
AdministratorName: administratorName,
}
}

func (id WorkspaceAADAdminId) String() string {
segments := []string{
fmt.Sprintf("Administrator Name %q", id.AdministratorName),
fmt.Sprintf("Workspace Name %q", id.WorkspaceName),
fmt.Sprintf("Resource Group %q", id.ResourceGroup),
}
segmentsStr := strings.Join(segments, " / ")
return fmt.Sprintf("%s: (%s)", "Workspace A A D Admin", segmentsStr)
}

func (id WorkspaceAADAdminId) ID() string {
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Synapse/workspaces/%s/administrators/%s"
return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.AdministratorName)
}

// WorkspaceAADAdminID parses a WorkspaceAADAdmin ID into an WorkspaceAADAdminId struct
func WorkspaceAADAdminID(input string) (*WorkspaceAADAdminId, error) {
id, err := azure.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

resourceId := WorkspaceAADAdminId{
SubscriptionId: id.SubscriptionID,
ResourceGroup: id.ResourceGroup,
}

if resourceId.SubscriptionId == "" {
return nil, fmt.Errorf("ID was missing the 'subscriptions' element")
}

if resourceId.ResourceGroup == "" {
return nil, fmt.Errorf("ID was missing the 'resourceGroups' element")
}

if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil {
return nil, err
}
if resourceId.AdministratorName, err = id.PopSegment("administrators"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &resourceId, nil
}
128 changes: 128 additions & 0 deletions internal/services/synapse/parse/workspace_aad_admin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package parse

// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten

import (
"testing"

"github.com/hashicorp/terraform-provider-azurerm/internal/resourceid"
)

var _ resourceid.Formatter = WorkspaceAADAdminId{}

func TestWorkspaceAADAdminIDFormatter(t *testing.T) {
actual := NewWorkspaceAADAdminID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "workspace1", "activeDirectory").ID()
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1/administrators/activeDirectory"
if actual != expected {
t.Fatalf("Expected %q but got %q", expected, actual)
}
}

func TestWorkspaceAADAdminID(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *WorkspaceAADAdminId
}{

{
// empty
Input: "",
Error: true,
},

{
// missing SubscriptionId
Input: "/",
Error: true,
},

{
// missing value for SubscriptionId
Input: "/subscriptions/",
Error: true,
},

{
// missing ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/",
Error: true,
},

{
// missing value for ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/",
Error: true,
},

{
// missing WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/",
Error: true,
},

{
// missing value for WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/",
Error: true,
},

{
// missing AdministratorName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1/",
Error: true,
},

{
// missing value for AdministratorName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1/administrators/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1/administrators/activeDirectory",
Expected: &WorkspaceAADAdminId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resourceGroup1",
WorkspaceName: "workspace1",
AdministratorName: "activeDirectory",
},
},

{
// upper-cased
Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.SYNAPSE/WORKSPACES/WORKSPACE1/ADMINISTRATORS/ACTIVEDIRECTORY",
Error: true,
},
}

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

actual, err := WorkspaceAADAdminID(v.Input)
if err != nil {
if v.Error {
continue
}

t.Fatalf("Expect a value but got an error: %s", err)
}
if v.Error {
t.Fatal("Expect an error but didn't get one")
}

if actual.SubscriptionId != v.Expected.SubscriptionId {
t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId)
}
if actual.ResourceGroup != v.Expected.ResourceGroup {
t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup)
}
if actual.WorkspaceName != v.Expected.WorkspaceName {
t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName)
}
if actual.AdministratorName != v.Expected.AdministratorName {
t.Fatalf("Expected %q but got %q for AdministratorName", v.Expected.AdministratorName, actual.AdministratorName)
}
}
}
1 change: 1 addition & 0 deletions internal/services/synapse/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource {
"azurerm_synapse_sql_pool_vulnerability_assessment": resourceSynapseSqlPoolVulnerabilityAssessment(),
"azurerm_synapse_sql_pool_vulnerability_assessment_baseline": resourceSynapseSqlPoolVulnerabilityAssessmentBaseline(),
"azurerm_synapse_workspace": resourceSynapseWorkspace(),
"azurerm_synapse_workspace_aad_admin": resourceSynapseWorkspaceAADAdmin(),
"azurerm_synapse_workspace_extended_auditing_policy": resourceSynapseWorkspaceExtendedAuditingPolicy(),
"azurerm_synapse_workspace_key": resourceSynapseWorkspaceKey(),
"azurerm_synapse_workspace_security_alert_policy": resourceSynapseWorkspaceSecurityAlertPolicy(),
Expand Down
1 change: 1 addition & 0 deletions internal/services/synapse/resourceids.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package synapse
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolVulnerabilityAssessment -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/vulnerabilityAssessments/default
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolVulnerabilityAssessmentBaseline -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/vulnerabilityAssessments/default/rules/rule1/baselines/baseline1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=WorkspaceAADAdmin -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.Synapse/workspaces/workspace1/administrators/activeDirectory
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=WorkspaceExtendedAuditingPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/extendedAuditingSettings/default
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=WorkspaceKeys -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/keys/key1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=WorkspaceSecurityAlertPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/securityAlertPolicies/Default
Expand Down
146 changes: 146 additions & 0 deletions internal/services/synapse/synapse_workspace_aad_admin_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package synapse

import (
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/services/synapse/mgmt/2021-03-01/synapse"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

func resourceSynapseWorkspaceAADAdmin() *pluginsdk.Resource {
return &pluginsdk.Resource{
Create: resourceSynapseWorkspaceAADAdminCreateUpdate,
Read: resourceSynapseWorkspaceAADAdminRead,
Update: resourceSynapseWorkspaceAADAdminCreateUpdate,
Delete: resourceSynapseWorkspaceAADAdminDelete,

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

Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
_, err := parse.WorkspaceAADAdminID(id)
return err
}),

Schema: map[string]*pluginsdk.Schema{
"synapse_workspace_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.WorkspaceID,
},

"login": {
Type: pluginsdk.TypeString,
Required: true,
},

"object_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsUUID,
},

"tenant_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsUUID,
},
},
}
}

func resourceSynapseWorkspaceAADAdminCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Synapse.WorkspaceAadAdminsClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

workspaceId, err := parse.WorkspaceID(d.Get("synapse_workspace_id").(string))
if err != nil {
return err
}
workspaceName := workspaceId.Name
workspaceResourceGroup := workspaceId.ResourceGroup

aadAdmin := &synapse.WorkspaceAadAdminInfo{
AadAdminProperties: &synapse.AadAdminProperties{
TenantID: utils.String(d.Get("tenant_id").(string)),
Login: utils.String(d.Get("login").(string)),
AdministratorType: utils.String("ActiveDirectory"),
Sid: utils.String(d.Get("object_id").(string)),
},
}

workspaceAadAdminsCreateOrUpdateFuture, err := client.CreateOrUpdate(ctx, workspaceResourceGroup, workspaceName, *aadAdmin)
if err != nil {
return fmt.Errorf("updating Synapse Workspace %q AAD Admin (Resource Group %q): %+v", workspaceName, workspaceResourceGroup, err)
}

if err = workspaceAadAdminsCreateOrUpdateFuture.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting on updating for Synapse Workspace %q AAD Admin (Resource Group %q): %+v", workspaceName, workspaceResourceGroup, err)
}

id := parse.NewWorkspaceAADAdminID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.Name, "activeDirectory")
d.SetId(id.ID())

return resourceSynapseWorkspaceAADAdminRead(d, meta)
}

func resourceSynapseWorkspaceAADAdminRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Synapse.WorkspaceAadAdminsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

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

aadAdmin, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName)
if err != nil {
if !utils.ResponseWasNotFound(aadAdmin.Response) {
return fmt.Errorf("retrieving Synapse Workspace %q AAD Admin (Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err)
}
}

workspaceID := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName)

d.Set("synapse_workspace_id", workspaceID.ID())
d.Set("login", aadAdmin.AadAdminProperties.Login)
d.Set("object_id", aadAdmin.AadAdminProperties.Sid)
d.Set("tenant_id", aadAdmin.AadAdminProperties.TenantID)

return nil
}

func resourceSynapseWorkspaceAADAdminDelete(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Synapse.WorkspaceAadAdminsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

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

future, err := client.Delete(ctx, id.ResourceGroup, id.WorkspaceName)
if err != nil {
return fmt.Errorf("setting empty Synapse Workspace %q AAD Admin (Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting on setting empty Synapse Workspace %q AAD Admin (Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err)
}

return nil
}
Loading

0 comments on commit 590f7fa

Please sign in to comment.