From ea5d7c6b3d8665112d0158d14dca7727b5edbdf2 Mon Sep 17 00:00:00 2001 From: Thiery Ouattara Date: Tue, 3 Dec 2024 23:19:29 +0000 Subject: [PATCH] Add EIM resources/data_sources and tests --- ...aged_policies_linked_to_user_group_test.go | 61 ++ outscale/data_source_outscale_access_key.go | 21 +- outscale/data_source_outscale_access_keys.go | 21 +- ...urce_outscale_entities_linked_to_policy.go | 163 +++++ ...outscale_entities_linked_to_policy_test.go | 64 ++ .../data_source_outscale_linked_policies.go | 90 +++ ...ta_source_outscale_linked_policies_test.go | 54 ++ ...data_source_outscale_load_balancer_test.go | 14 +- ...e_managed_policies_linked_to_user_group.go | 93 +++ outscale/data_source_outscale_policies.go | 143 +++++ outscale/data_source_outscale_policy.go | 124 ++++ outscale/data_source_outscale_policy_test.go | 37 ++ outscale/data_source_outscale_user.go | 111 ++++ outscale/data_source_outscale_user_group.go | 117 ++++ outscale/data_source_outscale_user_groups.go | 120 ++++ ...ta_source_outscale_user_groups_per_user.go | 102 ++++ ...urce_outscale_user_groups_per_user_test.go | 51 ++ .../data_source_outscale_user_groups_test.go | 37 ++ outscale/data_source_outscale_user_test.go | 38 ++ outscale/data_source_outscale_users.go | 92 +++ outscale/data_source_outscale_users_test.go | 34 ++ outscale/policy.json | 9 + outscale/provider.go | 167 ++--- outscale/resource_outscale_access_key.go | 66 +- ..._outscale_load_balancer_attributes_test.go | 13 +- outscale/resource_outscale_policy.go | 242 ++++++++ outscale/resource_outscale_policy_test.go | 33 + outscale/resource_outscale_policy_version.go | 189 ++++++ .../resource_outscale_policy_version_test.go | 39 ++ outscale/resource_outscale_user.go | 365 +++++++++++ outscale/resource_outscale_user_group.go | 576 ++++++++++++++++++ outscale/resource_outscale_user_group_test.go | 132 ++++ outscale/resource_outscale_user_test.go | 78 +++ .../step1.user_resource_attributes_ok.ref | 32 + .../step1.user_resource_attributes_ok.tf | 4 + .../step2.user_resource_attributes_ok.ref | 70 +++ .../step2.user_resource_attributes_ok.tf | 14 + ...1.user_group_datasources_attributes_ok.ref | 158 +++++ ...p1.user_group_datasources_attributes_ok.tf | 41 ++ utils/utils.go | 36 +- 40 files changed, 3721 insertions(+), 130 deletions(-) create mode 100644 outscale/data_source_managed_policies_linked_to_user_group_test.go create mode 100644 outscale/data_source_outscale_entities_linked_to_policy.go create mode 100644 outscale/data_source_outscale_entities_linked_to_policy_test.go create mode 100644 outscale/data_source_outscale_linked_policies.go create mode 100644 outscale/data_source_outscale_linked_policies_test.go create mode 100644 outscale/data_source_outscale_managed_policies_linked_to_user_group.go create mode 100644 outscale/data_source_outscale_policies.go create mode 100644 outscale/data_source_outscale_policy.go create mode 100644 outscale/data_source_outscale_policy_test.go create mode 100644 outscale/data_source_outscale_user.go create mode 100644 outscale/data_source_outscale_user_group.go create mode 100644 outscale/data_source_outscale_user_groups.go create mode 100644 outscale/data_source_outscale_user_groups_per_user.go create mode 100644 outscale/data_source_outscale_user_groups_per_user_test.go create mode 100644 outscale/data_source_outscale_user_groups_test.go create mode 100644 outscale/data_source_outscale_user_test.go create mode 100644 outscale/data_source_outscale_users.go create mode 100644 outscale/data_source_outscale_users_test.go create mode 100644 outscale/policy.json create mode 100644 outscale/resource_outscale_policy.go create mode 100644 outscale/resource_outscale_policy_test.go create mode 100644 outscale/resource_outscale_policy_version.go create mode 100644 outscale/resource_outscale_policy_version_test.go create mode 100644 outscale/resource_outscale_user.go create mode 100644 outscale/resource_outscale_user_group.go create mode 100644 outscale/resource_outscale_user_group_test.go create mode 100644 outscale/resource_outscale_user_test.go create mode 100644 tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref create mode 100644 tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf create mode 100644 tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref create mode 100644 tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf create mode 100644 tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref create mode 100644 tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf diff --git a/outscale/data_source_managed_policies_linked_to_user_group_test.go b/outscale/data_source_managed_policies_linked_to_user_group_test.go new file mode 100644 index 000000000..3093950d1 --- /dev/null +++ b/outscale/data_source_managed_policies_linked_to_user_group_test.go @@ -0,0 +1,61 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_policiesLinkedToGroup_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_managed_policies_linked_to_user_group.dataPoliciesLinkedToGroup" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataPoliciesLinkedGroupConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policies.#"), + ), + }, + }, + }) +} + +const testAccDataPoliciesLinkedGroupConfig = ` +resource "outscale_user_group" "groupPolicies01" { + user_group_name = "userGroupName" + path = "/GroupPolicies/" + policy { + policy_orn = outscale_policy.GpolicyLinked_01.orn + } + policy { + policy_orn = outscale_policy.GpolicyLinked_02.orn + } +} + +resource "outscale_policy" "GpolicyLinked_01" { + description = "Example Linked to group" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/Allow_test/" + policy_name = "policiesLinkedToGroup" +} +resource "outscale_policy" "GpolicyLinked_02" { + description = "Example Linked policy to group" + document = "{\"Statement\": [ {\"Effect\": \"Deny\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/OkhtTest/" + policy_name = "policyGroupStopAll" +} +data "outscale_managed_policies_linked_to_user_group" "dataPoliciesLinkedToGroup" { + filter { + name = "path_prefix" + values = [outscale_user_group.groupPolicies01.path] + } + filter { + name = "user_group_ids" + values = [outscale_user_group.groupPolicies01.user_group_id] + } + user_group_name = outscale_user_group.groupPolicies01.user_group_name +}` diff --git a/outscale/data_source_outscale_access_key.go b/outscale/data_source_outscale_access_key.go index 80d55c929..60ac85a27 100644 --- a/outscale/data_source_outscale_access_key.go +++ b/outscale/data_source_outscale_access_key.go @@ -19,6 +19,10 @@ func DataSourceOutscaleAccessKey() *schema.Resource { Read: DataSourceOutscaleAccessKeyRead, Schema: map[string]*schema.Schema{ "filter": dataSourceFiltersSchema(), + "user_name": { + Type: schema.TypeString, + Optional: true, + }, "access_key_id": { Type: schema.TypeString, Optional: true, @@ -56,10 +60,11 @@ func DataSourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) e state, stateOk := d.GetOk("state") if !filtersOk && !accessKeyOk && !stateOk { - return fmt.Errorf("One of filters, access_key_id or state must be assigned") + return fmt.Errorf("one of filters, access_key_id or state must be assigned") } filterReq := &oscgo.FiltersAccessKeys{} + if filtersOk { filterReq = buildOutscaleDataSourceAccessKeyFilters(filters.(*schema.Set)) } @@ -69,11 +74,15 @@ func DataSourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) e if stateOk { filterReq.SetStates([]string{state.(string)}) } - + req := oscgo.ReadAccessKeysRequest{} + req.SetFilters(*filterReq) + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } var resp oscgo.ReadAccessKeysResponse - var err error - err = resource.Retry(5*time.Minute, func() *resource.RetryError { - rp, httpResp, err := conn.AccessKeyApi.ReadAccessKeys(context.Background()).ReadAccessKeysRequest(oscgo.ReadAccessKeysRequest{Filters: filterReq}).Execute() + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.AccessKeyApi.ReadAccessKeys(context.Background()).ReadAccessKeysRequest(req).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } @@ -85,7 +94,7 @@ func DataSourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) e } if len(resp.GetAccessKeys()) == 0 { - return fmt.Errorf("Unable to find Access Key") + return fmt.Errorf("unable to find Access Key") } if len(resp.GetAccessKeys()) > 1 { diff --git a/outscale/data_source_outscale_access_keys.go b/outscale/data_source_outscale_access_keys.go index 0f5cbbd6b..1a0c31417 100644 --- a/outscale/data_source_outscale_access_keys.go +++ b/outscale/data_source_outscale_access_keys.go @@ -31,6 +31,10 @@ func DataSourceOutscaleAccessKeys() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"ACTIVE", "INACTIVE"}, false), }, }, + "user_name": { + Type: schema.TypeString, + Optional: true, + }, "access_keys": { Type: schema.TypeList, Computed: true, @@ -75,7 +79,7 @@ func DataSourceOutscaleAccessKeysRead(d *schema.ResourceData, meta interface{}) state, stateOk := d.GetOk("states") if !filtersOk && !accessKeyOk && !stateOk { - return fmt.Errorf("One of filters, access_key_ids or states must be assigned") + return fmt.Errorf("one of filters, access_key_ids or states must be assigned") } filterReq := &oscgo.FiltersAccessKeys{} @@ -88,13 +92,16 @@ func DataSourceOutscaleAccessKeysRead(d *schema.ResourceData, meta interface{}) if stateOk { filterReq.SetStates(utils.InterfaceSliceToStringSlice(state.([]interface{}))) } + req := oscgo.ReadAccessKeysRequest{ + Filters: filterReq, + } + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } var resp oscgo.ReadAccessKeysResponse - var err error - err = resource.Retry(5*time.Minute, func() *resource.RetryError { - rp, httpResp, err := conn.AccessKeyApi.ReadAccessKeys(context.Background()).ReadAccessKeysRequest(oscgo.ReadAccessKeysRequest{ - Filters: filterReq, - }).Execute() + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.AccessKeyApi.ReadAccessKeys(context.Background()).ReadAccessKeysRequest(req).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) } @@ -106,7 +113,7 @@ func DataSourceOutscaleAccessKeysRead(d *schema.ResourceData, meta interface{}) } if len(resp.GetAccessKeys()) == 0 { - return fmt.Errorf("Unable to find Access Keys") + return fmt.Errorf("unable to find Access Keys") } if err := d.Set("access_keys", flattenAccessKeys(resp.GetAccessKeys())); err != nil { diff --git a/outscale/data_source_outscale_entities_linked_to_policy.go b/outscale/data_source_outscale_entities_linked_to_policy.go new file mode 100644 index 000000000..14fae228a --- /dev/null +++ b/outscale/data_source_outscale_entities_linked_to_policy.go @@ -0,0 +1,163 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceEntitiesLinkedToPolicy() *schema.Resource { + return &schema.Resource{ + Read: DataSourceEntitiesLinkedToPoliciesRead, + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "entities_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"USER", "GROUP", "ACCOUNT"}, false), + }, + "policy_entities": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "users": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "groups": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "accounts": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func DataSourceEntitiesLinkedToPoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + orn := d.Get("policy_orn").(string) + req := oscgo.ReadEntitiesLinkedToPolicyRequest{PolicyOrn: &orn} + if entity := d.Get("entities_type").(string); entity != "" { + req.SetEntitiesType([]string{entity}) + } + + var resp oscgo.ReadEntitiesLinkedToPolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadEntitiesLinkedToPolicy(context.Background()).ReadEntitiesLinkedToPolicyRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + entities, ok := resp.GetPolicyEntitiesOk() + if !ok { + return fmt.Errorf("unable to find Entities linked to policy") + } + d.SetId(resource.UniqueId()) + + users := make([]map[string]interface{}, len(entities.GetUsers())) + groups := make([]map[string]interface{}, len(entities.GetGroups())) + accounts := make([]map[string]interface{}, len(entities.GetAccounts())) + if respUsers, ok := entities.GetUsersOk(); ok { + for i, v := range *respUsers { + user := make(map[string]interface{}) + user["id"] = v.GetId() + user["name"] = v.GetName() + user["orn"] = v.GetOrn() + users[i] = user + } + } + if respGroups, ok := entities.GetGroupsOk(); ok { + for i, v := range *respGroups { + group := make(map[string]interface{}) + group["name"] = v.GetName() + group["id"] = v.GetId() + group["orn"] = v.GetOrn() + groups[i] = group + } + } + if respAccounts, ok := entities.GetAccountsOk(); ok { + for i, v := range *respAccounts { + account := make(map[string]interface{}) + account["name"] = v.GetName() + account["id"] = v.GetId() + account["orn"] = v.GetOrn() + accounts[i] = account + } + } + + return d.Set("policy_entities", []map[string]interface{}{{ + "users": users, + "groups": groups, + "accounts": accounts, + }}) +} diff --git a/outscale/data_source_outscale_entities_linked_to_policy_test.go b/outscale/data_source_outscale_entities_linked_to_policy_test.go new file mode 100644 index 000000000..d71331a12 --- /dev/null +++ b/outscale/data_source_outscale_entities_linked_to_policy_test.go @@ -0,0 +1,64 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_entities_linked_to_policy_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_entities_linked_to_policy.entitiesLinked" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataEntitiesLinkedConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policy_entities.#"), + ), + }, + }, + }) +} + +const testAccDataEntitiesLinkedConfig = ` +resource "outscale_user" "user_01" { + user_name = "userLedGroup" + path = "/linkedUser/" + policy { + policy_orn = outscale_policy.policyEntities_01.orn + } +} +resource "outscale_user_group" "uGroupLinked" { + user_group_name = "GLinkedTestACC" + path = "/" + user { + user_name = outscale_user.user_01.user_name + path = "/linkedUser/" + } + policy { + policy_orn = outscale_policy.policyEntities_01.orn + } + depends_on = [outscale_user.user_01] +} +resource "outscale_user_group" "GroupLinkedPolicy" { + user_group_name = "GroupPolicyTestAcc" + path = "/TestPath/" + policy { + policy_orn = outscale_policy.policyEntities_01.orn + } +} +resource "outscale_policy" "policyEntities_01" { + description = "Example Entities Linked to policy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/Okht_test/" + policy_name = "policyEntitiesLinked" +} + +data "outscale_entities_linked_to_policy" "entitiesLinked" { + policy_orn = outscale_policy.policyEntities_01.orn + depends_on = [outscale_user_group.uGroupLinked, outscale_user_group.GroupLinkedPolicy, outscale_user.user_01] +}` diff --git a/outscale/data_source_outscale_linked_policies.go b/outscale/data_source_outscale_linked_policies.go new file mode 100644 index 000000000..3e24aa308 --- /dev/null +++ b/outscale/data_source_outscale_linked_policies.go @@ -0,0 +1,90 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceLinkedPolicies() *schema.Resource { + return &schema.Resource{ + Read: DataSourceLinkedPoliciesRead, + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Required: true, + }, + "policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceLinkedPoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadLinkedPoliciesRequest(d.Get("user_name").(string)) + var resp oscgo.ReadLinkedPoliciesResponse + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadLinkedPolicies(context.Background()).ReadLinkedPoliciesRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + policiesList := resp.GetPolicies() + if len(policiesList) == 0 { + return fmt.Errorf("unable to find Policies linked to user: %v", d.Get("user_name").(string)) + } + d.SetId(resource.UniqueId()) + + policies := make([]map[string]interface{}, len(policiesList)) + + for i, v := range policiesList { + policy := make(map[string]interface{}) + policy["policy_name"] = v.GetPolicyName() + policy["policy_id"] = v.GetPolicyId() + policy["orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + policies[i] = policy + } + return d.Set("policies", policies) +} diff --git a/outscale/data_source_outscale_linked_policies_test.go b/outscale/data_source_outscale_linked_policies_test.go new file mode 100644 index 000000000..3f176b0d4 --- /dev/null +++ b/outscale/data_source_outscale_linked_policies_test.go @@ -0,0 +1,54 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_Policies_linked_to_user_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_linked_policies.policiesLinkedToUser" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataPoliciesLinkedConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policies.#"), + ), + }, + }, + }) +} + +const testAccDataPoliciesLinkedConfig = ` +resource "outscale_user" "userPolicies01" { + user_name = "userLedGroup" + path = "/policiesUser/" + policy { + policy_orn = outscale_policy.policyLinked_01.orn + } + policy { + policy_orn = outscale_policy.policyLinked_02.orn + } +} + +resource "outscale_policy" "policyLinked_01" { + description = "Example Linked to user" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/Allow_est/" + policy_name = "policiesLinked" +} +resource "outscale_policy" "policyLinked_02" { + description = "Example Linked policy to user" + document = "{\"Statement\": [ {\"Effect\": \"Deny\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/OkhtTest/" + policy_name = "policyStopAll" +} +data "outscale_linked_policies" "policiesLinkedToUser" { + user_name = outscale_user.userPolicies01.user_name + depends_on = [outscale_user.userPolicies01] +}` diff --git a/outscale/data_source_outscale_load_balancer_test.go b/outscale/data_source_outscale_load_balancer_test.go index 47ccd16f6..7167f9efa 100644 --- a/outscale/data_source_outscale_load_balancer_test.go +++ b/outscale/data_source_outscale_load_balancer_test.go @@ -13,14 +13,14 @@ import ( func TestAccOthers_LBU_basic(t *testing.T) { t.Parallel() const ( - MIN_LB_NAME_SUFFIX int = 20 - MAX_LB_NAME_SUFFIX int = 35 + MIN_LB_NAME_SUFFIX int = 1000 + MAX_LB_NAME_SUFFIX int = 5000 ) var conf oscgo.LoadBalancer zone := fmt.Sprintf("%sa", utils.GetRegion()) - number := utils.RandIntRange(MIN_LB_NAME_SUFFIX, MAX_LB_NAME_SUFFIX) + suffix := utils.RandIntRange(MIN_LB_NAME_SUFFIX, MAX_LB_NAME_SUFFIX) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -30,7 +30,7 @@ func TestAccOthers_LBU_basic(t *testing.T) { CheckDestroy: testAccCheckOutscaleLBUDestroy, Steps: []resource.TestStep{ { - Config: testAccDSOutscaleLBUConfig(zone, number), + Config: testAccDSOutscaleLBUConfig(zone, suffix), Check: resource.ComposeTestCheckFunc( testAccCheckOutscaleLBUExists("outscale_load_balancer.dataLb", &conf), resource.TestCheckResourceAttr( @@ -42,11 +42,11 @@ func TestAccOthers_LBU_basic(t *testing.T) { }) } -func testAccDSOutscaleLBUConfig(zone string, number int) string { +func testAccDSOutscaleLBUConfig(zone string, suffix int) string { return fmt.Sprintf(` resource "outscale_load_balancer" "dataLb" { subregion_names = ["%s"] - load_balancer_name = "data-terraform-elb-%v" + load_balancer_name = "data-terraform-elb-%d" listeners { backend_port = 8000 @@ -64,5 +64,5 @@ func testAccDSOutscaleLBUConfig(zone string, number int) string { data "outscale_load_balancer" "dataTest" { load_balancer_name = outscale_load_balancer.dataLb.id } -`, zone, number) +`, zone, suffix) } diff --git a/outscale/data_source_outscale_managed_policies_linked_to_user_group.go b/outscale/data_source_outscale_managed_policies_linked_to_user_group.go new file mode 100644 index 000000000..f2cf660a8 --- /dev/null +++ b/outscale/data_source_outscale_managed_policies_linked_to_user_group.go @@ -0,0 +1,93 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceManagedPoliciesLinkedToUserGroup() *schema.Resource { + return &schema.Resource{ + Read: DataSourceManagedPoliciesLinkedToUserGroupRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "user_group_name": { + Type: schema.TypeString, + Required: true, + }, + "policies": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceManagedPoliciesLinkedToUserGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.ReadManagedPoliciesLinkedToUserGroupRequest{} + req.SetUserGroupName(d.Get("user_group_name").(string)) + + if filters, filtersOk := d.GetOk("filter"); filtersOk { + filterReq := buildUserGroupsFilters(filters.(*schema.Set)) + req.SetFilters(*filterReq) + } + + var resp oscgo.ReadManagedPoliciesLinkedToUserGroupResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadManagedPoliciesLinkedToUserGroup(context.Background()).ReadManagedPoliciesLinkedToUserGroupRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + if _, ok := resp.GetPoliciesOk(); !ok { + return fmt.Errorf("unable to find policies linked to user group") + } + policiesResp := resp.GetPolicies() + d.SetId(resource.UniqueId()) + policies := make([]map[string]interface{}, len(policiesResp)) + for i, v := range policiesResp { + policy := make(map[string]interface{}) + policy["policy_name"] = v.GetPolicyName() + policy["policy_id"] = v.GetPolicyId() + policy["orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + policies[i] = policy + } + return d.Set("policies", policies) +} diff --git a/outscale/data_source_outscale_policies.go b/outscale/data_source_outscale_policies.go new file mode 100644 index 000000000..f0da0f972 --- /dev/null +++ b/outscale/data_source_outscale_policies.go @@ -0,0 +1,143 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" + "github.com/spf13/cast" +) + +func DataSourcePolicies() *schema.Resource { + return &schema.Resource{ + Read: DataSourcePoliciesRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + + "policy_name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourcePoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + filters, filtersOk := d.GetOk("filter") + if !filtersOk { + return fmt.Errorf("One of filters, only_linked, path_refix,... must be assigned") + } + filterReq := buildPoliciesFilters(filters.(*schema.Set)) + req := oscgo.NewReadPoliciesRequest() + req.SetFilters(*filterReq) + var resp oscgo.ReadPoliciesResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicies(context.Background()).ReadPoliciesRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + policyResp := resp.GetPolicies() + if len(policyResp) == 0 { + return fmt.Errorf("Unable to find Policies with fileters: %v", filters.(*schema.Set)) + } + d.SetId(resource.UniqueId()) + + policies := make([]map[string]interface{}, len(policyResp)) + + for i, v := range policyResp { + policy := make(map[string]interface{}) + policy["policy_name"] = v.GetPolicyName() + policy["policy_id"] = v.GetPolicyId() + policy["path"] = v.GetPath() + policy["orn"] = v.GetOrn() + policy["resources_count"] = v.GetResourcesCount() + policy["is_linkable"] = v.GetIsLinkable() + policy["policy_default_version_id"] = v.GetPolicyDefaultVersionId() + policy["description"] = v.GetDescription() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + policies[i] = policy + } + return d.Set("policies", policies) +} + +func buildPoliciesFilters(set *schema.Set) *oscgo.ReadPoliciesFilters { + var filters oscgo.ReadPoliciesFilters + for _, v := range set.List() { + m := v.(map[string]interface{}) + var filterValues []string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, e.(string)) + } + + switch name := m["name"].(string); name { + case "only_linked": + filters.SetOnlyLinked(cast.ToBool(filterValues[0])) + case "path_prefix": + filters.SetPathPrefix(filterValues[0]) + case "scope": + filters.SetScope(filterValues[0]) + default: + log.Printf("[Debug] Unknown Filter Name: %s.", name) + } + } + return &filters +} diff --git a/outscale/data_source_outscale_policy.go b/outscale/data_source_outscale_policy.go new file mode 100644 index 000000000..7863db868 --- /dev/null +++ b/outscale/data_source_outscale_policy.go @@ -0,0 +1,124 @@ +package outscale + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourcePolicy() *schema.Resource { + return &schema.Resource{ + Read: DataSourcePolicyRead, + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "document": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DataSourcePolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyRequest(d.Get("policy_orn").(string)) + + var resp oscgo.ReadPolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicy(context.Background()).ReadPolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyOk(); !ok { + d.SetId("") + return nil + } + policy := resp.GetPolicy() + d.SetId(resource.UniqueId()) + if err := d.Set("policy_name", policy.GetPolicyName()); err != nil { + return err + } + if err := d.Set("policy_id", policy.GetPolicyId()); err != nil { + return err + } + if err := d.Set("path", policy.GetPath()); err != nil { + return err + } + if err := d.Set("orn", policy.GetOrn()); err != nil { + return err + } + if err := d.Set("resources_count", policy.GetResourcesCount()); err != nil { + return err + } + if err := d.Set("is_linkable", policy.GetIsLinkable()); err != nil { + return err + } + if err := d.Set("policy_default_version_id", policy.GetPolicyDefaultVersionId()); err != nil { + return err + } + if err := d.Set("description", policy.GetDescription()); err != nil { + return err + } + if err := d.Set("creation_date", (policy.GetCreationDate())); err != nil { + return err + } + if err := d.Set("last_modification_date", (policy.GetLastModificationDate())); err != nil { + return err + } + return nil +} diff --git a/outscale/data_source_outscale_policy_test.go b/outscale/data_source_outscale_policy_test.go new file mode 100644 index 000000000..84b007dcb --- /dev/null +++ b/outscale/data_source_outscale_policy_test.go @@ -0,0 +1,37 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_data_policy_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_policy.data_test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyDataConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policy_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +const testAccPolicyDataConfig = ` + resource "outscale_policy" "data_policy" { + policy_name = "TestACC_resoucePolicy" + document = file("~/terraform-provider-outscale/outscale/policy.json") + path = "/" + } + data "outscale_policy" "data_test" { + policy_orn = outscale_policy.data_policy.orn + } +` diff --git a/outscale/data_source_outscale_user.go b/outscale/data_source_outscale_user.go new file mode 100644 index 000000000..d1bb13e0e --- /dev/null +++ b/outscale/data_source_outscale_user.go @@ -0,0 +1,111 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "time" + + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceUser() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DataSourceUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + filters, filtersOk := d.GetOk("filter") + req := oscgo.NewReadUsersRequest() + if filtersOk { + filterReq := buildUsersFilters(filters.(*schema.Set)) + req.SetFilters(*filterReq) + } + var resp oscgo.ReadUsersResponse + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + users := resp.GetUsers() + d.SetId(resource.UniqueId()) + if len(users) == 0 { + return fmt.Errorf("Unable to find users") + } + if len(users) > 1 { + return fmt.Errorf("Find To many users") + } + + if err := d.Set("user_name", users[0].GetUserName()); err != nil { + return err + } + if err := d.Set("user_id", users[0].GetUserId()); err != nil { + return err + } + if err := d.Set("path", users[0].GetPath()); err != nil { + return err + } + if err := d.Set("creation_date", users[0].GetCreationDate()); err != nil { + return err + } + if err := d.Set("last_modification_date", users[0].GetLastModificationDate()); err != nil { + return err + } + return nil +} + +func buildUsersFilters(set *schema.Set) *oscgo.FiltersUsers { + var filters oscgo.FiltersUsers + for _, v := range set.List() { + m := v.(map[string]interface{}) + var filterValues []string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, e.(string)) + } + + switch name := m["name"].(string); name { + case "user_ids": + filters.SetUserIds(filterValues) + default: + log.Printf("[Debug] Unknown Filter Name: %s.", name) + } + } + return &filters +} diff --git a/outscale/data_source_outscale_user_group.go b/outscale/data_source_outscale_user_group.go new file mode 100644 index 000000000..ee46c347d --- /dev/null +++ b/outscale/data_source_outscale_user_group.go @@ -0,0 +1,117 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceUserGroup() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserGroupRead, + Schema: map[string]*schema.Schema{ + "user_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + }, + "user_group_id": { + Type: schema.TypeString, + Required: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + "user": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceUserGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + filters, filtersOk := d.GetOk("filter") + if !filtersOk { + return fmt.Errorf("One of filters, user_group_id or path_refix must be assigned") + } + + filterReq := buildUserGroupsFilters(filters.(*schema.Set)) + req := oscgo.ReadUserGroupsRequest{} + req.SetFilters(*filterReq) + var resp oscgo.ReadUserGroupsResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroups(context.Background()).ReadUserGroupsRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetUserGroupsOk(); !ok { + return fmt.Errorf("Unable to find user groups") + } + d.SetId(resource.UniqueId()) + userGps := resp.GetUserGroups() + userGroups := make([]map[string]interface{}, len(userGps)) + + for i, v := range userGps { + userGroup := make(map[string]interface{}) + userGroup["user_group_name"] = v.GetName() + userGroup["user_group_id"] = v.GetUserGroupId() + userGroup["path"] = v.GetPath() + userGroup["orn"] = v.GetOrn() + userGroup["creation_date"] = v.GetCreationDate() + userGroup["last_modification_date"] = v.GetLastModificationDate() + userGroups[i] = userGroup + } + return d.Set("user_groups", userGroups) +} diff --git a/outscale/data_source_outscale_user_groups.go b/outscale/data_source_outscale_user_groups.go new file mode 100644 index 000000000..1095e0647 --- /dev/null +++ b/outscale/data_source_outscale_user_groups.go @@ -0,0 +1,120 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceUserGroups() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserGroupsRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "user_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "user_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceUserGroupsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + filters, filtersOk := d.GetOk("filter") + if !filtersOk { + return fmt.Errorf("One of filters, user_group_id or path_refix must be assigned") + } + + filterReq := buildUserGroupsFilters(filters.(*schema.Set)) + req := oscgo.ReadUserGroupsRequest{} + req.SetFilters(*filterReq) + var resp oscgo.ReadUserGroupsResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroups(context.Background()).ReadUserGroupsRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetUserGroupsOk(); !ok { + return fmt.Errorf("Unable to find user groups") + } + d.SetId(resource.UniqueId()) + userGps := resp.GetUserGroups() + userGroups := make([]map[string]interface{}, len(userGps)) + + for i, v := range userGps { + userGroup := make(map[string]interface{}) + userGroup["user_group_name"] = v.GetName() + userGroup["user_group_id"] = v.GetUserGroupId() + userGroup["path"] = v.GetPath() + userGroup["orn"] = v.GetOrn() + userGroup["creation_date"] = v.GetCreationDate() + userGroup["last_modification_date"] = v.GetLastModificationDate() + userGroups[i] = userGroup + } + return d.Set("user_groups", userGroups) +} + +func buildUserGroupsFilters(set *schema.Set) *oscgo.FiltersUserGroup { + var filters oscgo.FiltersUserGroup + for _, v := range set.List() { + m := v.(map[string]interface{}) + var filterValues []string + for _, e := range m["values"].([]interface{}) { + filterValues = append(filterValues, e.(string)) + } + + switch name := m["name"].(string); name { + case "path_prefix": + filters.SetPathPrefix(filterValues[0]) + case "user_group_ids": + filters.SetUserGroupIds(filterValues) + default: + log.Printf("[Debug] Unknown Filter Name: %s.", name) + } + } + return &filters +} diff --git a/outscale/data_source_outscale_user_groups_per_user.go b/outscale/data_source_outscale_user_groups_per_user.go new file mode 100644 index 000000000..7ef9ba067 --- /dev/null +++ b/outscale/data_source_outscale_user_groups_per_user.go @@ -0,0 +1,102 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func DataSourceUserGroupsPerUser() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUserGroupsPerUserRead, + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Required: true, + }, + "user_path": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "user_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "user_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceUserGroupsPerUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + req := oscgo.NewReadUserGroupsPerUserRequest(d.Get("user_name").(string)) + if userPath := d.Get("user_path").(string); userPath != "" { + req.SetUserPath(userPath) + } + var resp oscgo.ReadUserGroupsPerUserResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroupsPerUser(context.Background()).ReadUserGroupsPerUserRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetUserGroupsOk(); !ok { + return fmt.Errorf("unable to find user groups") + } + d.SetId(resource.UniqueId()) + userGps := resp.GetUserGroups() + userGroups := make([]map[string]interface{}, len(userGps)) + + for i, v := range userGps { + userGroup := make(map[string]interface{}) + userGroup["user_group_name"] = v.GetName() + userGroup["user_group_id"] = v.GetUserGroupId() + userGroup["path"] = v.GetPath() + userGroup["orn"] = v.GetOrn() + userGroup["creation_date"] = v.GetCreationDate() + userGroup["last_modification_date"] = v.GetLastModificationDate() + userGroups[i] = userGroup + } + return d.Set("user_groups", userGroups) +} diff --git a/outscale/data_source_outscale_user_groups_per_user_test.go b/outscale/data_source_outscale_user_groups_per_user_test.go new file mode 100644 index 000000000..67a3d1620 --- /dev/null +++ b/outscale/data_source_outscale_user_groups_per_user_test.go @@ -0,0 +1,51 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_groups_per_user_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_user_groups_per_user.groupList" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataUserGroupsPerUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_groups.#"), + ), + }, + }, + }) +} + +const testAccDataUserGroupsPerUserBasicConfig = ` +resource "outscale_user" "userForGroup01" { + user_name = "user1_per_group" + path = "/groupsPerUser/" +} + +resource "outscale_user_group" "uGroupFORuser" { + user_group_name = "Group1TestACC" + path = "/" + user { + user_name = outscale_user.userForGroup01.user_name + path = "/groupsPerUser/" + } +} +resource "outscale_user_group" "uGroup2FORuser" { + user_group_name = "Group02TestACC" + path = "/TestPath/" + user { + user_name = outscale_user.userForGroup01.user_name + } +} +data "outscale_user_groups_per_user" "groupList" { + user_name = outscale_user.userForGroup01.user_name + depends_on =[outscale_user_group.uGroup2FORuser] +}` diff --git a/outscale/data_source_outscale_user_groups_test.go b/outscale/data_source_outscale_user_groups_test.go new file mode 100644 index 000000000..a13b46fef --- /dev/null +++ b/outscale/data_source_outscale_user_groups_test.go @@ -0,0 +1,37 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_groups_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_user_groups.basicUGTest" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataUserGroupsBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_groups.#"), + ), + }, + }, + }) +} + +const testAccDataUserGroupsBasicConfig = ` + resource "outscale_user_group" "uGroupData" { + user_group_name = "TestACC_uGdata" + path = "/" + } + data "outscale_user_groups" "basicUGTest" { + filter { + name = "user_group_ids" + values = [outscale_user_group.uGroupData.user_group_id] + } + }` diff --git a/outscale/data_source_outscale_user_test.go b/outscale/data_source_outscale_user_test.go new file mode 100644 index 000000000..cac3a5ad9 --- /dev/null +++ b/outscale/data_source_outscale_user_test.go @@ -0,0 +1,38 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_data_user_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_user.basicTestUser" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_id"), + ), + }, + }, + }) +} + +const testAccDataUserBasicConfig = ` + resource "outscale_user" "basic_dataUser" { + user_name = "ACC_user_data1" + path = "/" + } + data "outscale_user" "basicTestUser" { + filter { + name = "user_ids" + values = [outscale_user.basic_dataUser.user_id] + } + } +` diff --git a/outscale/data_source_outscale_users.go b/outscale/data_source_outscale_users.go new file mode 100644 index 000000000..81edc1298 --- /dev/null +++ b/outscale/data_source_outscale_users.go @@ -0,0 +1,92 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceUsers() *schema.Resource { + return &schema.Resource{ + Read: DataSourceUsersRead, + Schema: map[string]*schema.Schema{ + "filter": dataSourceFiltersSchema(), + "users": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func DataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + filters, filtersOk := d.GetOk("filter") + req := oscgo.NewReadUsersRequest() + if filtersOk { + filterReq := buildUsersFilters(filters.(*schema.Set)) + req.SetFilters(*filterReq) + } + var resp oscgo.ReadUsersResponse + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + users := resp.GetUsers() + d.SetId(resource.UniqueId()) + if len(users) == 0 { + return fmt.Errorf("Unable to find users") + } + d.SetId(resource.UniqueId()) + usersToSet := make([]map[string]interface{}, len(users)) + for i, v := range users { + user := make(map[string]interface{}) + + user["user_id"] = v.GetUserId() + user["user_name"] = v.GetUserName() + user["path"] = v.GetPath() + user["creation_date"] = v.GetCreationDate() + user["last_modification_date"] = v.GetLastModificationDate() + usersToSet[i] = user + } + return d.Set("users", usersToSet) +} diff --git a/outscale/data_source_outscale_users_test.go b/outscale/data_source_outscale_users_test.go new file mode 100644 index 000000000..d9950b824 --- /dev/null +++ b/outscale/data_source_outscale_users_test.go @@ -0,0 +1,34 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_users_basic(t *testing.T) { + t.Parallel() + resourceName := "data.outscale_users.basicTestUsers" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleDataUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "users.#"), + ), + }, + }, + }) +} + +const testAccOutscaleDataUserBasicConfig = ` + resource "outscale_user" "basic_data_users" { + user_name = "ACC_test_data1" + path = "/" + } + data "outscale_users" "basicTestUsers" { + depends_on = [outscale_user.basic_data_users] + }` diff --git a/outscale/policy.json b/outscale/policy.json new file mode 100644 index 000000000..774b41178 --- /dev/null +++ b/outscale/policy.json @@ -0,0 +1,9 @@ +{ + "Statement": [ + { + "Effect": "Allow", + "Action": ["*"], + "Resource": ["*"] + } + ] +} \ No newline at end of file diff --git a/outscale/provider.go b/outscale/provider.go index bc474ccb8..3cdd8203d 100644 --- a/outscale/provider.go +++ b/outscale/provider.go @@ -130,83 +130,97 @@ func Provider() *schema.Provider { "outscale_api_access_rule": ResourceOutscaleApiAccessRule(), "outscale_api_access_policy": ResourceOutscaleApiAccessPolicy(), "outscale_main_route_table_link": resourceLinkMainRouteTable(), + "outscale_user": ResourceOutscaleUser(), + "outscale_user_group": ResourceUserGroup(), + "outscale_policy": ResourceOutscalePolicy(), + "outscale_policy_version": ResourcePolicyVersion(), }, DataSourcesMap: map[string]*schema.Resource{ - "outscale_vm": DataSourceOutscaleVM(), - "outscale_vms": DataSourceOutscaleVMS(), - "outscale_security_group": DataSourceOutscaleSecurityGroup(), - "outscale_security_groups": DataSourceOutscaleSecurityGroups(), - "outscale_image": DataSourceOutscaleImage(), - "outscale_images": DataSourceOutscaleImages(), - "outscale_tag": DataSourceOutscaleTag(), - "outscale_tags": DataSourceOutscaleTags(), - "outscale_public_ip": DataSourceOutscalePublicIP(), - "outscale_public_ips": DataSourceOutscalePublicIPS(), - "outscale_volume": DataSourceOutscaleVolume(), - "outscale_volumes": DataSourceOutscaleVolumes(), - "outscale_nat_service": DataSourceOutscaleNatService(), - "outscale_nat_services": DataSourceOutscaleNatServices(), - "outscale_keypair": DataSourceOutscaleKeyPair(), - "outscale_keypairs": DataSourceOutscaleKeyPairs(), - "outscale_vm_state": DataSourceOutscaleVMState(), - "outscale_vm_states": DataSourceOutscaleVMStates(), - "outscale_internet_service": DataSourceOutscaleInternetService(), - "outscale_internet_services": DataSourceOutscaleInternetServices(), - "outscale_subnet": DataSourceOutscaleSubnet(), - "outscale_subnets": DataSourceOutscaleSubnets(), - "outscale_net": DataSourceOutscaleVpc(), - "outscale_nets": DataSourceOutscaleVpcs(), - "outscale_net_attributes": DataSourceOutscaleVpcAttr(), - "outscale_route_table": DataSourceOutscaleRouteTable(), - "outscale_route_tables": DataSourceOutscaleRouteTables(), - "outscale_snapshot": DataSourceOutscaleSnapshot(), - "outscale_snapshots": DataSourceOutscaleSnapshots(), - "outscale_net_peering": DataSourceOutscaleLinPeeringConnection(), - "outscale_net_peerings": DataSourceOutscaleLinPeeringsConnection(), - "outscale_nics": DataSourceOutscaleNics(), - "outscale_nic": DataSourceOutscaleNic(), - "outscale_client_gateway": DataSourceOutscaleClientGateway(), - "outscale_client_gateways": DataSourceOutscaleClientGateways(), - "outscale_virtual_gateway": DataSourceOutscaleVirtualGateway(), - "outscale_virtual_gateways": DataSourceOutscaleVirtualGateways(), - "outscale_vpn_connection": DataSourceOutscaleVPNConnection(), - "outscale_vpn_connections": DataSourceOutscaleVPNConnections(), - "outscale_access_key": DataSourceOutscaleAccessKey(), - "outscale_access_keys": DataSourceOutscaleAccessKeys(), - "outscale_dhcp_option": DataSourceOutscaleDHCPOption(), - "outscale_dhcp_options": DataSourceOutscaleDHCPOptions(), - "outscale_load_balancer": DataSourceOutscaleLoadBalancer(), - "outscale_load_balancer_listener_rule": DataSourceOutscaleLoadBalancerLDRule(), - "outscale_load_balancer_listener_rules": DataSourceOutscaleLoadBalancerLDRules(), - "outscale_load_balancer_tags": DataSourceOutscaleLBUTags(), - "outscale_load_balancer_vm_health": DataSourceOutscaleLoadBalancerVmsHeals(), - "outscale_load_balancers": DataSourceOutscaleLoadBalancers(), - "outscale_vm_types": DataSourceOutscaleVMTypes(), - "outscale_net_access_point": DataSourceOutscaleNetAccessPoint(), - "outscale_net_access_points": DataSourceOutscaleNetAccessPoints(), - "outscale_flexible_gpu": DataSourceOutscaleFlexibleGpu(), - "outscale_flexible_gpus": DataSourceOutscaleFlexibleGpus(), - "outscale_subregions": DataSourceOutscaleSubregions(), - "outscale_regions": DataSourceOutscaleRegions(), - "outscale_net_access_point_services": DataSourceOutscaleNetAccessPointServices(), - "outscale_flexible_gpu_catalog": DataSourceOutscaleFlexibleGpuCatalog(), - "outscale_product_type": DataSourceOutscaleProductType(), - "outscale_product_types": DataSourceOutscaleProductTypes(), - "outscale_quotas": DataSourceOutscaleQuotas(), - "outscale_image_export_task": DataSourceOutscaleImageExportTask(), - "outscale_image_export_tasks": DataSourceOutscaleImageExportTasks(), - "outscale_server_certificate": DataSourceOutscaleServerCertificate(), - "outscale_server_certificates": DataSourceOutscaleServerCertificates(), - "outscale_snapshot_export_task": DataSourceOutscaleSnapshotExportTask(), - "outscale_snapshot_export_tasks": DataSourceOutscaleSnapshotExportTasks(), - "outscale_ca": DataSourceOutscaleCa(), - "outscale_cas": DataSourceOutscaleCas(), - "outscale_api_access_rule": DataSourceOutscaleApiAccessRule(), - "outscale_api_access_rules": DataSourceOutscaleApiAccessRules(), - "outscale_api_access_policy": DataSourceOutscaleApiAccessPolicy(), - "outscale_public_catalog": DataSourceOutscalePublicCatalog(), - "outscale_account": DataSourceAccount(), - "outscale_accounts": DataSourceAccounts(), + "outscale_vm": DataSourceOutscaleVM(), + "outscale_vms": DataSourceOutscaleVMS(), + "outscale_security_group": DataSourceOutscaleSecurityGroup(), + "outscale_security_groups": DataSourceOutscaleSecurityGroups(), + "outscale_image": DataSourceOutscaleImage(), + "outscale_images": DataSourceOutscaleImages(), + "outscale_tag": DataSourceOutscaleTag(), + "outscale_tags": DataSourceOutscaleTags(), + "outscale_public_ip": DataSourceOutscalePublicIP(), + "outscale_public_ips": DataSourceOutscalePublicIPS(), + "outscale_volume": DataSourceOutscaleVolume(), + "outscale_volumes": DataSourceOutscaleVolumes(), + "outscale_nat_service": DataSourceOutscaleNatService(), + "outscale_nat_services": DataSourceOutscaleNatServices(), + "outscale_keypair": DataSourceOutscaleKeyPair(), + "outscale_keypairs": DataSourceOutscaleKeyPairs(), + "outscale_vm_state": DataSourceOutscaleVMState(), + "outscale_vm_states": DataSourceOutscaleVMStates(), + "outscale_internet_service": DataSourceOutscaleInternetService(), + "outscale_internet_services": DataSourceOutscaleInternetServices(), + "outscale_subnet": DataSourceOutscaleSubnet(), + "outscale_subnets": DataSourceOutscaleSubnets(), + "outscale_net": DataSourceOutscaleVpc(), + "outscale_nets": DataSourceOutscaleVpcs(), + "outscale_net_attributes": DataSourceOutscaleVpcAttr(), + "outscale_route_table": DataSourceOutscaleRouteTable(), + "outscale_route_tables": DataSourceOutscaleRouteTables(), + "outscale_snapshot": DataSourceOutscaleSnapshot(), + "outscale_snapshots": DataSourceOutscaleSnapshots(), + "outscale_net_peering": DataSourceOutscaleLinPeeringConnection(), + "outscale_net_peerings": DataSourceOutscaleLinPeeringsConnection(), + "outscale_nics": DataSourceOutscaleNics(), + "outscale_nic": DataSourceOutscaleNic(), + "outscale_client_gateway": DataSourceOutscaleClientGateway(), + "outscale_client_gateways": DataSourceOutscaleClientGateways(), + "outscale_virtual_gateway": DataSourceOutscaleVirtualGateway(), + "outscale_virtual_gateways": DataSourceOutscaleVirtualGateways(), + "outscale_vpn_connection": DataSourceOutscaleVPNConnection(), + "outscale_vpn_connections": DataSourceOutscaleVPNConnections(), + "outscale_access_key": DataSourceOutscaleAccessKey(), + "outscale_access_keys": DataSourceOutscaleAccessKeys(), + "outscale_dhcp_option": DataSourceOutscaleDHCPOption(), + "outscale_dhcp_options": DataSourceOutscaleDHCPOptions(), + "outscale_load_balancer": DataSourceOutscaleLoadBalancer(), + "outscale_load_balancer_listener_rule": DataSourceOutscaleLoadBalancerLDRule(), + "outscale_load_balancer_listener_rules": DataSourceOutscaleLoadBalancerLDRules(), + "outscale_load_balancer_tags": DataSourceOutscaleLBUTags(), + "outscale_load_balancer_vm_health": DataSourceOutscaleLoadBalancerVmsHeals(), + "outscale_load_balancers": DataSourceOutscaleLoadBalancers(), + "outscale_vm_types": DataSourceOutscaleVMTypes(), + "outscale_net_access_point": DataSourceOutscaleNetAccessPoint(), + "outscale_net_access_points": DataSourceOutscaleNetAccessPoints(), + "outscale_flexible_gpu": DataSourceOutscaleFlexibleGpu(), + "outscale_flexible_gpus": DataSourceOutscaleFlexibleGpus(), + "outscale_subregions": DataSourceOutscaleSubregions(), + "outscale_regions": DataSourceOutscaleRegions(), + "outscale_net_access_point_services": DataSourceOutscaleNetAccessPointServices(), + "outscale_flexible_gpu_catalog": DataSourceOutscaleFlexibleGpuCatalog(), + "outscale_product_type": DataSourceOutscaleProductType(), + "outscale_product_types": DataSourceOutscaleProductTypes(), + "outscale_quotas": DataSourceOutscaleQuotas(), + "outscale_image_export_task": DataSourceOutscaleImageExportTask(), + "outscale_image_export_tasks": DataSourceOutscaleImageExportTasks(), + "outscale_server_certificate": DataSourceOutscaleServerCertificate(), + "outscale_server_certificates": DataSourceOutscaleServerCertificates(), + "outscale_snapshot_export_task": DataSourceOutscaleSnapshotExportTask(), + "outscale_snapshot_export_tasks": DataSourceOutscaleSnapshotExportTasks(), + "outscale_ca": DataSourceOutscaleCa(), + "outscale_cas": DataSourceOutscaleCas(), + "outscale_api_access_rule": DataSourceOutscaleApiAccessRule(), + "outscale_api_access_rules": DataSourceOutscaleApiAccessRules(), + "outscale_api_access_policy": DataSourceOutscaleApiAccessPolicy(), + "outscale_public_catalog": DataSourceOutscalePublicCatalog(), + "outscale_account": DataSourceAccount(), + "outscale_accounts": DataSourceAccounts(), + "outscale_users": DataSourceUsers(), + "outscale_user": DataSourceUser(), + "outscale_user_groups": DataSourceUserGroups(), + "outscale_user_groups_per_user": DataSourceUserGroupsPerUser(), + "outscale_user_group": DataSourceUserGroup(), + "outscale_policy": DataSourcePolicy(), + "outscale_policies": DataSourcePolicies(), + "outscale_linked_policies": DataSourceLinkedPolicies(), + "outscale_entities_linked_to_policy": DataSourceEntitiesLinkedToPolicy(), + "outscale_managed_policies_linked_to_user_group": DataSourceManagedPoliciesLinkedToUserGroup(), }, ConfigureFunc: providerConfigureClient, @@ -292,6 +306,7 @@ func setOldProfile(conf *Config, profile gjson.Result) { if conf.SecretKeyID == "" { if secretKeyId := profile.Get("secret_key").String(); secretKeyId != "" { conf.SecretKeyID = secretKeyId + // checkov:skip=CKV_SECRET_6: ADD REASON } } if conf.Region == "" { @@ -333,6 +348,7 @@ func setProviderDefaultEnv(conf *Config) { } if conf.SecretKeyID == "" { if secretKeyId := utils.GetEnvVariableValue([]string{"OSC_SECRET_KEY", "OUTSCALE_SECRETKEYID"}); secretKeyId != "" { + // checkov:skip=CKV_SECRET_6: ADD REASON conf.SecretKeyID = secretKeyId } } @@ -347,6 +363,7 @@ func setProviderDefaultEnv(conf *Config) { if x509Cert := utils.GetEnvVariableValue([]string{"OSC_X509_CLIENT_CERT", "OUTSCALE_X509CERT"}); x509Cert != "" { conf.X509CertPath = x509Cert } + // checkov:skip=CKV_SECRET_6: ADD REASON } if conf.X509KeyPath == "" { diff --git a/outscale/resource_outscale_access_key.go b/outscale/resource_outscale_access_key.go index be35dede7..b53031b3b 100644 --- a/outscale/resource_outscale_access_key.go +++ b/outscale/resource_outscale_access_key.go @@ -24,6 +24,11 @@ func ResourceOutscaleAccessKey() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, "access_key_id": { Type: schema.TypeString, Computed: true, @@ -76,6 +81,9 @@ func ResourceOutscaleAccessKeyCreate(d *schema.ResourceData, meta interface{}) e } req.ExpirationDate = &expirDate } + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } var resp oscgo.CreateAccessKeyResponse err = resource.Retry(5*time.Minute, func() *resource.RetryError { @@ -91,17 +99,14 @@ func ResourceOutscaleAccessKeyCreate(d *schema.ResourceData, meta interface{}) e } d.SetId(*resp.GetAccessKey().AccessKeyId) - if err := d.Set("secret_key", *resp.GetAccessKey().SecretKey); err != nil { return err } - if d.Get("state").(string) != "ACTIVE" { - if err := updateAccessKey(conn, d.Id(), "INACTIVE"); err != nil { + if err := inactiveAccessKey(d, conn, "INACTIVE"); err != nil { return err } } - return ResourceOutscaleAccessKeyRead(d, meta) } @@ -115,7 +120,9 @@ func ResourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) err req := oscgo.ReadAccessKeysRequest{ Filters: &filter, } - + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } var resp oscgo.ReadAccessKeysResponse err := resource.Retry(2*time.Minute, func() *resource.RetryError { rp, httpResp, err := conn.AccessKeyApi.ReadAccessKeys(context.Background()).ReadAccessKeysRequest(req).Execute() @@ -135,7 +142,11 @@ func ResourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) err d.SetId("") return nil } - + if userName := d.Get("user_name").(string); userName != "" { + if err := d.Set("user_name", userName); err != nil { + return err + } + } if err := d.Set("access_key_id", accessKey[0].GetAccessKeyId()); err != nil { return err } @@ -148,7 +159,6 @@ func ResourceOutscaleAccessKeyRead(d *schema.ResourceData, meta interface{}) err if err := d.Set("last_modification_date", accessKey[0].GetLastModificationDate()); err != nil { return err } - if err := d.Set("state", accessKey[0].GetState()); err != nil { return err } @@ -160,9 +170,16 @@ func ResourceOutscaleAccessKeyUpdate(d *schema.ResourceData, meta interface{}) e conn := meta.(*OutscaleClient).OSCAPI req := oscgo.UpdateAccessKeyRequest{AccessKeyId: d.Id()} + if expirDate, newdate := d.GetChange("expiration_date"); newdate.(string) != "" { + req.SetExpirationDate(expirDate.(string)) + } + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } if d.HasChange("state") { req.State = d.Get("state").(string) } + if d.HasChange("expiration_date") { newExpirDate := d.Get("expiration_date").(string) state := d.Get("state").(string) @@ -170,11 +187,18 @@ func ResourceOutscaleAccessKeyUpdate(d *schema.ResourceData, meta interface{}) e if err := checkDateFormat(newExpirDate); err != nil { return err } - req.ExpirationDate = &newExpirDate + req.SetExpirationDate(newExpirDate) + } else { + if !req.HasUserName() { + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } + } + } + if req.State == "" { + req.SetState(state) } - req.State = state } - err := resource.Retry(2*time.Minute, func() *resource.RetryError { _, httpResp, err := conn.AccessKeyApi.UpdateAccessKey(context.Background()).UpdateAccessKeyRequest(req).Execute() if err != nil { @@ -194,9 +218,13 @@ func ResourceOutscaleAccessKeyDelete(d *schema.ResourceData, meta interface{}) e req := oscgo.DeleteAccessKeyRequest{ AccessKeyId: d.Id(), } - - var err error - err = resource.Retry(5*time.Minute, func() *resource.RetryError { + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + if err := inactiveAccessKey(d, conn, "INACTIVE"); err != nil { + return err + } + } + err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, httpResp, err := conn.AccessKeyApi.DeleteAccessKey(context.Background()).DeleteAccessKeyRequest(req).Execute() if err != nil { return utils.CheckThrottling(httpResp, err) @@ -204,17 +232,20 @@ func ResourceOutscaleAccessKeyDelete(d *schema.ResourceData, meta interface{}) e return nil }) if err != nil { - return fmt.Errorf("Error deleting Outscale Access Key %s: %s", d.Id(), err) + return fmt.Errorf(" Error deleting Outscale Access Key %s: %s", d.Id(), err) } return nil } -func updateAccessKey(conn *oscgo.APIClient, id, state string) error { +func inactiveAccessKey(d *schema.ResourceData, conn *oscgo.APIClient, state string) error { req := oscgo.UpdateAccessKeyRequest{ - AccessKeyId: id, + AccessKeyId: d.Id(), State: state, } + if userName := d.Get("user_name").(string); userName != "" { + req.SetUserName(userName) + } err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, httpResp, err := conn.AccessKeyApi.UpdateAccessKey(context.Background()).UpdateAccessKeyRequest(req).Execute() if err != nil { @@ -222,7 +253,6 @@ func updateAccessKey(conn *oscgo.APIClient, id, state string) error { } return nil }) - if err != nil { return err } @@ -235,7 +265,7 @@ func checkDateFormat(dateFormat string) error { currentDate := time.Now() if settingDate, err = datetime.Parse(dateFormat, time.UTC); err != nil { - return fmt.Errorf("Expiration Date should be 'ISO 8601' format ('2017-06-14' or '2017-06-14T00:00:00Z, ...) %s", err) + return fmt.Errorf(" Expiration Date should be 'ISO 8601' format ('2017-06-14' or '2017-06-14T00:00:00Z, ...) %s", err) } if currentDate.After(settingDate) { return fmt.Errorf(" Expiration date: '%s' should be after current date '%s'", settingDate, currentDate) diff --git a/outscale/resource_outscale_load_balancer_attributes_test.go b/outscale/resource_outscale_load_balancer_attributes_test.go index f994bb9fc..c1e929b6d 100644 --- a/outscale/resource_outscale_load_balancer_attributes_test.go +++ b/outscale/resource_outscale_load_balancer_attributes_test.go @@ -14,8 +14,11 @@ import ( func TestAccOthers_LBUAttr_basic(t *testing.T) { t.Parallel() var conf oscgo.AccessLog - - r := utils.RandIntRange(20, 30) + const ( + MIN_LB_NAME_SUFFIX int = 1 + MAX_LB_NAME_SUFFIX int = 1000 + ) + suffix := utils.RandIntRange(MIN_LB_NAME_SUFFIX, MAX_LB_NAME_SUFFIX) resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -25,7 +28,7 @@ func TestAccOthers_LBUAttr_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccOutscaleLBUAttrConfig(utils.GetRegion(), r), + Config: testAccOutscaleLBUAttrConfig(utils.GetRegion(), suffix), Check: resource.ComposeTestCheckFunc( testAccCheckOutscaleLBUAttrExists("outscale_load_balancer_attributes.bar2", &conf), )}, @@ -48,7 +51,7 @@ func testAccCheckOutscaleLBUAttrExists(n string, res *oscgo.AccessLog) resource. } } -func testAccOutscaleLBUAttrConfig(region string, r int) string { +func testAccOutscaleLBUAttrConfig(region string, suffix int) string { return fmt.Sprintf(` resource "outscale_load_balancer" "barAtt" { subregion_names = ["%sa"] @@ -72,5 +75,5 @@ resource "outscale_load_balancer_attributes" "bar2" { } load_balancer_name = outscale_load_balancer.barAtt.id } -`, region, r) +`, region, suffix) } diff --git a/outscale/resource_outscale_policy.go b/outscale/resource_outscale_policy.go new file mode 100644 index 000000000..283ac4749 --- /dev/null +++ b/outscale/resource_outscale_policy.go @@ -0,0 +1,242 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourceOutscalePolicy() *schema.Resource { + return &schema.Resource{ + Create: ResourceOutscalePolicyCreate, + Read: ResourceOutscalePolicyRead, + Delete: ResourceOutscalePolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "document": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "resources_count": { + Type: schema.TypeInt, + Computed: true, + }, + "policy_default_version_id": { + Type: schema.TypeString, + Computed: true, + }, + "is_linkable": { + Type: schema.TypeBool, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourceOutscalePolicyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + polDocument := d.Get("document").(string) + req := oscgo.NewCreatePolicyRequest(polDocument, d.Get("policy_name").(string)) + if polPath := d.Get("path").(string); polPath != "" { + req.SetPath(polPath) + } + if polDescription := d.Get("description").(string); polDescription != "" { + req.SetDescription(polDescription) + } + + var resp oscgo.CreatePolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.CreatePolicy(context.Background()).CreatePolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + + d.SetId(resource.UniqueId()) + ply := resp.GetPolicy() + if err := d.Set("orn", ply.GetOrn()); err != nil { + return err + } + + return ResourceOutscalePolicyRead(d, meta) +} + +func ResourceOutscalePolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyRequest(d.Get("orn").(string)) + + var resp oscgo.ReadPolicyResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicy(context.Background()).ReadPolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyOk(); !ok { + d.SetId("") + return nil + } + policy := resp.GetPolicy() + if err := d.Set("policy_name", policy.GetPolicyName()); err != nil { + return err + } + if err := d.Set("policy_id", policy.GetPolicyId()); err != nil { + return err + } + if err := d.Set("path", policy.GetPath()); err != nil { + return err + } + if err := d.Set("orn", policy.GetOrn()); err != nil { + return err + } + if err := d.Set("resources_count", policy.GetResourcesCount()); err != nil { + return err + } + if err := d.Set("is_linkable", policy.GetIsLinkable()); err != nil { + return err + } + if err := d.Set("policy_default_version_id", policy.GetPolicyDefaultVersionId()); err != nil { + return err + } + if err := d.Set("description", policy.GetDescription()); err != nil { + return err + } + + if err := d.Set("creation_date", (policy.GetCreationDate())); err != nil { + return err + } + + if err := d.Set("last_modification_date", (policy.GetLastModificationDate())); err != nil { + return err + } + return nil +} + +func ResourceOutscalePolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + policyOrn := d.Get("orn").(string) + if err := unlinkEntitiesToPolicy(conn, policyOrn); err != nil { + return err + } + + req := oscgo.NewDeletePolicyRequest(policyOrn) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.DeletePolicy(context.Background()).DeletePolicyRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("error deleting Outscale Policy %s: %s", d.Id(), err) + } + return nil +} + +func unlinkEntitiesToPolicy(conn *oscgo.APIClient, policyOrn string) error { + + req := oscgo.ReadEntitiesLinkedToPolicyRequest{PolicyOrn: &policyOrn} + var users, groups []oscgo.MinimalPolicy + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + resp, httpResp, err := conn.PolicyApi.ReadEntitiesLinkedToPolicy(context.Background()).ReadEntitiesLinkedToPolicyRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + if resp.HasPolicyEntities() { + users = *resp.GetPolicyEntities().Users + groups = *resp.GetPolicyEntities().Groups + } + return nil + }) + if err != nil { + return err + } + if len(users) > 0 { + unlinkReq := oscgo.UnlinkPolicyRequest{} + unlinkReq.SetPolicyOrn(policyOrn) + + for _, user := range users { + unlinkReq.SetUserName(*user.Name) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkPolicy(context.Background()).UnlinkPolicyRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(groups) > 0 { + unlinkReq := oscgo.UnlinkManagedPolicyFromUserGroupRequest{PolicyOrn: policyOrn} + for _, group := range groups { + unlinkReq.SetUserGroupName(*group.Name) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkManagedPolicyFromUserGroup(context.Background()).UnlinkManagedPolicyFromUserGroupRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + + return err +} diff --git a/outscale/resource_outscale_policy_test.go b/outscale/resource_outscale_policy_test.go new file mode 100644 index 000000000..da22e88ae --- /dev/null +++ b/outscale/resource_outscale_policy_test.go @@ -0,0 +1,33 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_policy_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_policy.basic_policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "policy_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +const testAccPolicyBasicConfig = ` + resource "outscale_policy" "basic_policy" { + policy_name = "TestACC_resoucePolicy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + }` diff --git a/outscale/resource_outscale_policy_version.go b/outscale/resource_outscale_policy_version.go new file mode 100644 index 000000000..42e82eb4d --- /dev/null +++ b/outscale/resource_outscale_policy_version.go @@ -0,0 +1,189 @@ +package outscale + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourcePolicyVersion() *schema.Resource { + return &schema.Resource{ + Create: ResourcePolicyVersionCreate, + Read: ResourcePolicyVersionRead, + Delete: ResourcePolicyVersionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "document": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "set_as_default": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "version_id": { + Type: schema.TypeString, + Computed: true, + }, + "default_version": { + Type: schema.TypeBool, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "body": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func ResourcePolicyVersionCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + pvDocument := d.Get("document").(string) + req := oscgo.NewCreatePolicyVersionRequest(pvDocument, d.Get("policy_orn").(string)) + + if asDefault, ok := d.GetOk("set_as_default"); ok { + req.SetSetAsDefault(asDefault.(bool)) + } + + var resp oscgo.CreatePolicyVersionResponse + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.CreatePolicyVersion(context.Background()).CreatePolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + + d.SetId(resource.UniqueId()) + pVersion := resp.GetPolicyVersion() + if err := d.Set("version_id", pVersion.GetVersionId()); err != nil { + return err + } + + return ResourcePolicyVersionRead(d, meta) +} + +func ResourcePolicyVersionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyVersionRequest(d.Get("policy_orn").(string), d.Get("version_id").(string)) + + var resp oscgo.ReadPolicyVersionResponse + err := retry.Retry(2*time.Minute, func() *retry.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicyVersion(context.Background()).ReadPolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetPolicyVersionOk(); !ok { + d.SetId("") + return nil + } + pVersion := resp.GetPolicyVersion() + if err := d.Set("default_version", pVersion.GetDefaultVersion()); err != nil { + return err + } + if err := d.Set("creation_date", (pVersion.GetCreationDate())); err != nil { + return err + } + if err := d.Set("body", pVersion.GetBody()); err != nil { + return err + } + return nil +} + +func ResourcePolicyVersionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + if d.Get("default_version").(bool) { + resps, err := readPolicyVersions(d, meta) + if err != nil { + return err + } + if _, ok := resps.GetPolicyVersionsOk(); !ok { + d.SetId("") + return nil + } + pVersions := resps.GetPolicyVersions() + if len(pVersions) <= 1 { + return fmt.Errorf("cannot delete the default policy version\n It will be deleted with the policy") + } + for _, version := range pVersions { + if version.GetVersionId() == "v1" { + req := oscgo.NewSetDefaultPolicyVersionRequest(d.Get("policy_orn").(string), version.GetVersionId()) + err = retry.Retry(2*time.Minute, func() *retry.RetryError { + _, httpResp, err := conn.PolicyApi.SetDefaultPolicyVersion(context.Background()).SetDefaultPolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + break + } + } + } + + req := oscgo.NewDeletePolicyVersionRequest(d.Get("policy_orn").(string), d.Get("version_id").(string)) + + err := retry.Retry(2*time.Minute, func() *retry.RetryError { + _, httpResp, err := conn.PolicyApi.DeletePolicyVersion(context.Background()).DeletePolicyVersionRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("error deleting Outscale Policy version %s: %s", d.Id(), err) + } + + return nil +} + +func readPolicyVersions(d *schema.ResourceData, meta interface{}) (oscgo.ReadPolicyVersionsResponse, error) { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadPolicyVersionsRequest(d.Get("policy_orn").(string)) + + var resp oscgo.ReadPolicyVersionsResponse + err := retry.Retry(2*time.Minute, func() *retry.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadPolicyVersions(context.Background()).ReadPolicyVersionsRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + return resp, err +} diff --git a/outscale/resource_outscale_policy_version_test.go b/outscale/resource_outscale_policy_version_test.go new file mode 100644 index 000000000..83b72efc3 --- /dev/null +++ b/outscale/resource_outscale_policy_version_test.go @@ -0,0 +1,39 @@ +package outscale + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_policy_Version_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_policy_version.policy_version" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPolicyVersionConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "body"), + resource.TestCheckResourceAttr(resourceName, "version_id", "v2"), + ), + }, + }, + }) +} + +const testAccPolicyVersionConfig = ` + resource "outscale_policy" "vers_policy" { + policy_name = "TestACC_VersionPolicy" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + } + resource "outscale_policy_version" "policy_version" { + policy_orn = outscale_policy.vers_policy.orn + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + } +` diff --git a/outscale/resource_outscale_user.go b/outscale/resource_outscale_user.go new file mode 100644 index 000000000..2932f9f25 --- /dev/null +++ b/outscale/resource_outscale_user.go @@ -0,0 +1,365 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourceOutscaleUser() *schema.Resource { + return &schema.Resource{ + Create: ResourceOutscaleUserCreate, + Read: ResourceOutscaleUserRead, + Update: ResourceOutscaleUserUpdate, + Delete: ResourceOutscaleUserDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Required: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Default: "/", + }, + "user_email": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + "policy": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "default_version_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func ResourceOutscaleUserCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewCreateUserRequest(d.Get("user_name").(string)) + path := d.Get("path").(string) + if err := utils.CheckPath(path); err != nil { + return fmt.Errorf("%v\n Got: %v", err, path) + } + req.SetPath(path) + if email := d.Get("user_email").(string); email != "" { + req.SetUserEmail(email) + } + + var resp oscgo.CreateUserResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserApi.CreateUser(context.Background()).CreateUserRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + d.SetId(*resp.GetUser().UserId) + if policiesToAdd, ok := d.GetOk("policy"); ok { + reqAddPolicy := oscgo.LinkPolicyRequest{} + + for _, v := range policiesToAdd.(*schema.Set).List() { + policy := v.(map[string]interface{}) + reqAddPolicy.SetUserName(d.Get("user_name").(string)) + reqAddPolicy.SetPolicyOrn(policy["policy_orn"].(string)) + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkPolicy(context.Background()).LinkPolicyRequest(reqAddPolicy).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + if versionId := policy["default_version_id"].(string); versionId != "" { + if err := setDefaultPolicyVersion(conn, policy["policy_orn"].(string), versionId); err != nil { + return err + } + } + } + } + + return ResourceOutscaleUserRead(d, meta) +} + +func ResourceOutscaleUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.ReadUsersRequest{ + Filters: &oscgo.FiltersUsers{UserIds: &[]string{d.Id()}}, + } + + var resp oscgo.ReadUsersResponse + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserApi.ReadUsers(context.Background()).ReadUsersRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + users := resp.GetUsers() + if len(users) == 0 { + d.SetId("") + return nil + } + linkReq := oscgo.NewReadLinkedPoliciesRequest(d.Get("user_name").(string)) + var linkResp oscgo.ReadLinkedPoliciesResponse + err = resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadLinkedPolicies(context.Background()).ReadLinkedPoliciesRequest(*linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + linkResp = rp + return nil + }) + if err != nil { + return err + } + + if err := d.Set("user_name", users[0].GetUserName()); err != nil { + return err + } + if err := d.Set("user_id", users[0].GetUserId()); err != nil { + return err + } + if err := d.Set("path", users[0].GetPath()); err != nil { + return err + } + if err := d.Set("user_email", users[0].GetUserEmail()); err != nil { + return err + } + if err := d.Set("creation_date", users[0].GetCreationDate()); err != nil { + return err + } + if err := d.Set("last_modification_date", users[0].GetLastModificationDate()); err != nil { + return err + } + + uPolicies := linkResp.GetPolicies() + policies := make([]map[string]interface{}, len(uPolicies)) + if len(uPolicies) > 0 { + for i, v := range uPolicies { + policy := make(map[string]interface{}) + policy["policy_id"] = v.GetPolicyId() + policy["policy_name"] = v.GetPolicyName() + policy["policy_orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + versionId, err := getPolicyVersion(conn, v.GetOrn()) + if err != nil { + return err + } + policy["default_version_id"] = versionId + + policies[i] = policy + } + } + if err := d.Set("policy", policies); err != nil { + return err + } + return nil +} + +func ResourceOutscaleUserUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.UpdateUserRequest{UserName: d.Get("user_name").(string)} + isUpdateUser := false + if d.HasChange("user_name") { + oldN, newN := d.GetChange("user_name") + if oldName := oldN.(string); oldName != "" { + req.SetUserName(oldName) + isUpdateUser = true + } + if newName := newN.(string); newName != "" && oldN.(string) != newN.(string) { + req.SetNewUserName(newName) + isUpdateUser = true + } + } + if d.HasChange("path") { + path := d.Get("path").(string) + if err := utils.CheckPath(path); err != nil { + return fmt.Errorf("%v, got:%v", err, path) + } + req.SetNewPath(path) + if req.GetUserName() == "" { + req.SetUserName(d.Get("user_name").(string)) + } + isUpdateUser = true + } + if d.HasChange("user_email") { + _, newM := d.GetChange("user_email") + req.SetNewUserEmail(newM.(string)) + isUpdateUser = true + } + if d.HasChange("policy") { + oldPolicies, newPolicies := d.GetChange("policy") + inter := oldPolicies.(*schema.Set).Intersection(newPolicies.(*schema.Set)) + toCreate := newPolicies.(*schema.Set).Difference(inter) + toRemove := oldPolicies.(*schema.Set).Difference(inter) + + if len(toRemove.List()) > 0 { + unlinkReq := oscgo.UnlinkPolicyRequest{} + oldN, _ := d.GetChange("user_name") + unlinkReq.SetUserName(oldN.(string)) + + for _, v := range toRemove.List() { + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkPolicy(context.Background()).UnlinkPolicyRequest(unlinkReq).Execute() + if err != nil { + log.Println("[INFO]: The policy has already been removed") + if strings.Contains(fmt.Sprint(httpResp.Body), utils.LinkedPolicyNotFound) { + return nil + } + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(toCreate.List()) > 0 { + linkReq := oscgo.LinkPolicyRequest{} + oldN, _ := d.GetChange("user_name") + linkReq.SetUserName(oldN.(string)) + + for _, v := range toCreate.List() { + policy := v.(map[string]interface{}) + linkReq.SetPolicyOrn(policy["policy_orn"].(string)) + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkPolicy(context.Background()).LinkPolicyRequest(linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + if versionId := policy["default_version_id"].(string); versionId != "" { + if err := setDefaultPolicyVersion(conn, policy["policy_orn"].(string), versionId); err != nil { + return err + } + } + } + } + } + if isUpdateUser { + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserApi.UpdateUser(context.Background()).UpdateUserRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + return ResourceOutscaleUserRead(d, meta) +} + +func ResourceOutscaleUserDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + if _, ok := d.GetOk("policy"); ok { + policies := d.Get("policy") + for _, v := range policies.(*schema.Set).List() { + unlinkReq := oscgo.UnlinkPolicyRequest{} + unlinkReq.SetUserName(d.Get("user_name").(string)) + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkPolicy(context.Background()).UnlinkPolicyRequest(unlinkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + + req := oscgo.DeleteUserRequest{ + UserName: d.Get("user_name").(string), + } + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserApi.DeleteUser(context.Background()).DeleteUserRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("error deleting Outscale user %s: %s", d.Id(), err) + } + + return nil +} diff --git a/outscale/resource_outscale_user_group.go b/outscale/resource_outscale_user_group.go new file mode 100644 index 000000000..9bc28890d --- /dev/null +++ b/outscale/resource_outscale_user_group.go @@ -0,0 +1,576 @@ +package outscale + +import ( + "context" + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + oscgo "github.com/outscale/osc-sdk-go/v2" + "github.com/outscale/terraform-provider-outscale/utils" +) + +func ResourceUserGroup() *schema.Resource { + return &schema.Resource{ + Create: ResourceUserGroupCreate, + Read: ResourceUserGroupRead, + Update: ResourceUserGroupUpdate, + Delete: ResourceUserGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "user_group_name": { + Type: schema.TypeString, + Required: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Default: "/", + }, + "user_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "orn": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + "policy": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_orn": { + Type: schema.TypeString, + Required: true, + }, + "default_version_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "policy_name": { + Type: schema.TypeString, + Computed: true, + }, + "policy_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "user": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Required: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "last_modification_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func ResourceUserGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + req := oscgo.NewCreateUserGroupRequest(d.Get("user_group_name").(string)) + groupPath := d.Get("path").(string) + if err := utils.CheckPath(groupPath); err != nil { + return fmt.Errorf("%v, got:%v", err, groupPath) + } + req.SetPath(groupPath) + + var resp oscgo.CreateUserGroupResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.CreateUserGroup(context.Background()).CreateUserGroupRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + if err != nil { + return err + } + + d.SetId(resource.UniqueId()) + if err := d.Set("user_group_id", resp.GetUserGroup().UserGroupId); err != nil { + return err + } + if usersToAdd, ok := d.GetOk("user"); ok { + reqUserAdd := oscgo.AddUserToUserGroupRequest{} + reqUserAdd.SetUserGroupName(d.Get("user_group_name").(string)) + reqUserAdd.SetUserGroupPath(groupPath) + + for _, v := range usersToAdd.(*schema.Set).List() { + user := v.(map[string]interface{}) + if userName := user["user_name"].(string); userName != "" { + reqUserAdd.SetUserName(userName) + } + if path := user["path"].(string); path != "" { + if err := utils.CheckPath(path); err != nil { + return fmt.Errorf("%v, got:%v", err, path) + } + reqUserAdd.SetUserPath(path) + } + reqUserAdd.SetUserGroupPath(groupPath) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserGroupApi.AddUserToUserGroup(context.Background()).AddUserToUserGroupRequest(reqUserAdd).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + + } + + } + if policiesToAdd, ok := d.GetOk("policy"); ok { + reqAddPolicy := oscgo.LinkManagedPolicyToUserGroupRequest{} + + for _, v := range policiesToAdd.(*schema.Set).List() { + policy := v.(map[string]interface{}) + + reqAddPolicy.SetUserGroupName(d.Get("user_group_name").(string)) + reqAddPolicy.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkManagedPolicyToUserGroup(context.Background()).LinkManagedPolicyToUserGroupRequest(reqAddPolicy).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + if versionId := policy["default_version_id"].(string); versionId != "" { + if err := setDefaultPolicyVersion(conn, policy["policy_orn"].(string), versionId); err != nil { + return err + } + } + } + } + return ResourceUserGroupRead(d, meta) +} + +func ResourceUserGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + filter := oscgo.FiltersUserGroup{ + UserGroupIds: &[]string{d.Get("user_group_id").(string)}, + } + req := oscgo.ReadUserGroupsRequest{} + req.SetFilters(filter) + var statusCode int + var resp oscgo.ReadUserGroupsResponse + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroups(context.Background()).ReadUserGroupsRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + statusCode = httpResp.StatusCode + return nil + }) + if err != nil { + if statusCode == http.StatusNotFound { + d.SetId("") + return nil + } + return err + } + + if _, ok := resp.GetUserGroupsOk(); !ok { + d.SetId("") + return nil + } + userGroup := resp.GetUserGroups()[0] + reqUser := oscgo.NewReadUserGroupRequest(userGroup.GetName()) + + var groupUsers []oscgo.User + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroup(context.Background()).ReadUserGroupRequest(*reqUser).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + groupUsers = rp.GetUsers() + statusCode = httpResp.StatusCode + return nil + }) + + linkReq := oscgo.NewReadManagedPoliciesLinkedToUserGroupRequest(d.Get("user_group_name").(string)) + var linkResp oscgo.ReadManagedPoliciesLinkedToUserGroupResponse + err = resource.Retry(1*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.PolicyApi.ReadManagedPoliciesLinkedToUserGroup(context.Background()).ReadManagedPoliciesLinkedToUserGroupRequest(*linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + linkResp = rp + return nil + }) + if err != nil { + return err + } + + if err := d.Set("user_group_name", userGroup.GetName()); err != nil { + return err + } + if err := d.Set("path", userGroup.GetPath()); err != nil { + return err + } + if err := d.Set("orn", userGroup.GetOrn()); err != nil { + return err + } + if err := d.Set("creation_date", (userGroup.GetCreationDate())); err != nil { + return err + } + if err := d.Set("last_modification_date", (userGroup.GetLastModificationDate())); err != nil { + return err + } + + users := make([]map[string]interface{}, len(groupUsers)) + if len(groupUsers) > 0 { + for i, v := range groupUsers { + user := make(map[string]interface{}) + user["user_id"] = v.GetUserId() + user["user_name"] = v.GetUserName() + user["path"] = v.GetPath() + user["creation_date"] = v.GetCreationDate() + user["last_modification_date"] = v.GetLastModificationDate() + users[i] = user + } + } + if err := d.Set("user", users); err != nil { + return err + } + + gPolicies := linkResp.GetPolicies() + policies := make([]map[string]interface{}, len(gPolicies)) + if len(gPolicies) > 0 { + for i, v := range gPolicies { + policy := make(map[string]interface{}) + policy["policy_id"] = v.GetPolicyId() + policy["policy_name"] = v.GetPolicyName() + policy["policy_orn"] = v.GetOrn() + policy["creation_date"] = v.GetCreationDate() + policy["last_modification_date"] = v.GetLastModificationDate() + versionId, err := getPolicyVersion(conn, v.GetOrn()) + if err != nil { + return err + } + policy["default_version_id"] = versionId + policies[i] = policy + } + } + if err := d.Set("policy", policies); err != nil { + return err + } + return nil +} + +func ResourceUserGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.UpdateUserGroupRequest{} + isUpdateGroup := false + if d.HasChange("user_group_name") { + oldName, newName := d.GetChange("user_group_name") + req.SetUserGroupName(oldName.(string)) + req.SetNewUserGroupName(newName.(string)) + isUpdateGroup = true + } + if d.HasChange("path") { + oldPath, newPath := d.GetChange("path") + oldName, _ := d.GetChange("user_group_name") + if err := utils.CheckPath(newPath.(string)); err != nil { + return fmt.Errorf("%v, got:%v", err, newPath.(string)) + } + if err := utils.CheckPath(oldPath.(string)); err != nil { + return fmt.Errorf("%v, got:%v", err, oldPath.(string)) + } + req.SetPath(oldPath.(string)) + req.SetNewPath(newPath.(string)) + req.SetUserGroupName(oldName.(string)) + isUpdateGroup = true + } + if d.HasChange("user") { + oldUsers, newUsers := d.GetChange("user") + inter := oldUsers.(*schema.Set).Intersection(newUsers.(*schema.Set)) + toCreate := newUsers.(*schema.Set).Difference(inter) + toRemove := oldUsers.(*schema.Set).Difference(inter) + + if len(toRemove.List()) > 0 { + rmUserReq := oscgo.RemoveUserFromUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + rmUserReq.SetUserGroupName(oldN.(string)) + oldP, _ := d.GetChange("path") + rmUserReq.SetUserGroupPath(oldP.(string)) + _, checkUpdate, _, err := getUsersLinkedToGroup(conn, toCreate.List(), oldN.(string)) + if err != nil { + return err + } + for _, v := range toRemove.List() { + user := v.(map[string]interface{}) + if len(checkUpdate) != 0 { + if checkUpdate[user["user_id"].(string)] { + continue + } + } + if userName := user["user_name"].(string); userName != "" { + rmUserReq.SetUserName(userName) + } + if path := user["path"].(string); path != "" { + rmUserReq.SetUserPath(path) + } + err = resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserGroupApi.RemoveUserFromUserGroup(context.Background()).RemoveUserFromUserGroupRequest(rmUserReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(toCreate.List()) > 0 { + addUserReq := oscgo.AddUserToUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + addUserReq.SetUserGroupName(oldN.(string)) + oldP, _ := d.GetChange("path") + addUserReq.SetUserGroupPath(oldP.(string)) + _, _, checkUpdate, err := getUsersLinkedToGroup(conn, toCreate.List(), oldN.(string)) + if err != nil { + return err + } + for _, v := range toCreate.List() { + user := v.(map[string]interface{}) + if len(checkUpdate) != 0 { + if checkUpdate[user["user_name"].(string)] { + continue + } + } + if userName := user["user_name"].(string); userName != "" { + addUserReq.SetUserName(userName) + } + if path := user["path"].(string); path != "" { + if err := utils.CheckPath(path); err != nil { + return fmt.Errorf("%v, got:%v", err, path) + } + addUserReq.SetUserPath(path) + } + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserGroupApi.AddUserToUserGroup(context.Background()).AddUserToUserGroupRequest(addUserReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + + } + } + } + + if d.HasChange("policy") { + oldPolicies, newPolicies := d.GetChange("policy") + inter := oldPolicies.(*schema.Set).Intersection(newPolicies.(*schema.Set)) + toCreate := newPolicies.(*schema.Set).Difference(inter) + toRemove := oldPolicies.(*schema.Set).Difference(inter) + + if len(toRemove.List()) > 0 { + unlinkReq := oscgo.UnlinkManagedPolicyFromUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + unlinkReq.SetUserGroupName(oldN.(string)) + for _, v := range toRemove.List() { + policy := v.(map[string]interface{}) + unlinkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.UnlinkManagedPolicyFromUserGroup(context.Background()).UnlinkManagedPolicyFromUserGroupRequest(unlinkReq).Execute() + if err != nil { + log.Println("[INFO]: The policy has already been removed") + if strings.Contains(fmt.Sprint(httpResp.Body), utils.LinkedPolicyNotFound) { + return nil + } + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + } + if len(toCreate.List()) > 0 { + linkReq := oscgo.LinkManagedPolicyToUserGroupRequest{} + oldN, _ := d.GetChange("user_group_name") + linkReq.SetUserGroupName(oldN.(string)) + + for _, v := range toCreate.List() { + policy := v.(map[string]interface{}) + linkReq.SetPolicyOrn(policy["policy_orn"].(string)) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.LinkManagedPolicyToUserGroup(context.Background()).LinkManagedPolicyToUserGroupRequest(linkReq).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + if versionId := policy["default_version_id"].(string); versionId != "" { + if err := setDefaultPolicyVersion(conn, policy["policy_orn"].(string), versionId); err != nil { + return err + } + } + } + } + } + if isUpdateGroup { + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserGroupApi.UpdateUserGroup(context.Background()).UpdateUserGroupRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return err + } + } + return ResourceUserGroupRead(d, meta) +} + +func ResourceUserGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + forceDeletion := true + req := oscgo.DeleteUserGroupRequest{ + UserGroupName: d.Get("user_group_name").(string), + Force: &forceDeletion, + } + + err := resource.Retry(3*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.UserGroupApi.DeleteUserGroup(context.Background()).DeleteUserGroupRequest(req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + if err != nil { + return fmt.Errorf("error deleting Outscale User Group %s: %s", d.Id(), err) + } + + return nil +} + +func getPolicyVersion(conn *oscgo.APIClient, policyOrn string) (string, error) { + version_id := "" + err := resource.Retry(3*time.Minute, func() *resource.RetryError { + resp, httpResp, err := conn.PolicyApi.ReadPolicy(context.Background()).ReadPolicyRequest( + oscgo.ReadPolicyRequest{PolicyOrn: policyOrn}).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + policy := resp.GetPolicy() + version_id = policy.GetPolicyDefaultVersionId() + return nil + }) + if err != nil { + return version_id, err + } + return version_id, err +} + +func setDefaultPolicyVersion(conn *oscgo.APIClient, policyOrn, version string) error { + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + _, httpResp, err := conn.PolicyApi.SetDefaultPolicyVersion(context.Background()).SetDefaultPolicyVersionRequest( + oscgo.SetDefaultPolicyVersionRequest{ + PolicyOrn: policyOrn, + VersionId: version, + }).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + return nil + }) + return err +} + +func getUsersLinkedToGroup(conn *oscgo.APIClient, toCreate []interface{}, groupName string) ([]oscgo.User, map[string]bool, map[string]bool, error) { + reqUser := oscgo.NewReadUserGroupRequest(groupName) + + var users []oscgo.User + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroup(context.Background()).ReadUserGroupRequest(*reqUser).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + users = rp.GetUsers() + return nil + }) + checkIds := make(map[string]bool) + checkName := make(map[string]bool) + for _, user := range users { + if len(toCreate) > 0 { + for _, v := range toCreate { + userTo := v.(map[string]interface{}) + if userTo["user_name"] == user.GetUserName() { + checkIds[user.GetUserId()] = true + checkName[user.GetUserName()] = true + } + } + } + } + return users, checkIds, checkName, err +} diff --git a/outscale/resource_outscale_user_group_test.go b/outscale/resource_outscale_user_group_test.go new file mode 100644 index 000000000..6953e773a --- /dev/null +++ b/outscale/resource_outscale_user_group_test.go @@ -0,0 +1,132 @@ +package outscale + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_user_group_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.basic_group" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} +func TestAccOthers_userGroup_with_user(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.userGroupAcc" + groupName := "groupWithUsers" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupWithUsers(groupName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", groupName), + ), + }, + }, + }) +} + +func TestAccOthers_userGroup_update(t *testing.T) { + t.Parallel() + resourceName := "outscale_user_group.userGroupTAcc1" + groupName := "Gp1UpUser" + userName := "userGp1" + newGpName := "Gp2UpUsers" + newUsName := "userGp2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccUserGroupUpadate(groupName, userName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", groupName), + ), + }, + { + Config: testAccUserGroupUpadate(newGpName, newUsName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "user.#"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_group_name", newGpName), + ), + }, + }, + }) +} + +const testAccUserGroupBasicConfig = ` + resource "outscale_user_group" "basic_group" { + user_group_name = "TestACC_group1" + path = "/" + }` + +func testAccUserGroupWithUsers(name string) string { + return fmt.Sprintf(` + resource "outscale_user" "userToAdd1" { + user_name = "userForGp1" + path = "/" + } + resource "outscale_user" "userToAdd2" { + user_name = "userForGp2" + path = "/TestPath/" + } + + resource "outscale_user_group" "userGroupAcc" { + user_group_name = "%s" + path = "/" + user { + user_name = outscale_user.userToAdd1.user_name + } + user { + user_name = outscale_user.userToAdd2.user_name + path = "/TestPath/" + } + } + `, name) +} + +func testAccUserGroupUpadate(name, userName string) string { + return fmt.Sprintf(` + resource "outscale_user" "userUpToAdd01" { + user_name = "userGp1" + path = "/" + } + resource "outscale_user" "userUpToAdd02" { + user_name = "userGp2" + path = "/TestPath/" + } + resource "outscale_user_group" "userGroupTAcc1" { + user_group_name = "%s" + path = "/" + user { + user_name = "%s" + } + depends_on = [outscale_user.userUpToAdd01] + } + `, name, userName) +} diff --git a/outscale/resource_outscale_user_test.go b/outscale/resource_outscale_user_test.go new file mode 100644 index 000000000..f1fe7ce30 --- /dev/null +++ b/outscale/resource_outscale_user_test.go @@ -0,0 +1,78 @@ +package outscale + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccOthers_User_basic(t *testing.T) { + t.Parallel() + resourceName := "outscale_user.basic_user" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleUserBasicConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + ), + }, + }, + }) +} + +func TestAccOthers_User_update(t *testing.T) { + t.Parallel() + resourceName := "outscale_user.update_user" + name := "TestACC_user1" + newName := "TestACC_user2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccOutscaleUserUpdatedConfig(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_name", name), + ), + }, + { + Config: testAccOutscaleUserUpdatedConfig(newName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "path"), + resource.TestCheckResourceAttrSet(resourceName, "user_name"), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "user_name", newName), + ), + }, + }, + }) +} + +const testAccOutscaleUserBasicConfig = ` + resource "outscale_access_key" "access_key01" { + state = "ACTIVE" + user_name = outscale_user.basic_user.user_name + depends_on = [outscale_user.basic_user] + } + resource "outscale_user" "basic_user" { + user_name = "ACC_test1" + path = "/" + }` + +func testAccOutscaleUserUpdatedConfig(name string) string { + return fmt.Sprintf(` + resource "outscale_user" "update_user" { + user_name = "%s" + path = "/" + } + `, name) +} diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref new file mode 100644 index 000000000..86c7b7495 --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.ref @@ -0,0 +1,32 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "outscale_user", + "name": "userInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "id": "##id-0##", + "last_modification_date": "########", + "path": "/", + "policy": [], + "user_id": "##id-0##", + "user_name": "test_integ" + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf new file mode 100644 index 000000000..09b10093f --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step1.user_resource_attributes_ok.tf @@ -0,0 +1,4 @@ +resource "outscale_user" "userInteg" { + user_name = "test_integ" + path = "/" +} diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref new file mode 100644 index 000000000..1fe88f7e0 --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.ref @@ -0,0 +1,70 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "outscale_policy", + "name": "policy_user01", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "description": "Example of description", + "document": "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}", + "id": "##id-0##", + "is_linkable": true, + "last_modification_date": "########", + "orn": "########", + "path": "/okht/", + "policy_default_version_id": "##id-1##", + "policy_id": "##id-2##", + "policy_name": "okht-user-policy", + "resources_count": 0 + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "outscale_user", + "name": "userInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "id": "##id-3##", + "last_modification_date": "########", + "path": "/Integ/", + "policy": [ + { + "creation_date": "########", + "last_modification_date": "########", + "policy_id": "##id-2##", + "policy_name": "okht-user-policy", + "policy_orn": "orn:ows:idauth::339215505907:policy/okht/okht-user-policy" + } + ], + "user_id": "##id-3##", + "user_name": "test_integ_update" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "outscale_policy.policy_user01" + ] + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf new file mode 100644 index 000000000..796188299 --- /dev/null +++ b/tests/qa_provider_oapi/data/user/TF-01_user_resource_attributes_ok/step2.user_resource_attributes_ok.tf @@ -0,0 +1,14 @@ +resource "outscale_policy" "policy_user01" { + description = "Example of description" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/okht/" + policy_name = "okht-user-policy" +} + +resource "outscale_user" "userInteg" { + user_name = "test_integ_update" + path = "/Integ/" + policy { + policy_orn = outscale_policy.policy_user01.orn + } +} diff --git a/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref new file mode 100644 index 000000000..1bb9cdec0 --- /dev/null +++ b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.ref @@ -0,0 +1,158 @@ +{ + "version": "########", + "terraform_version": "########", + "serial": "########", + "lineage": "########", + "outputs": {}, + "resources": [ + { + "mode": "data", + "type": "outscale_user_groups", + "name": "testgrpData", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "filter": "########", + "id": "##id-0##", + "user_groups": [ + { + "creation_date": "########", + "last_modification_date": "########", + "orn": "########", + "path": "/TestdataUG/", + "user_group_id": "##id-1##", + "user_group_name": "testDataugInteg" + } + ] + }, + "sensitive_attributes": [] + } + ] + }, + { + "mode": "managed", + "type": "outscale_policy", + "name": "policy_userGroup01", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "description": "Example of description", + "document": "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}", + "id": "##id-2##", + "is_linkable": true, + "last_modification_date": "########", + "orn": "########", + "path": "/okht/", + "policy_default_version_id": "##id-3##", + "policy_id": "##id-4##", + "policy_name": "group-policy", + "resources_count": 0 + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "outscale_policy", + "name": "policy_userTest", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "description": "Example of description", + "document": "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}", + "id": "##id-5##", + "is_linkable": true, + "last_modification_date": "########", + "orn": "########", + "path": "/", + "policy_default_version_id": "##id-3##", + "policy_id": "##id-6##", + "policy_name": "user-policy", + "resources_count": 0 + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "outscale_user", + "name": "userTest", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "id": "##id-7##", + "last_modification_date": "########", + "path": "/IntegGroup/", + "policy": [ + { + "creation_date": "########", + "last_modification_date": "########", + "policy_id": "##id-6##", + "policy_name": "user-policy", + "policy_orn": "orn:ows:idauth::339215505907:policy/user-policy" + } + ], + "user_id": "##id-7##", + "user_name": "group_user" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "outscale_policy.policy_userTest" + ] + } + ] + }, + { + "mode": "managed", + "type": "outscale_user_group", + "name": "dataUserGroupInteg", + "provider": "provider[\"registry.terraform.io/outscale/outscale\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "creation_date": "########", + "id": "##id-8##", + "last_modification_date": "########", + "orn": "########", + "path": "/TestdataUG/", + "policy": [ + { + "creation_date": "########", + "last_modification_date": "########", + "policy_id": "##id-4##", + "policy_name": "group-policy", + "policy_orn": "orn:ows:idauth::339215505907:policy/okht/group-policy" + } + ], + "user": [], + "user_group_id": "##id-1##", + "user_group_name": "testDataugInteg" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "outscale_policy.policy_userGroup01" + ] + } + ] + } + ], + "check_results": "########" +} \ No newline at end of file diff --git a/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf new file mode 100644 index 000000000..9797f5b07 --- /dev/null +++ b/tests/qa_provider_oapi/data/user_group/TF-03_user_group_datasources_attributes_ok/step1.user_group_datasources_attributes_ok.tf @@ -0,0 +1,41 @@ +resource "outscale_policy" "policy_userGroup01" { + description = "Example of description" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/okht/" + policy_name = "group-policy" +} + +resource "outscale_policy" "policy_userTest" { + description = "Example of description" + document = "{\"Statement\": [ {\"Effect\": \"Allow\", \"Action\": [\"*\"], \"Resource\": [\"*\"]} ]}" + path = "/" + policy_name = "user-policy" +} + +resource "outscale_user" "userTest" { + user_name = "group_user" + path = "/IntegGroup/" + policy { + policy_orn = outscale_policy.policy_userTest.orn + } +} + +resource "outscale_user_group" "dataUserGroupInteg" { + user_group_name = "testDataugInteg" + path = "/TestdataUG/" + policy { + policy_orn = outscale_policy.policy_userGroup01.orn + } +} + +data "outscale_user_groups" "testgrpData" { + filter { + name = "user_group_ids" + values = [outscale_user_group.dataUserGroupInteg.user_group_id] + } + filter { + name = "path_prefix" + values = ["/TestdataUG/"] + } +} + diff --git a/utils/utils.go b/utils/utils.go index 409888008..8dc31be3a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -10,6 +10,7 @@ import ( "math/rand" "net/http" "os" + "regexp" "strconv" "time" @@ -23,16 +24,19 @@ import ( // PrintToJSON method helper to debug responses const ( - randMin float32 = 1.0 - randMax float32 = 20.0 - MinPort int = 1 - MaxPort int = 65535 - MinIops int = 100 - MaxIops int = 13000 - DefaultIops int32 = 150 - MaxSize int = 14901 - InvalidState string = "InvalidState" - VolumeIOPSError string = ` + randMin float32 = 1.0 + randMax float32 = 20.0 + MinPort int = 1 + MaxPort int = 65535 + MinIops int = 100 + MaxIops int = 13000 + DefaultIops int32 = 150 + MaxSize int = 14901 + LinkedPolicyNotFound string = "5102" + InvalidState string = "InvalidState" + pathRegex string = "^(/[a-zA-Z0-9/_]+/)" + pathError string = "path must begin and end with '/' and contain only alphanumeric characters and/or '/', '_' characters" + VolumeIOPSError string = ` - The "iops" parameter can only be set if "io1" volume type is created. - "Standard" volume types have a default value of 150 iops. - For "gp2" volume types, iops value depend on your volume size. @@ -288,9 +292,9 @@ func I32toa(i int32) string { } func GetRegion() string { - region := fmt.Sprintf("%s", os.Getenv("OUTSCALE_REGION")) + region := os.Getenv("OUTSCALE_REGION") if region == "" { - region = fmt.Sprintf("%s", os.Getenv("OSC_REGION")) + region = os.Getenv("OSC_REGION") } return region } @@ -332,3 +336,11 @@ func GetEnvVariableValue(envVariables []string) string { } return "" } +func CheckPath(path string) error { + reg := regexp.MustCompile(pathRegex) + + if reg.MatchString(path) || path == "/" { + return nil + } + return fmt.Errorf("invalid path:\n %v", pathError) +}