-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Kusto Database Principal Assignment Support
- Loading branch information
1 parent
6f80ed1
commit a7c9fa6
Showing
7 changed files
with
620 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
248 changes: 248 additions & 0 deletions
248
azurerm/internal/services/kusto/kusto_database_principal_assignment_resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
package kusto | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"regexp" | ||
"time" | ||
|
||
"github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2020-02-15/kusto" | ||
"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/azure" | ||
"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/kusto/parse" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
func resourceArmKustoDatabasePrincipalAssignment() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceArmKustoDatabasePrincipalAssignmentCreateUpdate, | ||
Read: resourceArmKustoDatabasePrincipalAssignmentRead, | ||
Delete: resourceArmKustoDatabasePrincipalAssignmentDelete, | ||
|
||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Timeouts: &schema.ResourceTimeout{ | ||
Create: schema.DefaultTimeout(60 * time.Minute), | ||
Read: schema.DefaultTimeout(5 * time.Minute), | ||
Update: schema.DefaultTimeout(60 * time.Minute), | ||
Delete: schema.DefaultTimeout(60 * time.Minute), | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"resource_group_name": azure.SchemaResourceGroupName(), | ||
|
||
"cluster_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateAzureRMKustoClusterName, | ||
}, | ||
|
||
"database_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateAzureRMKustoDatabaseName, | ||
}, | ||
|
||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateAzureRMKustoDatabasePrincipalAssignmentName, | ||
}, | ||
|
||
"tenant_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringIsNotEmpty, | ||
}, | ||
|
||
"tenant_name": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"principal_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringIsNotEmpty, | ||
}, | ||
|
||
"principal_name": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"principal_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
string(kusto.PrincipalTypeApp), | ||
string(kusto.PrincipalTypeGroup), | ||
string(kusto.PrincipalTypeUser), | ||
}, false), | ||
}, | ||
|
||
"role": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
string(kusto.Admin), | ||
string(kusto.Ingestor), | ||
string(kusto.Monitor), | ||
string(kusto.User), | ||
string(kusto.UnrestrictedViewers), | ||
string(kusto.Viewer), | ||
}, false), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceArmKustoDatabasePrincipalAssignmentCreateUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Kusto.DatabasePrincipalAssignmentsClient | ||
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
log.Printf("[INFO] preparing arguments for Azure Kusto Database Principal Assignment creation.") | ||
|
||
resourceGroup := d.Get("resource_group_name").(string) | ||
clusterName := d.Get("cluster_name").(string) | ||
databaseName := d.Get("database_name").(string) | ||
name := d.Get("name").(string) | ||
|
||
if d.IsNewResource() { | ||
principalAssignment, err := client.Get(ctx, resourceGroup, clusterName, databaseName, name) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(principalAssignment.Response) { | ||
return fmt.Errorf("Error checking for presence of existing Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", name, resourceGroup, clusterName, databaseName, err) | ||
} | ||
} | ||
|
||
if principalAssignment.ID != nil && *principalAssignment.ID != "" { | ||
return tf.ImportAsExistsError("azurerm_kusto_database_principal_assignment", *principalAssignment.ID) | ||
} | ||
} | ||
|
||
tenantID := d.Get("tenant_id").(string) | ||
principalID := d.Get("principal_id").(string) | ||
principalType := d.Get("principal_type").(string) | ||
role := d.Get("role").(string) | ||
|
||
props := kusto.DatabasePrincipalProperties{ | ||
TenantID: utils.String(tenantID), | ||
PrincipalID: utils.String(principalID), | ||
PrincipalType: kusto.PrincipalType(principalType), | ||
Role: kusto.DatabasePrincipalRole(role), | ||
} | ||
|
||
principalAssignment := kusto.DatabasePrincipalAssignment{ | ||
DatabasePrincipalProperties: &props, | ||
} | ||
|
||
future, err := client.CreateOrUpdate(ctx, resourceGroup, clusterName, databaseName, name, principalAssignment) | ||
if err != nil { | ||
return fmt.Errorf("Error creating or updating Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", name, resourceGroup, clusterName, databaseName, err) | ||
} | ||
|
||
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { | ||
return fmt.Errorf("Error waiting for completion of Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", name, resourceGroup, clusterName, databaseName, err) | ||
} | ||
|
||
resp, err := client.Get(ctx, resourceGroup, clusterName, databaseName, name) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", name, resourceGroup, clusterName, databaseName, err) | ||
} | ||
|
||
if resp.ID == nil { | ||
return fmt.Errorf("Cannot read ID for Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q)", name, resourceGroup, clusterName, databaseName) | ||
} | ||
|
||
d.SetId(*resp.ID) | ||
|
||
return resourceArmKustoDatabasePrincipalAssignmentRead(d, meta) | ||
} | ||
|
||
func resourceArmKustoDatabasePrincipalAssignmentRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Kusto.DatabasePrincipalAssignmentsClient | ||
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.KustoDatabasePrincipalAssignmentID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Get(ctx, id.ResourceGroup, id.Cluster, id.Database, id.Name) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("Error retrieving Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", id.Name, id.ResourceGroup, id.Cluster, id.Database, err) | ||
} | ||
|
||
d.Set("resource_group_name", id.ResourceGroup) | ||
d.Set("cluster_name", id.Cluster) | ||
d.Set("database_name", id.Database) | ||
d.Set("name", id.Name) | ||
d.Set("tenant_id", *resp.TenantID) | ||
d.Set("tenant_name", *resp.TenantName) | ||
d.Set("principal_id", *resp.PrincipalID) | ||
d.Set("principal_name", *resp.PrincipalName) | ||
d.Set("principal_type", string(resp.PrincipalType)) | ||
d.Set("role", string(resp.Role)) | ||
|
||
return nil | ||
} | ||
|
||
func resourceArmKustoDatabasePrincipalAssignmentDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Kusto.DatabasePrincipalAssignmentsClient | ||
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.KustoDatabasePrincipalAssignmentID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
future, err := client.Delete(ctx, id.ResourceGroup, id.Cluster, id.Database, id.Name) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", id.Name, id.ResourceGroup, id.Cluster, id.Database, err) | ||
} | ||
|
||
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { | ||
return fmt.Errorf("Error waiting for deletion of Kusto Database Principal Assignment %q (Resource Group %q, Cluster %q, Database %q): %+v", id.Name, id.ResourceGroup, id.Cluster, id.Database, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func validateAzureRMKustoDatabasePrincipalAssignmentName(v interface{}, k string) (warnings []string, errors []error) { | ||
name := v.(string) | ||
|
||
if regexp.MustCompile(`^[\s]+$`).MatchString(name) { | ||
errors = append(errors, fmt.Errorf("%q must not consist of whitespaces only", k)) | ||
} | ||
|
||
if !regexp.MustCompile(`^[a-zA-Z0-9\s.-]+$`).MatchString(name) { | ||
errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters, whitespaces, dashes and dots: %q", k, name)) | ||
} | ||
|
||
if len(name) > 260 { | ||
errors = append(errors, fmt.Errorf("%q must be (inclusive) between 4 and 22 characters long but is %d", k, len(name))) | ||
} | ||
|
||
return warnings, errors | ||
} |
43 changes: 43 additions & 0 deletions
43
azurerm/internal/services/kusto/parse/kusto_database_principal_assignment.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package parse | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" | ||
) | ||
|
||
type KustoDatabasePrincipalAssignmentId struct { | ||
ResourceGroup string | ||
Cluster string | ||
Database string | ||
Name string | ||
} | ||
|
||
func KustoDatabasePrincipalAssignmentID(input string) (*KustoDatabasePrincipalAssignmentId, error) { | ||
id, err := azure.ParseAzureResourceID(input) | ||
if err != nil { | ||
return nil, fmt.Errorf("[ERROR] Unable to parse Kusto Database Principal ID %q: %+v", input, err) | ||
} | ||
|
||
principal := KustoDatabasePrincipalAssignmentId{ | ||
ResourceGroup: id.ResourceGroup, | ||
} | ||
|
||
if principal.Cluster, err = id.PopSegment("Clusters"); err != nil { | ||
return nil, err | ||
} | ||
|
||
if principal.Database, err = id.PopSegment("Databases"); err != nil { | ||
return nil, err | ||
} | ||
|
||
if principal.Name, err = id.PopSegment("PrincipalAssignments"); err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := id.ValidateNoEmptySegments(input); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &principal, nil | ||
} |
68 changes: 68 additions & 0 deletions
68
azurerm/internal/services/kusto/parse/kusto_database_principal_assignment_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package parse | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestKustoDatabasePrincipalAssignmentId(t *testing.T) { | ||
testData := []struct { | ||
Name string | ||
Input string | ||
Expected *KustoDatabasePrincipalAssignmentId | ||
}{ | ||
{ | ||
Name: "Empty", | ||
Input: "", | ||
Expected: nil, | ||
}, | ||
{ | ||
Name: "Missing Cluster", | ||
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Kusto/Databases/database1/PrincipalAssignments/assignment1", | ||
Expected: nil, | ||
}, | ||
{ | ||
Name: "Missing Database", | ||
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Kusto/Clusters/cluster1/PrincipalAssignments/assignment1", | ||
Expected: nil, | ||
}, | ||
{ | ||
Name: "Database Principal Assignment ID", | ||
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Kusto/Clusters/cluster1/Databases/database1/PrincipalAssignments/assignment1", | ||
Expected: &KustoDatabasePrincipalAssignmentId{ | ||
Name: "assignment1", | ||
Database: "database1", | ||
Cluster: "cluster1", | ||
ResourceGroup: "group1", | ||
}, | ||
}, | ||
} | ||
|
||
for _, v := range testData { | ||
t.Logf("[DEBUG] Testing %q", v.Name) | ||
|
||
actual, err := KustoDatabasePrincipalAssignmentID(v.Input) | ||
if err != nil { | ||
if v.Expected == nil { | ||
continue | ||
} | ||
|
||
t.Fatalf("Expected a value but got an error: %s", err) | ||
} | ||
|
||
if actual.Name != v.Expected.Name { | ||
t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) | ||
} | ||
|
||
if actual.Database != v.Expected.Database { | ||
t.Fatalf("Expected %q but got %q for Database", v.Expected.Database, actual.Database) | ||
} | ||
|
||
if actual.Cluster != v.Expected.Cluster { | ||
t.Fatalf("Expected %q but got %q for Cluster", v.Expected.Cluster, actual.Cluster) | ||
} | ||
|
||
if actual.ResourceGroup != v.Expected.ResourceGroup { | ||
t.Fatalf("Expected %q but got %q for Resource Group", v.Expected.ResourceGroup, actual.ResourceGroup) | ||
} | ||
} | ||
} |
Oops, something went wrong.