Skip to content

Commit

Permalink
feat(Organizations): add organizations delegated administrator (#3940)
Browse files Browse the repository at this point in the history
  • Loading branch information
houpeng80 authored Jan 3, 2024
1 parent d2d0a02 commit 942f188
Show file tree
Hide file tree
Showing 4 changed files with 366 additions and 0 deletions.
46 changes: 46 additions & 0 deletions docs/resources/organizations_delegated_administrator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
subcategory: "Organizations"
---

# huaweicloud_organizations_delegated_administrator

Manages an Organizations delegated administrator resource within HuaweiCloud.

## Example Usage

```hcl
variable "account_id" {}
variable "service_principal" {}
resource "huaweicloud_organizations_delegated_administrator" "test"{
account_id = var.account_id
service_principal = var.service_principal
}
```

## Argument Reference

The following arguments are supported:

* `account_id` - (Required, String, ForceNew) Specifies the unique ID of an account.

Changing this parameter will create a new resource.

* `service_principal` - (Required, String, ForceNew) Specifies the name of the service principal.

Changing this parameter will create a new resource.

## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The resource ID.

## Import

The Organizations delegated administrator can be imported using the `account_id` and `service_principal` separated by
a slash, e.g.

```bash
$ terraform import huaweicloud_organizations_delegated_administrator.test <account_id>/<service_principal>
```
1 change: 1 addition & 0 deletions huaweicloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,7 @@ func Provider() *schema.Provider {
"huaweicloud_organizations_trusted_service": organizations.ResourceTrustedService(),
"huaweicloud_organizations_policy": organizations.ResourcePolicy(),
"huaweicloud_organizations_policy_attach": organizations.ResourcePolicyAttach(),
"huaweicloud_organizations_delegated_administrator": organizations.ResourceDelegatedAdministrator(),

"huaweicloud_dli_queue_v1": dli.ResourceDliQueue(),
"huaweicloud_networking_vip_v2": vpc.ResourceNetworkingVip(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package organizations

import (
"encoding/json"
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/chnsz/golangsdk"
"github.com/chnsz/golangsdk/pagination"

"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
)

func getDelegatedAdministratorResourceFunc(cfg *config.Config, state *terraform.ResourceState) (interface{}, error) {
region := acceptance.HW_REGION_NAME
// getDelegatedAdministrator: Query Organizations delegated administrator
var (
getDelegatedAdministratorHttpUrl = "v1/organizations/delegated-administrators"
getDelegatedAdministratorProduct = "organizations"
)
getDelegatedAdministratorClient, err := cfg.NewServiceClient(getDelegatedAdministratorProduct, region)
if err != nil {
return nil, fmt.Errorf("error creating Organizations client: %s", err)
}

parts := strings.Split(state.Primary.ID, "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid id format, must be <account_id>/<service_principal>")
}
accountID := parts[0]
servicePrincipal := parts[1]

getDelegatedAdministratorPath := getDelegatedAdministratorClient.Endpoint + getDelegatedAdministratorHttpUrl
getDelegatedAdministratorQueryParams := buildGetDelegatedAdministratorQueryParams(servicePrincipal)
getDelegatedAdministratorPath += getDelegatedAdministratorQueryParams

getDelegatedAdministratorResp, err := pagination.ListAllItems(
getDelegatedAdministratorClient,
"marker",
getDelegatedAdministratorPath,
&pagination.QueryOpts{MarkerField: "account_id"})
if err != nil {
return nil, fmt.Errorf("error retrieving Organizations delegated administrator: %s", err)
}

getDelegatedAdministratorRespJson, err := json.Marshal(getDelegatedAdministratorResp)
if err != nil {
return nil, fmt.Errorf("error retrieving Organizations delegated administrator: %s", err)
}
var getDelegatedAdministratorRespBody interface{}
err = json.Unmarshal(getDelegatedAdministratorRespJson, &getDelegatedAdministratorRespBody)
if err != nil {
return nil, fmt.Errorf("error retrieving Organizations delegated administrator: %s", err)
}

delegatedAdministrator := utils.PathSearch(fmt.Sprintf("delegated_administrators|[?account_id=='%s']|[0]",
accountID), getDelegatedAdministratorRespBody, nil)
if delegatedAdministrator == nil {
return nil, golangsdk.ErrDefault404{}
}

return delegatedAdministrator, nil
}

func buildGetDelegatedAdministratorQueryParams(servicePrincipal string) string {
return fmt.Sprintf("?service_principal=%v", servicePrincipal)
}

func TestAccDelegatedAdministrator_basic(t *testing.T) {
var obj interface{}

rName := "huaweicloud_organizations_delegated_administrator.test"

rc := acceptance.InitResourceCheck(
rName,
&obj,
getDelegatedAdministratorResourceFunc,
)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acceptance.TestAccPreCheck(t)
acceptance.TestAccPreCheckMultiAccount(t)
acceptance.TestAccPreCheckOrganizationsOpen(t)
acceptance.TestAccPreCheckOrganizationsAccountName(t)
},
ProviderFactories: acceptance.TestAccProviderFactories,
CheckDestroy: rc.CheckResourceDestroy(),
Steps: []resource.TestStep{
{
Config: testDelegatedAdministrator_basic(acceptance.HW_ORGANIZATIONS_ACCOUNT_NAME),
Check: resource.ComposeTestCheckFunc(
rc.CheckResourceExists(),
resource.TestCheckResourceAttrPair(rName, "account_id",
"data.huaweicloud_organizations_accounts.test", "accounts.0.id"),
resource.TestCheckResourceAttrPair(rName, "service_principal",
"huaweicloud_organizations_trusted_service.test", "service"),
),
},
{
ResourceName: rName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testDelegatedAdministrator_basic(name string) string {
return fmt.Sprintf(`
%[1]s
data "huaweicloud_organizations_accounts" "test" {
name = "%[2]s"
}
resource "huaweicloud_organizations_delegated_administrator" "test" {
account_id = data.huaweicloud_organizations_accounts.test.accounts.0.id
service_principal = huaweicloud_organizations_trusted_service.test.service
}
`, testTrustedService_basic(), name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// ---------------------------------------------------------------
// *** AUTO GENERATED CODE ***
// @Product Organizations
// ---------------------------------------------------------------

package organizations

import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/chnsz/golangsdk"
"github.com/chnsz/golangsdk/pagination"

"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
"github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
)

func ResourceDelegatedAdministrator() *schema.Resource {
return &schema.Resource{
CreateContext: resourceDelegatedAdministratorCreate,
ReadContext: resourceDelegatedAdministratorRead,
DeleteContext: resourceDelegatedAdministratorDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `Specifies the unique ID of an account.`,
},
"service_principal": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `Specifies the name of the service principal.`,
},
},
}
}

func resourceDelegatedAdministratorCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)

// createDelegatedAdministrator: create Organizations delegated administrator
var (
createDelegatedAdministratorHttpUrl = "v1/organizations/delegated-administrators/register"
createDelegatedAdministratorProduct = "organizations"
)
createDelegatedAdministratorClient, err := cfg.NewServiceClient(createDelegatedAdministratorProduct, region)
if err != nil {
return diag.Errorf("error creating Organizations client: %s", err)
}

createDelegatedAdministratorPath := createDelegatedAdministratorClient.Endpoint + createDelegatedAdministratorHttpUrl

createDelegatedAdministratorOpt := golangsdk.RequestOpts{
KeepResponseBody: true,
}

createDelegatedAdministratorOpt.JSONBody = utils.RemoveNil(deleteDelegatedAdministratorBodyParams(d))
_, err = createDelegatedAdministratorClient.Request("POST", createDelegatedAdministratorPath,
&createDelegatedAdministratorOpt)
if err != nil {
return diag.Errorf("error creating Organizations delegated administrator: %s", err)
}

accountID := d.Get("account_id")
servicePrincipal := d.Get("service_principal")

d.SetId(fmt.Sprintf("%s/%s", accountID, servicePrincipal))

return resourceDelegatedAdministratorRead(ctx, d, meta)
}

func resourceDelegatedAdministratorRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)

var mErr *multierror.Error

// getDelegatedAdministrator: Query Organizations delegated administrator
var (
getDelegatedAdministratorHttpUrl = "v1/organizations/delegated-administrators"
getDelegatedAdministratorProduct = "organizations"
)
getDelegatedAdministratorClient, err := cfg.NewServiceClient(getDelegatedAdministratorProduct, region)
if err != nil {
return diag.Errorf("error creating Organizations client: %s", err)
}

parts := strings.Split(d.Id(), "/")
if len(parts) != 2 {
return diag.Errorf("invalid id format, must be <account_id>/<service_principal>")
}
accountID := parts[0]
servicePrincipal := parts[1]

getDelegatedAdministratorPath := getDelegatedAdministratorClient.Endpoint + getDelegatedAdministratorHttpUrl

getDelegatedAdministratorQueryParams := buildGetDelegatedAdministratorQueryParams(servicePrincipal)
getDelegatedAdministratorPath += getDelegatedAdministratorQueryParams

getDelegatedAdministratorResp, err := pagination.ListAllItems(
getDelegatedAdministratorClient,
"marker",
getDelegatedAdministratorPath,
&pagination.QueryOpts{MarkerField: "account_id"})

if err != nil {
return common.CheckDeletedDiag(d, err, "error retrieving Organizations delegated administrator")
}

