Skip to content

Commit

Permalink
azurerm_stack_hci_cluster - support for identity, export `cloud_i…
Browse files Browse the repository at this point in the history
…d`, `service_endpoint`, `resource_provider_object_id` (hashicorp#25031)

* wip

* support for identity, export cloud_id, service_endpoint, resource_provider_object_id

* fix

* fix golangci-lint error

* link swagger issue

* fix doc
  • Loading branch information
teowa authored and reastyn committed Mar 14, 2024
1 parent 623aab4 commit e07e1f8
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 11 deletions.
58 changes: 51 additions & 7 deletions internal/services/azurestackhci/stack_hci_cluster_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2023-08-01/clusters"
Expand All @@ -35,13 +36,17 @@ func (r StackHCIClusterDataSource) ModelObject() interface{} {
}

type StackHCIClusterDataSourceModel struct {
Name string `tfschema:"name"`
ResourceGroupName string `tfschema:"resource_group_name"`
Location string `tfschema:"location"`
ClientId string `tfschema:"client_id"`
TenantId string `tfschema:"tenant_id"`
AutomanageConfigurationId string `tfschema:"automanage_configuration_id"`
Tags map[string]interface{} `tfschema:"tags"`
Name string `tfschema:"name"`
ResourceGroupName string `tfschema:"resource_group_name"`
Location string `tfschema:"location"`
ClientId string `tfschema:"client_id"`
TenantId string `tfschema:"tenant_id"`
AutomanageConfigurationId string `tfschema:"automanage_configuration_id"`
CloudId string `tfschema:"cloud_id"`
ServiceEndpoint string `tfschema:"service_endpoint"`
ResourceProviderObjectId string `tfschema:"resource_provider_object_id"`
Identity []identity.ModelSystemAssigned `tfschema:"identity"`
Tags map[string]interface{} `tfschema:"tags"`
}

func (r StackHCIClusterDataSource) Arguments() map[string]*schema.Schema {
Expand Down Expand Up @@ -75,6 +80,23 @@ func (r StackHCIClusterDataSource) Attributes() map[string]*schema.Schema {
Computed: true,
},

"cloud_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"service_endpoint": {
Type: pluginsdk.TypeString,
Computed: true,
},

"resource_provider_object_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"identity": commonschema.SystemAssignedIdentityComputed(),

"tags": commonschema.TagsDataSource(),
}
}
Expand Down Expand Up @@ -108,6 +130,10 @@ func (r StackHCIClusterDataSource) Read() sdk.ResourceFunc {

if props := model.Properties; props != nil {
cluster.ClientId = pointer.From(props.AadClientId)
cluster.CloudId = pointer.From(props.CloudId)
cluster.Identity = flattenSystemAssignedToModel(model.Identity)
cluster.ResourceProviderObjectId = pointer.From(props.ResourceProviderObjectId)
cluster.ServiceEndpoint = pointer.From(props.ServiceEndpoint)
cluster.TenantId = pointer.From(props.AadTenantId)

assignmentResp, err := hciAssignmentClient.Get(ctx, id.ResourceGroupName, id.ClusterName, "default")
Expand All @@ -132,3 +158,21 @@ func (r StackHCIClusterDataSource) Read() sdk.ResourceFunc {
},
}
}

func flattenSystemAssignedToModel(input *identity.SystemAndUserAssignedMap) []identity.ModelSystemAssigned {
if input == nil {
return []identity.ModelSystemAssigned{}
}

if input.Type == identity.TypeNone {
return []identity.ModelSystemAssigned{}
}

return []identity.ModelSystemAssigned{
{
Type: input.Type,
PrincipalId: input.PrincipalId,
TenantId: input.TenantId,
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,25 @@ func TestAccStackHCIClusterDataSource_basic(t *testing.T) {
check.That(data.ResourceName).Key("location").IsNotEmpty(),
check.That(data.ResourceName).Key("client_id").IsNotEmpty(),
check.That(data.ResourceName).Key("tenant_id").IsNotEmpty(),
check.That(data.ResourceName).Key("cloud_id").IsNotEmpty(),
check.That(data.ResourceName).Key("service_endpoint").IsNotEmpty(),
check.That(data.ResourceName).Key("resource_provider_object_id").IsNotEmpty(),
),
},
})
}

