Skip to content

Commit

Permalink
azurerm_kusto_cluster - supports double_encryption_enabled, systemA…
Browse files Browse the repository at this point in the history
…ssignedIdentity and systemAssignedUserAssignedIdentity (#10264)

upport new fields: "double_encryption_enabled"
enhancement for identity block: support systemAssignedIdentity and systemAssignedUserAssignedIdentity
once more validation item for sku name

the acctest "TestAccKustoCluster_zones" failed either in master branch, I have emailed the service team

acording to docs.microsoft.com/en-us/azure/data-explorer/double-encryption?tabs=portal

Enabling double encryption is only possible during cluster creation.
Once infrastructure encryption is enabled on your cluster, you can't disable it.

We could infer that it's a forcenew field
  • Loading branch information
njuCZ authored Mar 1, 2021
1 parent 19f2acf commit bbe21d8
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 34 deletions.
51 changes: 36 additions & 15 deletions azurerm/internal/services/kusto/identity.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package kusto

import (
"fmt"

"github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2020-09-18/kusto"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
Expand All @@ -22,44 +24,63 @@ func schemaIdentity() *schema.Schema {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(kusto.IdentityTypeNone),
string(kusto.IdentityTypeSystemAssigned),
string(kusto.IdentityTypeUserAssigned),
string(kusto.IdentityTypeSystemAssignedUserAssigned),
}, true),
DiffSuppressFunc: suppress.CaseDifference,
},

"identity_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: msivalidate.UserAssignedIdentityID,
},
},

"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,
ValidateFunc: msivalidate.UserAssignedIdentityID,
},
},
},
},
}
}

func expandIdentity(input []interface{}) *kusto.Identity {
func expandIdentity(input []interface{}) (*kusto.Identity, error) {
if len(input) == 0 || input[0] == nil {
return nil
return &kusto.Identity{
Type: kusto.IdentityTypeNone,
}, nil
}
identity := input[0].(map[string]interface{})
identityType := kusto.IdentityType(identity["type"].(string))
raw := input[0].(map[string]interface{})

kustoIdentity := kusto.Identity{
Type: identityType,
Type: kusto.IdentityType(raw["type"].(string)),
}

identityIdsRaw := raw["identity_ids"].(*schema.Set).List()
identityIds := make(map[string]*kusto.IdentityUserAssignedIdentitiesValue)
for _, v := range identityIdsRaw {
identityIds[v.(string)] = &kusto.IdentityUserAssignedIdentitiesValue{}
}

if len(identityIds) > 0 {
if kustoIdentity.Type != kusto.IdentityTypeUserAssigned && kustoIdentity.Type != kusto.IdentityTypeSystemAssignedUserAssigned {
return nil, fmt.Errorf("`identity_ids` can only be specified when `type` includes `UserAssigned`")
}

kustoIdentity.UserAssignedIdentities = identityIds
}

return &kustoIdentity
return &kustoIdentity, nil
}

func flattenIdentity(input *kusto.Identity) ([]interface{}, error) {
Expand Down
34 changes: 24 additions & 10 deletions azurerm/internal/services/kusto/kusto_cluster_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
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"
)
Expand All @@ -26,9 +27,10 @@ func resourceKustoCluster() *schema.Resource {
Update: resourceKustoClusterCreateUpdate,
Delete: resourceKustoClusterDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.ClusterID(id)
return err
}),

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(60 * time.Minute),
Expand Down Expand Up @@ -76,6 +78,7 @@ func resourceKustoCluster() *schema.Resource {
string(kusto.StandardE16aV4),
string(kusto.StandardE2aV4),
string(kusto.StandardE4aV4),
string(kusto.StandardE64iV3),
string(kusto.StandardE8asV41TBPS),
string(kusto.StandardE8asV42TBPS),
string(kusto.StandardE8aV4),
Expand Down Expand Up @@ -126,6 +129,12 @@ func resourceKustoCluster() *schema.Resource {
},
},

"double_encryption_enabled": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},

