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

azurerm_kusto_cluster - supports double_encryption_enabled, systemAssignedIdentity and systemAssignedUserAssignedIdentity #10264

Merged
merged 2 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
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{
katbyte marked this conversation as resolved.
Show resolved Hide resolved
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/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto/parse"
"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 @@ -262,11 +271,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 @@ -291,8 +301,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 @@ -405,6 +418,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