Skip to content

Commit

Permalink
Support: Cross Account iam_service_policy
Browse files Browse the repository at this point in the history
  • Loading branch information
kavya498 committed Mar 10, 2021
1 parent 2bbdc33 commit 9e39484
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 56 deletions.
60 changes: 44 additions & 16 deletions ibm/data_source_ibm_iam_service_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ package ibm
import (
"fmt"

"github.com/IBM-Cloud/bluemix-go/api/iampap/iampapv1"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/IBM-Cloud/bluemix-go/api/iampap/iampapv1"
)

// Data source to find all the policies for a serviceID
Expand All @@ -17,9 +18,16 @@ func dataSourceIBMIAMServicePolicy() *schema.Resource {

Schema: map[string]*schema.Schema{
"iam_service_id": {
Description: "UUID of ServiceID",
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"iam_service_id", "iam_id"},
Description: "UUID of ServiceID",
},
"iam_id": {
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"iam_service_id", "iam_id"},
Description: "IAM ID of ServiceID",
},
"sort": {
Description: "Sort query for policies",
Expand Down Expand Up @@ -88,19 +96,26 @@ func dataSourceIBMIAMServicePolicy() *schema.Resource {
}

func dataSourceIBMIAMServicePolicyRead(d *schema.ResourceData, meta interface{}) error {
iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err
}

serviceIDUUID := d.Get("iam_service_id").(string)
var iamID string
if v, ok := d.GetOk("iam_service_id"); ok && v != nil {

userDetails, err := meta.(ClientSession).BluemixUserDetails()
if err != nil {
return err
serviceIDUUID := v.(string)
iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err
}
serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
if err != nil {
return err
}
iamID = serviceID.IAMID
}
if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID = v.(string)
}

serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
userDetails, err := meta.(ClientSession).BluemixUserDetails()
if err != nil {
return err
}
Expand All @@ -113,7 +128,7 @@ func dataSourceIBMIAMServicePolicyRead(d *schema.ResourceData, meta interface{})
query := iampapv1.SearchParams{
AccountID: userDetails.userAccount,
Type: iampapv1.AccessPolicyType,
IAMID: serviceID.IAMID,
IAMID: iamID,
}

if v, ok := d.GetOk("sort"); ok {
Expand All @@ -133,13 +148,26 @@ func dataSourceIBMIAMServicePolicyRead(d *schema.ResourceData, meta interface{})
}
resources := flattenPolicyResource(policy.Resources)
p := map[string]interface{}{
"id": fmt.Sprintf("%s/%s", serviceIDUUID, policy.ID),
"roles": roles,
"resources": resources,
}
if v, ok := d.GetOk("iam_service_id"); ok && v != nil {
serviceIDUUID := v.(string)
p["id"] = fmt.Sprintf("%s/%s", serviceIDUUID, policy.ID)
} else if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID := v.(string)
p["id"] = fmt.Sprintf("%s/%s", iamID, policy.ID)
}
servicePolicies = append(servicePolicies, p)
}
d.SetId(serviceIDUUID)

if v, ok := d.GetOk("iam_service_id"); ok && v != nil {
serviceIDUUID := v.(string)
d.SetId(serviceIDUUID)
} else if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID := v.(string)
d.SetId(iamID)
}
d.Set("policies", servicePolicies)
return nil
}
100 changes: 65 additions & 35 deletions ibm/resource_ibm_iam_service_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package ibm

