diff --git a/azurerm/helpers/azure/kusto.go b/azurerm/helpers/azure/kusto.go new file mode 100644 index 0000000000000..e77599b4fa3b0 --- /dev/null +++ b/azurerm/helpers/azure/kusto.go @@ -0,0 +1,91 @@ +package azure + +import ( + "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/suppress" +) + +func SchemaKustoIdentity() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(kusto.IdentityTypeNone), + string(kusto.IdentityTypeSystemAssigned), + }, true), + DiffSuppressFunc: suppress.CaseDifference, + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + "identity_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + } +} + +func ExpandKustoIdentity(input []interface{}) *kusto.Identity { + if len(input) == 0 || input[0] == nil { + return nil + } + identity := input[0].(map[string]interface{}) + identityType := kusto.IdentityType(identity["type"].(string)) + + kustoIdentity := kusto.Identity{ + Type: identityType, + } + + return &kustoIdentity +} + +func FlattenKustoIdentity(input *kusto.Identity) []interface{} { + if input == nil || input.Type == kusto.IdentityTypeNone { + return []interface{}{} + } + + identityIds := make([]string, 0) + if input.UserAssignedIdentities != nil { + for k := range input.UserAssignedIdentities { + identityIds = append(identityIds, k) + } + } + + principalID := "" + if input.PrincipalID != nil { + principalID = *input.PrincipalID + } + + tenantID := "" + if input.TenantID != nil { + tenantID = *input.TenantID + } + + return []interface{}{ + map[string]interface{}{ + "type": string(input.Type), + "identity_ids": identityIds, + "principal_id": principalID, + "tenant_id": tenantID, + }, + } +} diff --git a/azurerm/internal/services/kusto/kusto_cluster_resource.go b/azurerm/internal/services/kusto/kusto_cluster_resource.go index bcb69cdb468ce..db50517de0a60 100644 --- a/azurerm/internal/services/kusto/kusto_cluster_resource.go +++ b/azurerm/internal/services/kusto/kusto_cluster_resource.go @@ -50,6 +50,8 @@ func resourceArmKustoCluster() *schema.Resource { "location": azure.SchemaLocation(), + "identity": azure.SchemaKustoIdentity(), + "sku": { Type: schema.TypeList, Required: true, @@ -169,6 +171,12 @@ func resourceArmKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{ Tags: tags.Expand(t), } + if _, ok := d.GetOk("identity"); ok { + kustoIdentityRaw := d.Get("identity").([]interface{}) + kustoIdentity := azure.ExpandKustoIdentity(kustoIdentityRaw) + kustoCluster.Identity = kustoIdentity + } + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, kustoCluster) if err != nil { return fmt.Errorf("Error creating or updating Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) @@ -219,6 +227,10 @@ func resourceArmKustoClusterRead(d *schema.ResourceData, meta interface{}) error d.Set("location", azure.NormalizeLocation(*location)) } + if err := d.Set("identity", azure.FlattenKustoIdentity(clusterResponse.Identity)); err != nil { + return fmt.Errorf("Error setting `identity`: %s", err) + } + if err := d.Set("sku", flattenKustoClusterSku(clusterResponse.Sku)); err != nil { return fmt.Errorf("Error setting `sku`: %+v", err) } diff --git a/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go b/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go index ff6009d0ece42..3c6128c93e159 100644 --- a/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go +++ b/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -129,6 +130,28 @@ func TestAccAzureRMKustoCluster_sku(t *testing.T) { }) } +func TestAccAzureRMKustoCluster_identitySystemAssigned(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMKustoClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMKustoCluster_identitySystemAssigned(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "identity.0.type", "SystemAssigned"), + resource.TestCheckResourceAttr(data.ResourceName, "identity.0.identity_ids.#", "0"), + resource.TestMatchResourceAttr(data.ResourceName, "identity.0.principal_id", validate.UUIDRegExp), + ), + }, + data.ImportStep(), + }, + }) +} + func testAccAzureRMKustoCluster_basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -261,6 +284,34 @@ resource "azurerm_kusto_cluster" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } +func testAccAzureRMKustoCluster_identitySystemAssigned(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } + + identity { + type = "SystemAssigned" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + func testCheckAzureRMKustoClusterDestroy(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).Kusto.ClustersClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext diff --git a/website/docs/r/kusto_cluster.html.markdown b/website/docs/r/kusto_cluster.html.markdown index 91a0b95c87013..a73e9198c0890 100644 --- a/website/docs/r/kusto_cluster.html.markdown +++ b/website/docs/r/kusto_cluster.html.markdown @@ -46,6 +46,8 @@ The following arguments are supported: * `sku` - (Required) A `sku` block as defined below. +* `identity` - (Optional) A identity block. + * `enable_disk_encryption` - (Optional) Specifies if the cluster's disks are encrypted. * `enable_streaming_ingest` - (Optional) Specifies if the streaming ingest is enabled. @@ -62,6 +64,19 @@ A `sku` block supports the following: * `capacity` - (Required) Specifies the node count for the cluster. Boundaries depend on the sku name. +--- + +An `identity` block supports the following: + +* `type` - (Required) Specifies the type of Managed Service Identity that is configured on this Kusto Cluster. Possible values are: `SystemAssigned` (where Azure will generate a Service Principal for you). + +* `principal_id` - (Computed) Specifies the Principal ID of the System Assigned Managed Service Identity that is configured on this Kusto Cluster. + +* `tenant_id` - (Computed) Specifies the Tenant ID of the System Assigned Managed Service Identity that is configured on this Kusto Cluster. + +* `identity_ids` - (Computed) The list of user identities associated with the Kusto cluster. + +~> **NOTE:** When `type` is set to `SystemAssigned`, the Principal ID can be retrieved after the cluster has been created. More details are available below. See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview) for additional information. ## Attributes Reference