func TestAccStackHCIClusterDataSource_identity(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_stack_hci_cluster", "test")
r := StackHCIClusterDataSource{}

data.DataSourceTest(t, []acceptance.TestStep{
{
Config: r.identity(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"),
check.That(data.ResourceName).Key("identity.0.principal_id").IsNotEmpty(),
check.That(data.ResourceName).Key("identity.0.tenant_id").IsNotEmpty(),
),
},
})
Expand All @@ -39,3 +58,14 @@ data "azurerm_stack_hci_cluster" "test" {
}
`, StackHCIClusterResource{}.basic(data))
}

func (d StackHCIClusterDataSource) identity(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
data "azurerm_stack_hci_cluster" "test" {
name = azurerm_stack_hci_cluster.test.name
resource_group_name = azurerm_stack_hci_cluster.test.resource_group_name
}
`, StackHCIClusterResource{}.systemAssignedIdentity(data))
}
62 changes: 61 additions & 1 deletion internal/services/azurestackhci/stack_hci_cluster_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2023-08-01/clusters"
Expand Down Expand Up @@ -77,6 +78,23 @@ func resourceArmStackHCICluster() *pluginsdk.Resource {
ValidateFunc: autoVal.AutomanageConfigurationID,
},

"cloud_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"service_endpoint": {
Type: pluginsdk.TypeString,
Computed: true,
},

"resource_provider_object_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"identity": commonschema.SystemAssignedIdentityOptional(),

"tags": commonschema.Tags(),
},
}
Expand Down Expand Up @@ -108,6 +126,10 @@ func resourceArmStackHCIClusterCreate(d *pluginsdk.ResourceData, meta interface{
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
}

if v, ok := d.GetOk("identity"); ok {
cluster.Identity = expandSystemAssigned(v.([]interface{}))
}

if v, ok := d.GetOk("tenant_id"); ok {
cluster.Properties.AadTenantId = utils.String(v.(string))
} else {
Expand Down Expand Up @@ -185,10 +207,14 @@ func resourceArmStackHCIClusterRead(d *pluginsdk.ResourceData, meta interface{})

if model := resp.Model; model != nil {
d.Set("location", location.Normalize(model.Location))
d.Set("identity", flattenSystemAssigned(model.Identity))

if props := model.Properties; props != nil {
d.Set("client_id", props.AadClientId)
d.Set("tenant_id", props.AadTenantId)
d.Set("cloud_id", props.CloudId)
d.Set("service_endpoint", props.ServiceEndpoint)
d.Set("resource_provider_object_id", props.ResourceProviderObjectId)

assignmentResp, err := hciAssignmentClient.Get(ctx, id.ResourceGroupName, id.ClusterName, "default")
if err != nil && !utils.ResponseWasNotFound(assignmentResp.Response) {
Expand Down Expand Up @@ -231,6 +257,10 @@ func resourceArmStackHCIClusterUpdate(d *pluginsdk.ResourceData, meta interface{
cluster.Tags = tags.Expand(d.Get("tags").(map[string]interface{}))
}

if d.HasChange("identity") {
cluster.Identity = expandSystemAssigned(d.Get("identity").([]interface{}))
}

if _, err := client.Update(ctx, *id, cluster); err != nil {
return fmt.Errorf("updating %s: %+v", *id, err)
}
Expand Down Expand Up @@ -274,7 +304,6 @@ func resourceArmStackHCIClusterUpdate(d *pluginsdk.ResourceData, meta interface{
}
}
}

}

return resourceArmStackHCIClusterRead(d, meta)
Expand Down Expand Up @@ -308,3 +337,34 @@ func resourceArmStackHCIClusterDelete(d *pluginsdk.ResourceData, meta interface{

return nil
}

// API does not accept userAssignedIdentity as in swagger https://github.com/Azure/azure-rest-api-specs/issues/28260
func expandSystemAssigned(input []interface{}) *identity.SystemAndUserAssignedMap {
if len(input) == 0 || input[0] == nil {
return &identity.SystemAndUserAssignedMap{
Type: identity.TypeNone,
}
}

return &identity.SystemAndUserAssignedMap{
Type: identity.TypeSystemAssigned,
}
}

func flattenSystemAssigned(input *identity.SystemAndUserAssignedMap) []interface{} {
if input == nil {
return []interface{}{}
}

if input.Type == identity.TypeNone {
return []interface{}{}
}

return []interface{}{
map[string]interface{}{
"type": input.Type,
"principal_id": input.PrincipalId,
"tenant_id": input.TenantId,
},
}
}
71 changes: 71 additions & 0 deletions internal/services/azurestackhci/stack_hci_cluster_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func TestAccStackHCICluster_basic(t *testing.T) {
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("cloud_id").IsNotEmpty(),
check.That(data.ResourceName).Key("service_endpoint").IsNotEmpty(),
check.That(data.ResourceName).Key("resource_provider_object_id").IsNotEmpty(),
),
},
data.ImportStep(),
Expand All @@ -49,6 +52,53 @@ func TestAccStackHCICluster_requiresImport(t *testing.T) {
})
}

func TestAccStackHCICluster_systemAssignedIdentity(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_stack_hci_cluster", "test")
r := StackHCIClusterResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.systemAssignedIdentity(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"),
check.That(data.ResourceName).Key("identity.0.principal_id").IsNotEmpty(),
check.That(data.ResourceName).Key("identity.0.tenant_id").IsNotEmpty(),
),
},
data.ImportStep(),
})
}