import (
"fmt"
"strings"

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

Expand All @@ -24,12 +25,19 @@ func resourceIBMIAMServicePolicy() *schema.Resource {

Schema: map[string]*schema.Schema{
"iam_service_id": {
Type: schema.TypeString,
Required: true,
Description: "UUID of ServiceID",
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"iam_service_id", "iam_id"},
Description: "UUID of ServiceID",
ForceNew: true,
},
"iam_id": {
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"iam_service_id", "iam_id"},
Description: "IAM ID of ServiceID",
ForceNew: true,
},

"roles": {
Type: schema.TypeList,
Required: true,
Expand Down Expand Up @@ -115,11 +123,24 @@ func resourceIBMIAMServicePolicy() *schema.Resource {
}

func resourceIBMIAMServicePolicyCreate(d *schema.ResourceData, meta interface{}) error {
iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err

var iamID string
if v, ok := d.GetOk("iam_service_id"); ok && v != nil {
serviceIDUUID := v.(string)

iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err
}
serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
if err != nil {
return err
}
iamID = serviceID.IAMID
}
if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID = v.(string)
}
serviceIDUUID := d.Get("iam_service_id").(string)

userDetails, err := meta.(ClientSession).BluemixUserDetails()
if err != nil {
Expand All @@ -132,39 +153,37 @@ func resourceIBMIAMServicePolicyCreate(d *schema.ResourceData, meta interface{})
if err != nil {
return err
}

serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
if err != nil {
return err
}

iampapClient, err := meta.(ClientSession).IAMPAPAPI()
if err != nil {
return err
}

policy.Resources[0].SetAccountID(userDetails.userAccount)

policy.Subjects = []iampapv1.Subject{
{
Attributes: []iampapv1.Attribute{
{
Name: "iam_id",
Value: serviceID.IAMID,
Value: iamID,
},
},
},
}

policy.Type = iampapv1.AccessPolicyType

iampapClient, err := meta.(ClientSession).IAMPAPAPI()
if err != nil {
return err
}
servicePolicy, err := iampapClient.V1Policy().Create(policy)

if err != nil {
return fmt.Errorf("Error creating servicePolicy: %s", err)
}

d.SetId(fmt.Sprintf("%s/%s", serviceIDUUID, servicePolicy.ID))
if v, ok := d.GetOk("iam_service_id"); ok && v != nil {
serviceIDUUID := v.(string)
d.SetId(fmt.Sprintf("%s/%s", serviceIDUUID, servicePolicy.ID))
} else if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID := v.(string)
d.SetId(fmt.Sprintf("%s/%s", iamID, servicePolicy.ID))
}

return resourceIBMIAMServicePolicyRead(d, meta)
}
Expand All @@ -187,8 +206,12 @@ func resourceIBMIAMServicePolicyRead(d *schema.ResourceData, meta interface{}) e
if err != nil {
return fmt.Errorf("Error retrieving servicePolicy: %s", err)
}
if strings.HasPrefix(serviceIDUUID, "iam-") {
d.Set("iam_id", serviceIDUUID)
} else {
d.Set("iam_service_id", serviceIDUUID)
}