getDelegatedAdministratorRespJson, err := json.Marshal(getDelegatedAdministratorResp)
if err != nil {
return diag.FromErr(err)
}
var getDelegatedAdministratorRespBody interface{}
err = json.Unmarshal(getDelegatedAdministratorRespJson, &getDelegatedAdministratorRespBody)
if err != nil {
return diag.FromErr(err)
}

delegatedAdministrator := utils.PathSearch(fmt.Sprintf("delegated_administrators|[?account_id=='%s']|[0]",
accountID), getDelegatedAdministratorRespBody, nil)
if delegatedAdministrator == nil {
return common.CheckDeletedDiag(d, golangsdk.ErrDefault404{}, "")
}

mErr = multierror.Append(
mErr,
d.Set("account_id", accountID),
d.Set("service_principal", servicePrincipal),
)

return diag.FromErr(mErr.ErrorOrNil())
}

func buildGetDelegatedAdministratorQueryParams(servicePrincipal string) string {
return fmt.Sprintf("?service_principal=%v", servicePrincipal)
}

func resourceDelegatedAdministratorDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)

// deleteDelegatedAdministrator: Delete Organizations delegated administrator
var (
deleteDelegatedAdministratorHttpUrl = "v1/organizations/delegated-administrators/deregister"
deleteDelegatedAdministratorProduct = "organizations"
)
deleteDelegatedAdministratorClient, err := cfg.NewServiceClient(deleteDelegatedAdministratorProduct, region)
if err != nil {
return diag.Errorf("error creating Organizations client: %s", err)
}

deleteDelegatedAdministratorPath := deleteDelegatedAdministratorClient.Endpoint + deleteDelegatedAdministratorHttpUrl

deleteDelegatedAdministratorOpt := golangsdk.RequestOpts{
KeepResponseBody: true,
}

deleteDelegatedAdministratorOpt.JSONBody = utils.RemoveNil(deleteDelegatedAdministratorBodyParams(d))
_, err = deleteDelegatedAdministratorClient.Request("POST", deleteDelegatedAdministratorPath,
&deleteDelegatedAdministratorOpt)
if err != nil {
return diag.Errorf("error deleting Organizations delegated administrator: %s", err)
}

return nil
}

func deleteDelegatedAdministratorBodyParams(d *schema.ResourceData) map[string]interface{} {
bodyParams := map[string]interface{}{
"account_id": d.Get("account_id"),
"service_principal": d.Get("service_principal"),
}
return bodyParams
}

0 comments on commit 942f188

Please sign in to comment.