func TestAccStackHCICluster_systemAssignedIdentityUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_stack_hci_cluster", "test")
r := StackHCIClusterResource{}

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

func TestAccStackHCICluster_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_stack_hci_cluster", "test")
r := StackHCIClusterResource{}
Expand Down Expand Up @@ -158,6 +208,24 @@ resource "azurerm_stack_hci_cluster" "import" {
`, config)
}

func (r StackHCIClusterResource) systemAssignedIdentity(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
%s
resource "azurerm_stack_hci_cluster" "test" {
name = "acctest-StackHCICluster-%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
client_id = azuread_application.test.application_id
tenant_id = data.azurerm_client_config.current.tenant_id
identity {
type = "SystemAssigned"
}
}
`, template, data.RandomInteger)
}

func (r StackHCIClusterResource) complete(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
Expand All @@ -169,6 +237,9 @@ resource "azurerm_stack_hci_cluster" "test" {
location = azurerm_resource_group.test.location
client_id = azuread_application.test.application_id
tenant_id = data.azurerm_client_config.current.tenant_id
identity {
type = "SystemAssigned"
}
tags = {
ENV = "Test"
Expand Down
22 changes: 20 additions & 2 deletions website/docs/d/stack_hci_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,33 @@ In addition to the Arguments listed above - the following Attributes are exporte

* `id` - The ID of the Azure Stack HCI Cluster.

* `location` - The Azure Region where the Azure Stack HCI Cluster exists.
* `automanage_configuration_id` - The ID of the Automanage Configuration assigned to the Azure Stack HCI Cluster.

* `client_id` - The Client ID of the Azure Active Directory used by the Azure Stack HCI Cluster.

* `cloud_id` - An immutable UUID for the Azure Stack HCI Cluster.

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

* `location` - The Azure Region where the Azure Stack HCI Cluster exists.

* `resource_provider_object_id` - The object ID of the Resource Provider Service Principal.

* `service_endpoint` - The region specific Data Path Endpoint of the Azure Stack HCI Cluster.

* `tenant_id` - The Tenant ID of the Azure Active Directory used by the Azure Stack HCI Cluster.

* `tags` - A mapping of tags assigned to the Azure Stack HCI Cluster.

* `automanage_configuration_id` - The ID of the Automanage Configuration assigned to the Azure Stack HCI Cluster.
---

An `identity` block exports the following:

* `type` - (Required) The type of Managed Service Identity configured on the Azure Stack HCI Cluster.

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

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

## Timeouts

Expand Down
Loading

0 comments on commit e07e1f8

Please sign in to comment.