"enable_disk_encryption": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -263,11 +272,12 @@ func resourceKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{})
engine := kusto.EngineType(d.Get("engine").(string))

clusterProperties := kusto.ClusterProperties{
OptimizedAutoscale: optimizedAutoScale,
EnableDiskEncryption: utils.Bool(d.Get("enable_disk_encryption").(bool)),
EnableStreamingIngest: utils.Bool(d.Get("enable_streaming_ingest").(bool)),
EnablePurge: utils.Bool(d.Get("enable_purge").(bool)),
EngineType: engine,
OptimizedAutoscale: optimizedAutoScale,
EnableDiskEncryption: utils.Bool(d.Get("enable_disk_encryption").(bool)),
EnableDoubleEncryption: utils.Bool(d.Get("double_encryption_enabled").(bool)),
EnableStreamingIngest: utils.Bool(d.Get("enable_streaming_ingest").(bool)),
EnablePurge: utils.Bool(d.Get("enable_purge").(bool)),
EngineType: engine,
}

if v, ok := d.GetOk("virtual_network_configuration"); ok {
Expand All @@ -292,8 +302,11 @@ func resourceKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{})
}

if _, ok := d.GetOk("identity"); ok {
kustoIdentityRaw := d.Get("identity").([]interface{})
kustoIdentity := expandIdentity(kustoIdentityRaw)
kustoIdentity, err := expandIdentity(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf("expanding `identity`: %+v", err)
}

kustoCluster.Identity = kustoIdentity
}

Expand Down Expand Up @@ -406,6 +419,7 @@ func resourceKustoClusterRead(d *schema.ResourceData, meta interface{}) error {
}

if clusterProperties := clusterResponse.ClusterProperties; clusterProperties != nil {
d.Set("double_encryption_enabled", clusterProperties.EnableDoubleEncryption)
d.Set("trusted_external_tenants", flattenTrustedExternalTenants(clusterProperties.TrustedExternalTenants))
d.Set("enable_disk_encryption", clusterProperties.EnableDiskEncryption)
d.Set("enable_streaming_ingest", clusterProperties.EnableStreamingIngest)
Expand Down
144 changes: 144 additions & 0 deletions azurerm/internal/services/kusto/kusto_cluster_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ func TestAccKustoCluster_update(t *testing.T) {
})
}

func TestAccKustoCluster_doubleEncryption(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test")
r := KustoClusterResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.doubleEncryption(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccKustoCluster_withTags(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test")
r := KustoClusterResource{}
Expand Down Expand Up @@ -154,6 +169,40 @@ func TestAccKustoCluster_identitySystemAssigned(t *testing.T) {
})
}

func TestAccKustoCluster_UserAssignedIdentity(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test")
r := KustoClusterResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.userAssignedIdentity(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("identity.0.type").HasValue("UserAssigned"),
check.That(data.ResourceName).Key("identity.0.identity_ids.#").HasValue("1"),
check.That(data.ResourceName).Key("identity.0.principal_id").HasValue(""),
),
},
})
}

func TestAccKustoCluster_multipleAssignedIdentity(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test")
r := KustoClusterResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.multipleAssignedIdentity(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned, UserAssigned"),
check.That(data.ResourceName).Key("identity.0.identity_ids.#").HasValue("1"),
resource.TestMatchResourceAttr(data.ResourceName, "identity.0.principal_id", validate.UUIDRegExp),
),
},
})
}

func TestAccKustoCluster_vnet(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test")
r := KustoClusterResource{}
Expand Down Expand Up @@ -288,6 +337,31 @@ resource "azurerm_kusto_cluster" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func (KustoClusterResource) doubleEncryption(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
double_encryption_enabled = true
sku {
name = "Dev(No SLA)_Standard_D11_v2"
capacity = 1
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func (KustoClusterResource) withTags(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down Expand Up @@ -450,6 +524,76 @@ resource "azurerm_kusto_cluster" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomString)
}

func (KustoClusterResource) userAssignedIdentity(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_user_assigned_identity" "test" {
name = "acctest%s"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
}
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 = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.test.id]
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString)
}

