Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

d/servicecatalog_portfolio_constraints: New data source #19813

Merged
merged 12 commits into from
Jun 30, 2021
3 changes: 3 additions & 0 deletions .changelog/19813.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-notes:new-data-source
aws_servicecatalog_portfolio_constraints
```
8 changes: 7 additions & 1 deletion aws/data_source_aws_servicecatalog_constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ func dataSourceAwsServiceCatalogConstraintRead(d *schema.ResourceData, meta inte
return fmt.Errorf("error getting Service Catalog Constraint: empty response")
}

d.Set("accept_language", d.Get("accept_language").(string))
acceptLanguage := d.Get("accept_language").(string)

if acceptLanguage == "" {
acceptLanguage = tfservicecatalog.AcceptLanguageEnglish
}

d.Set("accept_language", acceptLanguage)

d.Set("parameters", output.ConstraintParameters)
d.Set("status", output.Status)
Expand Down
151 changes: 151 additions & 0 deletions aws/data_source_aws_servicecatalog_portfolio_constraints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
tfservicecatalog "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog/waiter"
)

func dataSourceAwsServiceCatalogPortfolioConstraints() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsServiceCatalogPortfolioConstraintsRead,

Schema: map[string]*schema.Schema{
"accept_language": {
Type: schema.TypeString,
Optional: true,
Default: tfservicecatalog.AcceptLanguageEnglish,
ValidateFunc: validation.StringInSlice(tfservicecatalog.AcceptLanguage_Values(), false),
},
"details": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"constraint_id": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"owner": {
Type: schema.TypeString,
Computed: true,
},
"portfolio_id": {
Type: schema.TypeString,
Computed: true,
},
"product_id": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"portfolio_id": {
Type: schema.TypeString,
Required: true,
},
"product_id": {
Type: schema.TypeString,
Optional: true,
},
},
}
}

func dataSourceAwsServiceCatalogPortfolioConstraintsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).scconn

output, err := waiter.PortfolioConstraintsReady(conn, d.Get("accept_language").(string), d.Get("portfolio_id").(string), d.Get("product_id").(string))

if err != nil {
return fmt.Errorf("error describing Service Catalog Portfolio Constraints: %w", err)
}

if len(output) == 0 {
return fmt.Errorf("error getting Service Catalog Portfolio Constraints: no results, change your input")
}

acceptLanguage := d.Get("accept_language").(string)

if acceptLanguage == "" {
acceptLanguage = tfservicecatalog.AcceptLanguageEnglish
}

d.Set("accept_language", acceptLanguage)
d.Set("portfolio_id", d.Get("portfolio_id").(string))
d.Set("product_id", d.Get("product_id").(string))

if err := d.Set("details", flattenServiceCatalogConstraintDetails(output)); err != nil {
return fmt.Errorf("error setting details: %w", err)
}

d.SetId(tfservicecatalog.PortfolioConstraintsID(d.Get("accept_language").(string), d.Get("portfolio_id").(string), d.Get("product_id").(string)))

return nil
}

func flattenServiceCatalogConstraintDetail(apiObject *servicecatalog.ConstraintDetail) map[string]interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if v := apiObject.ConstraintId; v != nil {
tfMap["constraint_id"] = aws.StringValue(v)
}

if v := apiObject.Description; v != nil {
tfMap["description"] = aws.StringValue(v)
}

if v := apiObject.Owner; v != nil {
tfMap["owner"] = aws.StringValue(v)
}

if v := apiObject.PortfolioId; v != nil {
tfMap["portfolio_id"] = aws.StringValue(v)
}

if v := apiObject.ProductId; v != nil {
tfMap["product_id"] = aws.StringValue(v)
}

if v := apiObject.Type; v != nil {
tfMap["type"] = aws.StringValue(v)
}

return tfMap
}

func flattenServiceCatalogConstraintDetails(apiObjects []*servicecatalog.ConstraintDetail) []interface{} {
if len(apiObjects) == 0 {
return nil
}

var tfList []interface{}

for _, apiObject := range apiObjects {
if apiObject == nil {
continue
}

tfList = append(tfList, flattenServiceCatalogConstraintDetail(apiObject))
}

return tfList
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package aws

import (
"testing"

"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAWSServiceCatalogPortfolioConstraintDataSource_basic(t *testing.T) {
resourceName := "aws_servicecatalog_constraint.test"
dataSourceName := "data.aws_servicecatalog_portfolio_constraints.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, servicecatalog.EndpointsID),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAWSServiceCatalogPortfolioConstraintDataSourceConfig_basic(rName, rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "accept_language", resourceName, "accept_language"),
resource.TestCheckResourceAttr(dataSourceName, "details.#", "1"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.constraint_id", resourceName, "id"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.description", resourceName, "description"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.owner", resourceName, "owner"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.portfolio_id", resourceName, "portfolio_id"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.product_id", resourceName, "product_id"),
resource.TestCheckResourceAttrPair(dataSourceName, "details.0.type", resourceName, "type"),
resource.TestCheckResourceAttrPair(dataSourceName, "portfolio_id", resourceName, "portfolio_id"),
),
},
},
})
}

func testAccAWSServiceCatalogPortfolioConstraintDataSourceConfig_basic(rName, description string) string {
return composeConfig(testAccAWSServiceCatalogConstraintConfig_basic(rName, description), `
data "aws_servicecatalog_portfolio_constraints" "test" {
portfolio_id = aws_servicecatalog_constraint.test.portfolio_id
}
`)
}
4 changes: 4 additions & 0 deletions aws/internal/service/servicecatalog/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ func PrincipalPortfolioAssociationParseID(id string) (string, string, string, er
func PrincipalPortfolioAssociationID(acceptLanguage, principalARN, portfolioID string) string {
return strings.Join([]string{acceptLanguage, principalARN, portfolioID}, ",")
}

func PortfolioConstraintsID(acceptLanguage, portfolioID, productID string) string {
return strings.Join([]string{acceptLanguage, portfolioID, productID}, ":")
}
44 changes: 44 additions & 0 deletions aws/internal/service/servicecatalog/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,47 @@ func RecordStatus(conn *servicecatalog.ServiceCatalog, acceptLanguage, id string
return output, aws.StringValue(output.RecordDetail.Status), err
}
}

func PortfolioConstraintsStatus(conn *servicecatalog.ServiceCatalog, acceptLanguage, portfolioID, productID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &servicecatalog.ListConstraintsForPortfolioInput{
PortfolioId: aws.String(portfolioID),
}

if acceptLanguage != "" {
input.AcceptLanguage = aws.String(acceptLanguage)
}

if productID != "" {
input.ProductId = aws.String(productID)
}

var output []*servicecatalog.ConstraintDetail

err := conn.ListConstraintsForPortfolioPages(input, func(page *servicecatalog.ListConstraintsForPortfolioOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, deet := range page.ConstraintDetails {
if deet == nil {
continue
}

output = append(output, deet)
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) {
return nil, StatusNotFound, nil
}

if err != nil {
return nil, servicecatalog.StatusFailed, err
}

return output, servicecatalog.StatusAvailable, err
}
}
19 changes: 19 additions & 0 deletions aws/internal/service/servicecatalog/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const (

RecordReadyTimeout = 3 * time.Minute

PortfolioConstraintsReadyTimeout = 3 * time.Minute

MinTimeout = 2 * time.Second
NotFoundChecks = 5
ContinuousTargetOccurrence = 2
Expand Down Expand Up @@ -540,3 +542,20 @@ func RecordReady(conn *servicecatalog.ServiceCatalog, acceptLanguage, id string)

return nil, err
}

func PortfolioConstraintsReady(conn *servicecatalog.ServiceCatalog, acceptLanguage, portfolioID, productID string) ([]*servicecatalog.ConstraintDetail, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{StatusNotFound},
Target: []string{servicecatalog.StatusAvailable},
Refresh: PortfolioConstraintsStatus(conn, acceptLanguage, portfolioID, productID),
Timeout: PortfolioConstraintsReadyTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.([]*servicecatalog.ConstraintDetail); ok {
return output, err
}

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ func Provider() *schema.Provider {
"aws_secretsmanager_secret_version": dataSourceAwsSecretsManagerSecretVersion(),
"aws_servicecatalog_constraint": dataSourceAwsServiceCatalogConstraint(),
"aws_servicecatalog_launch_paths": dataSourceAwsServiceCatalogLaunchPaths(),
"aws_servicecatalog_portfolio_constraints": dataSourceAwsServiceCatalogPortfolioConstraints(),
"aws_servicecatalog_portfolio": dataSourceAwsServiceCatalogPortfolio(),
"aws_servicecatalog_product": dataSourceAwsServiceCatalogProduct(),
"aws_servicequotas_service": dataSourceAwsServiceQuotasService(),
Expand Down
2 changes: 1 addition & 1 deletion aws/resource_aws_servicecatalog_constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func resourceAwsServiceCatalogConstraintRead(d *schema.ResourceData, meta interf
return fmt.Errorf("error describing Service Catalog Constraint (%s): %w", d.Id(), err)
}

if output == nil {
if output == nil || output.ConstraintDetail == nil {
return fmt.Errorf("error getting Service Catalog Constraint (%s): empty response", d.Id())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
subcategory: "Service Catalog"
layout: "aws"
page_title: "AWS: aws_servicecatalog_portfolio_constraints"
description: |-
Provides information on Service Catalog Portfolio Constraints
---

# Data source: aws_servicecatalog_portfolio_constraints

Provides information on Service Catalog Portfolio Constraints.

## Example Usage

### Basic Usage

```terraform
data "aws_servicecatalog_portfolio_constraints" "example" {
portfolio_id = "port-3lli3b3an"
}
```

## Argument Reference

The following arguments are required:

* `portfolio_id` - (Required) Portfolio identifier.

The following arguments are optional:

* `accept_language` - (Optional) Language code. Valid values: `en` (English), `jp` (Japanese), `zh` (Chinese). Default value is `en`.
* `product_id` - (Optional) Product identifier.

## Attributes Reference

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

* `details` - List of information about the constraints. See details below.

### details

* `constraint_id` - Identifier of the constraint.
* `description` - Description of the constraint.
* `portfolio_id` - Identifier of the portfolio the product resides in. The constraint applies only to the instance of the product that lives within this portfolio.
* `product_id` - Identifier of the product the constraint applies to. A constraint applies to a specific instance of a product within a certain portfolio.
* `type` - Type of constraint. Valid values are `LAUNCH`, `NOTIFICATION`, `STACKSET`, and `TEMPLATE`.