d.Set("iam_service_id", serviceIDUUID)
roles := make([]string, len(servicePolicy.Roles))
for i, role := range servicePolicy.Roles {
roles[i] = role.Name
Expand All @@ -212,27 +235,34 @@ func resourceIBMIAMServicePolicyUpdate(d *schema.ResourceData, meta interface{})

if d.HasChange("roles") || d.HasChange("resources") || d.HasChange("account_management") {

iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err
}
parts, err := idParts(d.Id())
if err != nil {
return err
}
serviceIDUUID := parts[0]
servicePolicyID := parts[1]

userDetails, err := meta.(ClientSession).BluemixUserDetails()
if err != nil {
return err
var iamID string
if v, ok := d.GetOk("iam_service_id"); ok && v != nil {
serviceIDUUID := v.(string)

iamClient, err := meta.(ClientSession).IAMAPI()
if err != nil {
return err
}
serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
if err != nil {
return err
}
iamID = serviceID.IAMID
}
if v, ok := d.GetOk("iam_id"); ok && v != nil {
iamID = v.(string)
}

serviceID, err := iamClient.ServiceIds().Get(serviceIDUUID)
userDetails, err := meta.(ClientSession).BluemixUserDetails()
if err != nil {
return err
}

var policy iampapv1.Policy

policy, err = generateAccountPolicyV2(d, meta)
Expand All @@ -247,7 +277,7 @@ func resourceIBMIAMServicePolicyUpdate(d *schema.ResourceData, meta interface{})
Attributes: []iampapv1.Attribute{
{
Name: "iam_id",
Value: serviceID.IAMID,
Value: iamID,
},
},
},
Expand Down
5 changes: 3 additions & 2 deletions website/docs/d/iam_service_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ data "ibm_iam_service_policy" "testacc_ds_service_policy" {

The following arguments are supported:

* `iam_service_id` - (Required, string) The UUID of the serviceID.
* `iam_service_id` - (Optional, string) UUID of the serviceID. Exactly one of `iam_service_id`, `iam_id` is required.
* `iam_id` - (Optional, string) IAM ID of the serviceID. Exactly one of `iam_service_id`, `iam_id` is required. Can be used to get cross account service ID Policy.
* `sort` - (Optional, string) Single field sort query for policies.

## Attribute Reference

The following attributes are exported:

* `policies` - A nested block describing IAM Policies assigned to serviceID. Nested `policies` blocks have the following structure:
* `id` - The unique identifier of the IAM service policy.The id is composed of \<iam_service_id\>/\<service_policy_id\>
* `id` - The unique identifier of the IAM service policy.The id is composed of \<iam_service_id\>/\<service_policy_id\> if policy is created using <iam_service_id>. The id is composed of \<iam_id\>/\<service_policy_id\> if policy is created using <iam_id>.
* `roles` - Roles assigned to the policy.
* `resources` - A nested block describing the resources in the policy.
* `service` - Service name of the policy definition.
Expand Down
34 changes: 31 additions & 3 deletions website/docs/r/iam_service_policy.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,40 @@ resource "ibm_iam_service_policy" "policy" {
}
}
```
### Cross account Service policy using iam_id

```hcl
provider "ibm" {
alias = "accA"
ibmcloud_api_key = "Account A Api Key"
}
resource "ibm_iam_service_id" "serviceID" {
provider = ibm.accA
name = "test"
}
provider "ibm" {
alias = "accB"
ibmcloud_api_key = "Account B Api Key"
}
resource "ibm_iam_service_policy" "policy" {
provider = ibm.accB
iam_id = ibm_iam_service_id.serviceID.iam_id
roles = ["Reader"]
resources {
service = "cloud-object-storage"
}
}
```

## Argument Reference

The following arguments are supported:

* `iam_service_id` - (Required, Forces new resource, string) UUID of the serviceID.
* `iam_service_id` - (Optional, Forces new resource, string) UUID of the serviceID. Exactly one of `iam_service_id`, `iam_id` is required.
* `iam_id` - (Optional, Forces new resource, string) IAM ID of the serviceID. Exactly one of `iam_service_id`, `iam_id` is required. Can be used to assign cross account service ID Policy.
* `roles` - (Required, list) comma separated list of roles. Valid roles are Writer, Reader, Manager, Administrator, Operator, Viewer, Editor.
* `resources` - (Optional, list) A nested block describing the resource of this policy.
Nested `resources` blocks have the following structure:
Expand All @@ -167,15 +194,16 @@ Nested `resources` blocks have the following structure:

The following attributes are exported:

* `id` - The unique identifier of the service policy. The id is composed of \<iam_service_id\>/\<service_policy_id\>
* `id` - The unique identifier of the service policy. The id is composed of \<iam_service_id\>/\<service_policy_id\> if policy is created using <iam_service_id>. The id is composed of \<iam_id\>/\<service_policy_id\> if policy is created using <iam_id>.

* `version` - Version of the service policy.

## Import

ibm_iam_service_policy can be imported using serviceID and service policy id, eg
ibm_iam_service_policy can be imported using serviceID and service policy id or iamID and service policy id, eg

```
$ terraform import ibm_iam_service_policy.example ServiceId-d7bec597-4726-451f-8a63-e62e6f19c32c/cea6651a-bc0a-4438-9f8a-a0770bbf3ebb
$ terraform import ibm_iam_service_policy.example iam-ServiceId-d7bec597-4726-451f-8a63-e62e6f19c32c/cea6651a-bc0a-4438-9f8a-a0770bbf3ebb
```

0 comments on commit 9e39484

Please sign in to comment.