From 2a71f16e3eda7ec58174136e694424bbb4599c63 Mon Sep 17 00:00:00 2001 From: luoping-12345 Date: Mon, 9 Dec 2024 14:17:00 +0800 Subject: [PATCH] feat(identitycenter): add datasource access control attribute configurations --- ..._access_control_attribute_configuration.md | 53 ++++ huaweicloud/provider.go | 19 +- ...ss_control_attribute_configuration_test.go | 122 +++++++++ ..._access_control_attribute_configuration.go | 251 ++++++++++++++++++ 4 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 docs/resources/identitycenter_access_control_attribute_configuration.md create mode 100644 huaweicloud/services/acceptance/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration_test.go create mode 100644 huaweicloud/services/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration.go diff --git a/docs/resources/identitycenter_access_control_attribute_configuration.md b/docs/resources/identitycenter_access_control_attribute_configuration.md new file mode 100644 index 00000000000..3b95831336e --- /dev/null +++ b/docs/resources/identitycenter_access_control_attribute_configuration.md @@ -0,0 +1,53 @@ +--- +subcategory: "IAM Identity Center" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_identitycenter_access_control_attribute_configuration" +description: |- + Manages an Identity Center access control attribute configuration resource within HuaweiCloud. +--- + +# huaweicloud_identitycenter_access_control_attribute_configuration + +Manages an Identity Center access control attribute configuration resource within HuaweiCloud. + +## Example Usage + +```hcl +variable "instance_id" {} + +resource "huaweicloud_identitycenter_access_control_attribute_configuration" "test" { + instance_id = var.instance_id + + access_control_attributes { + key = "test" + value = ["$${user:email}"] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource. + If omitted, the provider-level region will be used. + Changing this creates a new resource. + +* `instance_id` - (Required, String, ForceNew) Specifies the ID of the IAM Identity Center instance. + Changing this creates a new resource. + +* `access_control_attributes` - (Optional, List) Specifies the properties of ABAC configuration in IAM Identity Center instance. + The [access_control_attributes](#access_control_attributes) structure is documented below. + + +The `access_control_attributes` block supports: + +* `key` - (Required, String) Specifies the name of the attribute associated with the identity in your identity source. + +* `value` - (Required, List) Specifies the value used to map the specified attribute to the identity source. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index e0d277a727c..bc937f453fc 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1764,15 +1764,16 @@ func Provider() *schema.Provider { "huaweicloud_identity_user_token": iam.ResourceIdentityUserToken(), "huaweicloud_identity_policy": iam.ResourceIdentityPolicy(), - "huaweicloud_identitycenter_user": identitycenter.ResourceIdentityCenterUser(), - "huaweicloud_identitycenter_group": identitycenter.ResourceIdentityCenterGroup(), - "huaweicloud_identitycenter_group_membership": identitycenter.ResourceGroupMembership(), - "huaweicloud_identitycenter_permission_set": identitycenter.ResourcePermissionSet(), - "huaweicloud_identitycenter_system_policy_attachment": identitycenter.ResourceSystemPolicyAttachment(), - "huaweicloud_identitycenter_system_identity_policy_attachment": identitycenter.ResourceSystemIdentityPolicyAttachment(), - "huaweicloud_identitycenter_account_assignment": identitycenter.ResourceIdentityCenterAccountAssignment(), - "huaweicloud_identitycenter_custom_policy_attachment": identitycenter.ResourceCustomPolicyAttachment(), - "huaweicloud_identitycenter_custom_role_attachment": identitycenter.ResourceCustomRoleAttachment(), + "huaweicloud_identitycenter_user": identitycenter.ResourceIdentityCenterUser(), + "huaweicloud_identitycenter_group": identitycenter.ResourceIdentityCenterGroup(), + "huaweicloud_identitycenter_group_membership": identitycenter.ResourceGroupMembership(), + "huaweicloud_identitycenter_permission_set": identitycenter.ResourcePermissionSet(), + "huaweicloud_identitycenter_system_policy_attachment": identitycenter.ResourceSystemPolicyAttachment(), + "huaweicloud_identitycenter_system_identity_policy_attachment": identitycenter.ResourceSystemIdentityPolicyAttachment(), + "huaweicloud_identitycenter_account_assignment": identitycenter.ResourceIdentityCenterAccountAssignment(), + "huaweicloud_identitycenter_custom_policy_attachment": identitycenter.ResourceCustomPolicyAttachment(), + "huaweicloud_identitycenter_custom_role_attachment": identitycenter.ResourceCustomRoleAttachment(), + "huaweicloud_identitycenter_access_control_attribute_configuration": identitycenter.ResourceAccessControlAttributeConfiguration(), "huaweicloud_iec_eip": iec.ResourceEip(), "huaweicloud_iec_keypair": iec.ResourceKeypair(), diff --git a/huaweicloud/services/acceptance/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration_test.go b/huaweicloud/services/acceptance/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration_test.go new file mode 100644 index 00000000000..1900fecc751 --- /dev/null +++ b/huaweicloud/services/acceptance/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration_test.go @@ -0,0 +1,122 @@ +package identitycenter + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/identitycenter" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +func getAccessControlAttributeConfigurationResourceFunc(cfg *config.Config, state *terraform.ResourceState) (interface{}, error) { + region := acceptance.HW_REGION_NAME + client, err := cfg.NewServiceClient("identitycenter", region) + if err != nil { + return nil, fmt.Errorf("error creating Identity Center client: %s", err) + } + + resp, err := identitycenter.GetAccessControlAttributeConfiguration(client, state.Primary.ID) + if err != nil { + return nil, err + } + + status := utils.PathSearch("status", resp, "").(string) + if status != "ENABLED" { + return nil, golangsdk.ErrDefault404{} + } + + return resp, nil +} + +func TestAccAccessControlAttributeConfiguration_basic(t *testing.T) { + var obj interface{} + + name := acceptance.RandomAccResourceName() + rName := "huaweicloud_identitycenter_access_control_attribute_configuration.test" + + rc := acceptance.InitResourceCheck( + rName, + &obj, + getAccessControlAttributeConfigurationResourceFunc, + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckMultiAccount(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: rc.CheckResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testAccessControlAttributeConfiguration_basic(name), + Check: resource.ComposeTestCheckFunc( + rc.CheckResourceExists(), + resource.TestCheckResourceAttrPair(rName, "instance_id", "data.huaweicloud_identitycenter_instance.system", "id"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.#", "1"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.0.key", name+"_1"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.0.value.0", "${user:email}"), + ), + }, + { + Config: testAccessControlAttributeConfiguration_update(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(rName, "instance_id", "data.huaweicloud_identitycenter_instance.system", "id"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.#", "2"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.0.key", name+"_1"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.0.value.0", "${user:email}"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.1.key", name+"_2"), + resource.TestCheckResourceAttr(rName, "access_control_attributes.1.value.0", "${user:familyName}"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"instance_id"}, + }, + }, + }) +} + +func testAccessControlAttributeConfiguration_basic(name string) string { + return fmt.Sprintf(` +data "huaweicloud_identitycenter_instance" "system" {} + +resource "huaweicloud_identitycenter_access_control_attribute_configuration" "test" { + instance_id = data.huaweicloud_identitycenter_instance.system.id + + access_control_attributes { + key = "%[1]s_1" + value = ["$${user:email}"] + } +} +`, name) +} + +func testAccessControlAttributeConfiguration_update(name string) string { + return fmt.Sprintf(` +data "huaweicloud_identitycenter_instance" "system" {} + +resource "huaweicloud_identitycenter_access_control_attribute_configuration" "test" { + instance_id = data.huaweicloud_identitycenter_instance.system.id + + access_control_attributes { + key = "%[1]s_1" + value = ["$${user:email}"] + } + + access_control_attributes { + key = "%[1]s_2" + value = ["$${user:familyName}"] + } +} +`, name) +} diff --git a/huaweicloud/services/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration.go b/huaweicloud/services/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration.go new file mode 100644 index 00000000000..c7c8a320e29 --- /dev/null +++ b/huaweicloud/services/identitycenter/resource_huaweicloud_identitycenter_access_control_attribute_configuration.go @@ -0,0 +1,251 @@ +package identitycenter + +import ( + "context" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +// @API IdentityCenter POST /v1/instances/{instance_id}/access-control-attribute-configuration +// @API IdentityCenter GET /v1/instances/{instance_id}/access-control-attribute-configuration +// @API IdentityCenter PUT /v1/instances/{instance_id}/access-control-attribute-configuration +// @API IdentityCenter DELETE /v1/instances/{instance_id}/access-control-attribute-configuration +func ResourceAccessControlAttributeConfiguration() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAccessControlAttributeConfigurationCreate, + UpdateContext: resourceAccessControlAttributeConfigurationUpdate, + ReadContext: resourceAccessControlAttributeConfigurationRead, + DeleteContext: resourceAccessControlAttributeConfigurationDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Description: "schema: Internal", + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "instance_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the ID of the IAM Identity Center instance.`, + }, + "access_control_attributes": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the name of the attribute associated with the identity in your identity source.`, + }, + "value": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `Specifies the value used to map the specified attribute to the identity source.`, + }, + }, + }, + Description: `Specifies the properties of ABAC configuration in IAM Identity Center instance.`, + }, + }, + } +} + +func resourceAccessControlAttributeConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + instanceId := d.Get("instance_id").(string) + + // createIdentityCenterAttrConfig: create IdentityCenter access control attribute configuration + var ( + createAttrConfigHttpUrl = "v1/instances/{instance_id}/access-control-attribute-configuration" + createProduct = "identitycenter" + ) + client, err := cfg.NewServiceClient(createProduct, region) + if err != nil { + return diag.Errorf("error creating IdentityCenter client: %s", err) + } + + createAttrConfigPath := client.Endpoint + createAttrConfigHttpUrl + createAttrConfigPath = strings.ReplaceAll(createAttrConfigPath, "{instance_id}", instanceId) + + createAttrConfigPathOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + createAttrConfigPathOpt.JSONBody = buildAccessControlAttributeConfigParams(d) + _, err = client.Request("POST", createAttrConfigPath, &createAttrConfigPathOpt) + if err != nil { + return diag.Errorf("error creating IdentityCenter access control attribute configuration: %s", err) + } + + d.SetId(instanceId) + + return resourceAccessControlAttributeConfigurationRead(ctx, d, meta) +} + +func buildAccessControlAttributeConfigParams(d *schema.ResourceData) map[string]interface{} { + rawArray := d.Get("access_control_attributes").(*schema.Set).List() + accessControlAttributes := make([]map[string]interface{}, len(rawArray)) + for i, v := range rawArray { + raw := v.(map[string]interface{}) + accessControlAttributes[i] = map[string]interface{}{ + "key": raw["key"], + "value": map[string]interface{}{ + "source": raw["value"], + }, + } + } + + bodyParams := map[string]interface{}{ + "instance_access_control_attribute_configuration": map[string]interface{}{ + "access_control_attributes": accessControlAttributes, + }, + } + + return bodyParams +} + +func resourceAccessControlAttributeConfigurationRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + + // getIdentityCenterAttrConfig: Query Identity Center access control attribute configuration + getAttrConfigClient, err := cfg.NewServiceClient("identitycenter", region) + if err != nil { + return diag.Errorf("error creating IdentityCenter client: %s", err) + } + + resp, err := GetAccessControlAttributeConfiguration(getAttrConfigClient, d.Id()) + if err != nil { + return diag.Errorf("error querying IdentityCenter access control attribute configuration: %s", err) + } + + status := utils.PathSearch("status", resp, "").(string) + if status != "ENABLED" { + return common.CheckDeletedDiag(d, golangsdk.ErrDefault404{}, "") + } + + mErr := multierror.Append( + d.Set("region", region), + d.Set("access_control_attributes", flattenAccessControlAttributes( + utils.PathSearch("instance_access_control_attribute_configuration.access_control_attributes", resp, nil))), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func flattenAccessControlAttributes(resp interface{}) []map[string]interface{} { + if resp == nil { + return nil + } + curArray := resp.([]interface{}) + rst := make([]map[string]interface{}, len(curArray)) + for i, v := range curArray { + rst[i] = map[string]interface{}{ + "key": utils.PathSearch("key", v, nil), + "value": utils.PathSearch("value.source", v, nil), + } + } + return rst +} + +func resourceAccessControlAttributeConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + + // updateIdentityCenterAttrConfig: update IdentityCenter access control attribute configuration + var ( + updateAttrConfigHttpUrl = "v1/instances/{instance_id}/access-control-attribute-configuration" + updateProduct = "identitycenter" + ) + client, err := cfg.NewServiceClient(updateProduct, region) + if err != nil { + return diag.Errorf("error creating IdentityCenter client: %s", err) + } + + updateAttrConfigPath := client.Endpoint + updateAttrConfigHttpUrl + updateAttrConfigPath = strings.ReplaceAll(updateAttrConfigPath, "{instance_id}", d.Id()) + + updateAttrConfigPathOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + updateAttrConfigPathOpt.JSONBody = buildAccessControlAttributeConfigParams(d) + _, err = client.Request("PUT", updateAttrConfigPath, &updateAttrConfigPathOpt) + if err != nil { + return diag.Errorf("error updating IdentityCenter access control attribute configuration: %s", err) + } + + return resourceAccessControlAttributeConfigurationRead(ctx, d, meta) +} + +func resourceAccessControlAttributeConfigurationDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + + // deleteIdentityCenterAttrConfig: Delete IdentityCenter access control attribute configuration + var ( + deleteAttrConfigHttpUrl = "v1/instances/{instance_id}/access-control-attribute-configuration" + deleteAttrConfigProduct = "identitycenter" + ) + deleteAttrConfigClient, err := cfg.NewServiceClient(deleteAttrConfigProduct, region) + if err != nil { + return diag.Errorf("error creating IdentityCenter client: %s", err) + } + + deleteAttrConfigPath := deleteAttrConfigClient.Endpoint + deleteAttrConfigHttpUrl + deleteAttrConfigPath = strings.ReplaceAll(deleteAttrConfigPath, "{instance_id}", d.Id()) + + deleteAttrConfigOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + _, err = deleteAttrConfigClient.Request("DELETE", deleteAttrConfigPath, &deleteAttrConfigOpt) + if err != nil { + return diag.Errorf("error deleting IdentityCenter access control attribute configuration: %s", err) + } + + // Check if access control attribute configuration is disabled. + resp, err := GetAccessControlAttributeConfiguration(deleteAttrConfigClient, d.Id()) + if err != nil { + return diag.Errorf("error querying IdentityCenter access control attribute configuration: %s", err) + } + + status := utils.PathSearch("status", resp, "").(string) + if status != "CREATION_FAILED" { + return diag.Errorf("error deleting IdentityCenter access control attribute configuration: %s", err) + } + + return nil +} + +func GetAccessControlAttributeConfiguration(client *golangsdk.ServiceClient, id string) (interface{}, error) { + getAttrConfigHttpUrl := "v1/instances/{instance_id}/access-control-attribute-configuration" + getAttrConfigPath := client.Endpoint + getAttrConfigHttpUrl + getAttrConfigPath = strings.ReplaceAll(getAttrConfigPath, "{instance_id}", id) + + getAttrConfigOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + getAttrConfigResp, err := client.Request("GET", getAttrConfigPath, &getAttrConfigOpt) + if err != nil { + return nil, err + } + + return utils.FlattenResponse(getAttrConfigResp) +}