func (KustoClusterResource) multipleAssignedIdentity(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_user_assigned_identity" "test" {
name = "acctest%s"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
}
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, UserAssigned"
identity_ids = [azurerm_user_assigned_identity.test.id]
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString)
}

func (KustoClusterResource) languageExtensions(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
26 changes: 17 additions & 9 deletions website/docs/r/kusto_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ The following arguments are supported:

* `sku` - (Required) A `sku` block as defined below.

* `identity` - (Optional) A identity block.
* `double_encryption_enabled` - (Optional) Is the cluster's double encryption enabled? Defaults to `false`. Changing this forces a new resource to be created.

* `identity` - (Optional) An identity block.

* `enable_disk_encryption` - (Optional) Specifies if the cluster's disks are encrypted.

Expand All @@ -72,7 +74,7 @@ The following arguments are supported:

A `sku` block supports the following:

* `name` - (Required) The name of the SKU. Valid values are: `Dev(No SLA)_Standard_D11_v2`, `Dev(No SLA)_Standard_E2a_v4`, `Standard_D11_v2`, `Standard_D12_v2`, `Standard_D13_v2`, `Standard_D14_v2`, `Standard_DS13_v2+1TB_PS`, `Standard_DS13_v2+2TB_PS`, `Standard_DS14_v2+3TB_PS`, `Standard_DS14_v2+4TB_PS`, `Standard_E16as_v4+3TB_PS`, `Standard_E16as_v4+4TB_PS`, `Standard_E16a_v4`, `Standard_E2a_v4`, `Standard_E4a_v4`, `Standard_E8as_v4+1TB_PS`, `Standard_E8as_v4+2TB_PS`, `Standard_E8a_v4`, `Standard_L16s`, `Standard_L4s` and `Standard_L8s`
* `name` - (Required) The name of the SKU. Valid values are: `Dev(No SLA)_Standard_D11_v2`, `Dev(No SLA)_Standard_E2a_v4`, `Standard_D11_v2`, `Standard_D12_v2`, `Standard_D13_v2`, `Standard_D14_v2`, `Standard_DS13_v2+1TB_PS`, `Standard_DS13_v2+2TB_PS`, `Standard_DS14_v2+3TB_PS`, `Standard_DS14_v2+4TB_PS`, `Standard_E16as_v4+3TB_PS`, `Standard_E16as_v4+4TB_PS`, `Standard_E16a_v4`, `Standard_E2a_v4`, `Standard_E4a_v4`, `Standard_E64i_v3`, `Standard_E8as_v4+1TB_PS`, `Standard_E8as_v4+2TB_PS`, `Standard_E8a_v4`, `Standard_L16s`, `Standard_L4s` and `Standard_L8s`.

* `capacity` - (Optional) Specifies the node count for the cluster. Boundaries depend on the sku name.

Expand All @@ -93,15 +95,11 @@ A `virtual_network_configuration` block supports the following:

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.
* `type` - (Required) Specifies the type of Managed Service Identity that is configured on this Kusto Cluster. Possible values are: `SystemAssigned`, `UserAssigned` and `SystemAssigned, UserAssigned`.

* `identity_ids` - (Computed) The list of user identities associated with the Kusto cluster.
* `identity_ids` - (Optional) A list of IDs for User Assigned Managed Identity resources to be assigned.

~> **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.
~> **NOTE:** This is required when `type` is set to `UserAssigned` or `SystemAssigned, UserAssigned`.

---

Expand All @@ -121,6 +119,16 @@ The following attributes are exported:

* `data_ingestion_uri` - The Kusto Cluster URI to be used for data ingestion.

* `identity` - An `identity` block as defined below.

---

An `identity` block exports the following:

* `principal_id` - The Principal ID associated with this System Assigned Managed Service Identity.

* `tenant_id` - The Tenant ID associated with this System Assigned Managed Service Identity.

## Timeouts


Expand Down

0 comments on commit bbe21d8

Please sign in to comment.