From 19f23e3c75a31a90710814d981c7c82900d00973 Mon Sep 17 00:00:00 2001 From: Thiery Ouattara Date: Fri, 6 Sep 2024 11:19:56 +0000 Subject: [PATCH] Add user and user_group resources --- outscale/data_source_outscale_user_groups.go | 121 +++++++ outscale/data_source_outscale_users.go | 76 +++++ outscale/provider.go | 4 + outscale/resource_outscale_user.go | 160 +++++++++ outscale/resource_outscale_user_group.go | 332 +++++++++++++++++++ 5 files changed, 693 insertions(+) create mode 100644 outscale/data_source_outscale_user_groups.go create mode 100644 outscale/data_source_outscale_users.go create mode 100644 outscale/resource_outscale_user.go create mode 100644 outscale/resource_outscale_user_group.go diff --git a/outscale/data_source_outscale_user_groups.go b/outscale/data_source_outscale_user_groups.go new file mode 100644 index 000000000..97526b650 --- /dev/null +++ b/outscale/data_source_outscale_user_groups.go @@ -0,0 +1,121 @@ +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_users.go b/outscale/data_source_outscale_users.go new file mode 100644 index 000000000..cece2454a --- /dev/null +++ b/outscale/data_source_outscale_users.go @@ -0,0 +1,76 @@ +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{ + "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, + }, + }, + }, + }, + }, + } +} + +func DataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadUsersRequest() + 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() + usersToSet[i] = user + } + return d.Set("users", usersToSet) +} diff --git a/outscale/provider.go b/outscale/provider.go index 25ed9a4a0..4cd7f763b 100644 --- a/outscale/provider.go +++ b/outscale/provider.go @@ -115,6 +115,8 @@ 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": ResourceOutscaleUserGroup(), }, DataSourcesMap: map[string]*schema.Resource{ "outscale_vm": DataSourceOutscaleVM(), @@ -192,6 +194,8 @@ func Provider() *schema.Provider { "outscale_public_catalog": DataSourceOutscalePublicCatalog(), "outscale_account": DataSourceAccount(), "outscale_accounts": DataSourceAccounts(), + "outscale_users": DataSourceUsers(), + "outscale_user_groups": DataSourceUserGroups(), }, ConfigureFunc: providerConfigureClient, diff --git a/outscale/resource_outscale_user.go b/outscale/resource_outscale_user.go new file mode 100644 index 000000000..479165510 --- /dev/null +++ b/outscale/resource_outscale_user.go @@ -0,0 +1,160 @@ +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 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, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func ResourceOutscaleUserCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + + req := oscgo.NewCreateUserRequest(d.Get("user_name").(string)) + if path := d.Get("path").(string); path != "" { + req.Path = &path + } + + var resp oscgo.CreateUserResponse + err := resource.Retry(5*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(resource.UniqueId()) + if err := d.Set("user_id", *resp.GetUser().UserId); err != nil { + return err + } + + return ResourceOutscaleUserRead(d, meta) +} + +func ResourceOutscaleUserRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadUsersRequest() + + 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() + if len(users) == 0 { + d.SetId("") + return nil + } + for _, user := range users { + if user.GetUserId() == d.Get("user_id") { + + if err := d.Set("user_name", user.GetUserName()); err != nil { + return err + } + if err := d.Set("user_id", user.GetUserId()); err != nil { + return err + } + if err := d.Set("path", user.GetPath()); err != nil { + return err + } + break + } + } + + return nil +} + +func ResourceOutscaleUserUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.UpdateUserRequest{} + + oldN, newN := d.GetChange("user_name") + if oldName := oldN.(string); oldName != "" { + req.SetUserName(oldName) + } + if newName := newN.(string); newName != "" && oldN.(string) != newN.(string) { + req.SetNewUserName(newName) + } + + if d.HasChange("path") { + path := d.Get("path").(string) + req.NewPath = &path + } + + 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 + + req := oscgo.DeleteUserRequest{ + UserName: d.Get("user_name").(string), + } + + var err error + 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 Access Key %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..7ebb5791d --- /dev/null +++ b/outscale/resource_outscale_user_group.go @@ -0,0 +1,332 @@ +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 ResourceOutscaleUserGroup() *schema.Resource { + return &schema.Resource{ + Create: ResourceOutscaleUserGroupCreate, + Read: ResourceOutscaleUserGroupRead, + Update: ResourceOutscaleUserGroupUpdate, + Delete: ResourceOutscaleUserGroupDelete, + 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, + }, + "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, + }, + "users": { + 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 ResourceOutscaleUserGroupCreate(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 groupPath != "" { + req.Path = &groupPath + } + var resp oscgo.CreateUserGroupResponse + err := resource.Retry(5*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()) + // Remove d.Set when read user_group return user_group_id + if err := d.Set("user_group_id", resp.GetUserGroup().UserGroupId); err != nil { + return err + } + if usersToAdd, ok := d.GetOk("users"); ok { + reqUserAdd := oscgo.AddUserToUserGroupRequest{} + reqUserAdd.SetUserGroupName(d.Get("user_group_name").(string)) + if path := d.Get("path").(string); path != "" { + reqUserAdd.UserGroupPath = &path + } + + 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 != "" { + reqUserAdd.SetUserPath(path) + } + if groupPath != "" { + reqUserAdd.SetUserGroupPath(groupPath) + } + err := resource.Retry(5*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 + } + + } + + } + return ResourceOutscaleUserGroupRead(d, meta) +} + +func ResourceOutscaleUserGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.NewReadUserGroupRequest(d.Get("user_group_name").(string)) + if path := d.Get("path").(string); path != "" { + req.Path = &path + } + + var resp oscgo.ReadUserGroupResponse + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + rp, httpResp, err := conn.UserGroupApi.ReadUserGroup(context.Background()).ReadUserGroupRequest(*req).Execute() + if err != nil { + return utils.CheckThrottling(httpResp, err) + } + resp = rp + return nil + }) + + if err != nil { + return err + } + + if _, ok := resp.GetUserGroupOk(); !ok { + d.SetId("") + return nil + } + userGroup := resp.GetUserGroup() + if err := d.Set("user_group_name", userGroup.GetName()); err != nil { + return err + } + /* Remove comment when read user_group return user_group_id + if err := d.Set("user_group_id", userGroup.GetUserGroupId()); 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 + } + usrs := resp.GetUsers() + users := make([]map[string]interface{}, len(usrs)) + if len(usrs) > 0 { + usrs := resp.GetUsers() + for i, v := range usrs { + 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("users", users); err != nil { + return err + } + return nil +} + +func ResourceOutscaleUserGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + req := oscgo.UpdateUserGroupRequest{} + isUpdateGroup := false + + oldN, newN := d.GetChange("user_group_name") + if oldName := oldN.(string); oldName != "" { + req.SetUserGroupName(oldName) + } + if newName := newN.(string); newName != "" && oldN.(string) != newN.(string) { + req.SetNewUserGroupName(newName) + isUpdateGroup = true + } + + oldP, newP := d.GetChange("path") + if oldPath := oldP.(string); oldPath != "" { + req.SetPath(oldPath) + } + if newPath := newP.(string); newPath != "" && oldP.(string) != newP.(string) { + req.SetNewPath(newPath) + isUpdateGroup = true + } + if d.HasChange("users") { + oldUsers, newUsers := d.GetChange("users") + 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)) + + for _, v := range toRemove.List() { + user := v.(map[string]interface{}) + if userName := user["user_name"].(string); userName != "" { + rmUserReq.SetUserName(userName) + } + if path := user["path"].(string); path != "" { + rmUserReq.SetUserGroupPath(path) + } + err := resource.Retry(5*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)) + + for _, v := range toCreate.List() { + user := v.(map[string]interface{}) + if userName := user["user_name"].(string); userName != "" { + addUserReq.SetUserName(userName) + } + if path := user["path"].(string); path != "" { + addUserReq.SetUserGroupPath(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 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 ResourceOutscaleUserGroupRead(d, meta) +} + +func ResourceOutscaleUserGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*OutscaleClient).OSCAPI + forceDeletion := true + req := oscgo.DeleteUserGroupRequest{ + UserGroupName: d.Get("user_group_name").(string), + Force: &forceDeletion, + } + if path := d.Get("path").(string); path != "" { + req.Path = &path + } + + var err error + err = resource.Retry(5*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 +}