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

r/servicecatalog_portfolio_share: New resource(s) #19278

Merged
merged 41 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
253c128
r/servicecatalog_portfolio: Remove unnecessary length limits
YakDriver May 7, 2021
255f8c7
provider: Add new servicecatalog resources
YakDriver May 7, 2021
c17914e
servicecatalog: Add status for orgs/portfolio_share
YakDriver May 7, 2021
4a62855
servicecatalog: Add waiter for orgs/portfolio_share
YakDriver May 7, 2021
7455a84
servicecatalog: Add finder for portfolio share
YakDriver May 7, 2021
1758402
docs/r/servicecatalog_portfolio_share: Initial docs
YakDriver May 7, 2021
5765062
r/servicecatalog_portfolio_share: New resource
YakDriver May 7, 2021
9cee563
tests/r/servicecatalog_portfolio_share: Tests for new resource
YakDriver May 7, 2021
faa71ad
docs/r/servicecatalog_organizations_access: Docs
YakDriver May 7, 2021
fdbfe4a
tests/r/servicecatalog_organizations_acess: Test new resource
YakDriver May 7, 2021
63e8535
r/servicecatalog_organizations_acess: New resource
YakDriver May 7, 2021
cdf8b43
r/servicecatalog_portfolio_share: Add changelog
YakDriver May 7, 2021
0a3598e
tests/validators: Add test for servicecatalog principal
YakDriver May 7, 2021
7fbba43
validators: Add validator for servicecatalog principal
YakDriver May 7, 2021
26e8e38
r/servicecatalog_portfolio_share: Rework schema to simplify
YakDriver May 7, 2021
cdd59c7
tests/r/servicecatalog_portfolio_share: Rework tests for new schema
YakDriver May 7, 2021
9aa9c29
internal/servicecatalog: Rework finder for new schema
YakDriver May 7, 2021
478202d
internal/servicecatalog: Rework status for new schema
YakDriver May 7, 2021
96d4bd4
internal/servicecatalog: Rework waiter for new schema
YakDriver May 7, 2021
14d340f
docs/r/servicecatalog_portfolio_share: Update for schema rework
YakDriver May 7, 2021
92253f3
docs/r/servicecatalog_portfolio_share: Add wait arg
YakDriver May 7, 2021
5d259d7
internal/servicecatalog: Adjust not found errors
YakDriver May 7, 2021
a4ea865
internal/servicecatalog: Adjust not found errors
YakDriver May 7, 2021
ea82747
tests/r/servicecatalog_portfolio_share: Add new account test
YakDriver May 7, 2021
75e28a7
r/servicecatalog_portfolio_share: Adjust for wait option
YakDriver May 7, 2021
547f518
envvar: Add skipping option func
YakDriver May 7, 2021
34ccacb
provider: Skip when prequisite alt acct envvars not set
YakDriver May 7, 2021
c1a29fd
tests/r/servicecatalog_portfolio_share: Add prechecks
YakDriver May 7, 2021
a1bfca5
tests/r/servicecatalog_portfolio_share: Hardcoded fix
YakDriver May 7, 2021
44861f3
envvar: Update skip func docs
YakDriver May 7, 2021
56e278b
provider: Add servicecatalog resource
YakDriver May 7, 2021
cd74e57
tests/r/servicecatalog_portfolio_share: Remove hardcoded partition
YakDriver May 7, 2021
a6166f5
r/servicecatalog_portfolio_share: Move id stuff to internal
YakDriver May 11, 2021
26c06ed
r/servicecat_org_access: Clean up messages, delete
YakDriver May 11, 2021
6d12cff
r/servicecat_portfolio: Enforce field lengths
YakDriver May 11, 2021
ea01f5c
r/servicecat_portfolio_share: Use internal ID helper
YakDriver May 11, 2021
1eb17f2
Add 'testAccOrganizationManagementAccountPreCheck'.
ewbankkit May 13, 2021
40d9bbe
Fix type casting error.
ewbankkit May 13, 2021
0b71dd4
r/servicecat_portfolio_share: Revise org test
YakDriver May 14, 2021
cb8d633
r/servicecat_portfolio_share: Finder contains string
YakDriver May 14, 2021
5635137
r/aws_servicecatalog_portfolio_share: Get tests working for organizat…
ewbankkit May 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/19278.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_servicecatalog_organizations_access
```

```release-note:new-resource
aws_servicecatalog_portfolio_share
```
15 changes: 15 additions & 0 deletions aws/internal/envvar/testing_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,18 @@ func TestSkipIfEmpty(t testing.T, name string, usageMessage string) string {

return value
}

// TestSkipIfAllEmpty verifies that at least one environment variable is non-empty or skips the test.
//
// If at lease one environment variable is non-empty, returns the first name and value.
func TestSkipIfAllEmpty(t testing.T, names []string, usageMessage string) (string, string) {
t.Helper()

name, value, err := RequireOneOf(names, usageMessage)
if err != nil {
t.Skipf("skipping test because %s.", err)
return "", ""
}

return name, value
}
25 changes: 25 additions & 0 deletions aws/internal/service/organizations/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package finder

import (
"github.com/aws/aws-sdk-go/service/organizations"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func Organization(conn *organizations.Organizations) (*organizations.Organization, error) {
input := &organizations.DescribeOrganizationInput{}

output, err := conn.DescribeOrganization(input)

if err != nil {
return nil, err
}

if output == nil || output.Organization == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output.Organization, nil
}
37 changes: 37 additions & 0 deletions aws/internal/service/servicecatalog/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package finder

import (
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/servicecatalog"
)

func PortfolioShare(conn *servicecatalog.ServiceCatalog, portfolioID, shareType, principalID string) (*servicecatalog.PortfolioShareDetail, error) {
input := &servicecatalog.DescribePortfolioSharesInput{
PortfolioId: aws.String(portfolioID),
Type: aws.String(shareType),
}
var result *servicecatalog.PortfolioShareDetail

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

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

if strings.Contains(principalID, aws.StringValue(deet.PrincipalId)) {
result = deet
return false
}
}

return !lastPage
})

return result, err
}
20 changes: 20 additions & 0 deletions aws/internal/service/servicecatalog/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package servicecatalog

import (
"fmt"
"strings"
)

func PortfolioShareParseResourceID(id string) (string, string, string, error) {
parts := strings.SplitN(id, ":", 3)

if len(parts) != 3 || parts[0] == "" || parts[1] == "" || parts[2] == "" {
return "", "", "", fmt.Errorf("unexpected format of ID (%s), expected portfolioID:type:principalID", id)
}

return parts[0], parts[1], parts[2], nil
}

func PortfolioShareCreateResourceID(portfolioID, shareType, principalID string) string {
return strings.Join([]string{portfolioID, shareType, principalID}, ":")
}
72 changes: 72 additions & 0 deletions aws/internal/service/servicecatalog/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog/finder"
)

func ProductStatus(conn *servicecatalog.ServiceCatalog, acceptLanguage, productID string) resource.StateRefreshFunc {
Expand Down Expand Up @@ -68,3 +69,74 @@ func TagOptionStatus(conn *servicecatalog.ServiceCatalog, id string) resource.St
return output.TagOptionDetail, servicecatalog.StatusAvailable, err
}
}

func PortfolioShareStatusWithToken(conn *servicecatalog.ServiceCatalog, token string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &servicecatalog.DescribePortfolioShareStatusInput{
PortfolioShareToken: aws.String(token),
}
output, err := conn.DescribePortfolioShareStatus(input)

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

if err != nil {
return nil, servicecatalog.ShareStatusError, fmt.Errorf("error describing portfolio share status: %w", err)
}

if output == nil {
return nil, StatusUnavailable, fmt.Errorf("error describing portfolio share status: empty response")
}

return output, aws.StringValue(output.Status), err
}
}

func PortfolioShareStatus(conn *servicecatalog.ServiceCatalog, portfolioID, shareType, principalID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.PortfolioShare(conn, portfolioID, shareType, principalID)

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

if err != nil {
return nil, servicecatalog.ShareStatusError, fmt.Errorf("error finding portfolio share: %w", err)
}

if output == nil {
return nil, StatusNotFound, &resource.NotFoundError{
Message: fmt.Sprintf("error finding portfolio share (%s:%s:%s): empty response", portfolioID, shareType, principalID),
}
}

if !aws.BoolValue(output.Accepted) {
return output, servicecatalog.ShareStatusInProgress, err
}

return output, servicecatalog.ShareStatusCompleted, err
}
}

func OrganizationsAccessStatus(conn *servicecatalog.ServiceCatalog) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &servicecatalog.GetAWSOrganizationsAccessStatusInput{}

output, err := conn.GetAWSOrganizationsAccessStatus(input)

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

if err != nil {
return nil, OrganizationAccessStatusError, fmt.Errorf("error getting Organizations Access: %w", err)
}

if output == nil {
return nil, StatusUnavailable, fmt.Errorf("error getting Organizations Access: empty response")
}

return output, aws.StringValue(output.AccessStatus), err
}
}
111 changes: 110 additions & 1 deletion aws/internal/service/servicecatalog/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package waiter
import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

const (
Expand All @@ -15,11 +17,17 @@ const (
TagOptionReadyTimeout = 3 * time.Minute
TagOptionDeleteTimeout = 3 * time.Minute

PortfolioShareCreateTimeout = 3 * time.Minute

OrganizationsAccessStableTimeout = 3 * time.Minute

StatusNotFound = "NOT_FOUND"
StatusUnavailable = "UNAVAILABLE"

// AWS documentation is wrong, says that status will be "AVAILABLE" but it is actually "CREATED"
ProductStatusCreated = "CREATED"

OrganizationAccessStatusError = "ERROR"
)

func ProductReady(conn *servicecatalog.ServiceCatalog, acceptLanguage, productID string) (*servicecatalog.DescribeProductAsAdminOutput, error) {
Expand All @@ -44,7 +52,7 @@ func ProductDeleted(conn *servicecatalog.ServiceCatalog, acceptLanguage, product
Pending: []string{servicecatalog.StatusCreating, servicecatalog.StatusAvailable, ProductStatusCreated, StatusUnavailable},
Target: []string{StatusNotFound},
Refresh: ProductStatus(conn, acceptLanguage, productID),
Timeout: ProductReadyTimeout,
Timeout: ProductDeleteTimeout,
}

_, err := stateConf.WaitForState()
Expand Down Expand Up @@ -89,3 +97,104 @@ func TagOptionDeleted(conn *servicecatalog.ServiceCatalog, id string) error {

return err
}

func PortfolioShareReady(conn *servicecatalog.ServiceCatalog, portfolioID, shareType, principalID string, acceptRequired bool) (*servicecatalog.PortfolioShareDetail, error) {
targets := []string{servicecatalog.ShareStatusCompleted}

if !acceptRequired {
targets = append(targets, servicecatalog.ShareStatusInProgress)
}

stateConf := &resource.StateChangeConf{
Pending: []string{servicecatalog.ShareStatusNotStarted, servicecatalog.ShareStatusInProgress, StatusNotFound, StatusUnavailable},
Target: targets,
Refresh: PortfolioShareStatus(conn, portfolioID, shareType, principalID),
Timeout: PortfolioShareCreateTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}

func PortfolioShareCreatedWithToken(conn *servicecatalog.ServiceCatalog, token string, acceptRequired bool) (*servicecatalog.DescribePortfolioShareStatusOutput, error) {
targets := []string{servicecatalog.ShareStatusCompleted}

if !acceptRequired {
targets = append(targets, servicecatalog.ShareStatusInProgress)
}

stateConf := &resource.StateChangeConf{
Pending: []string{servicecatalog.ShareStatusNotStarted, servicecatalog.ShareStatusInProgress, StatusNotFound, StatusUnavailable},
Target: targets,
Refresh: PortfolioShareStatusWithToken(conn, token),
Timeout: PortfolioShareCreateTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}

func PortfolioShareDeleted(conn *servicecatalog.ServiceCatalog, portfolioID, shareType, principalID string) (*servicecatalog.PortfolioShareDetail, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{servicecatalog.ShareStatusNotStarted, servicecatalog.ShareStatusInProgress, servicecatalog.ShareStatusCompleted, StatusUnavailable},
Target: []string{StatusNotFound},
Refresh: PortfolioShareStatus(conn, portfolioID, shareType, principalID),
Timeout: PortfolioShareCreateTimeout,
}

outputRaw, err := stateConf.WaitForState()

if tfresource.NotFound(err) {
return nil, nil
}

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

return nil, err
}

func PortfolioShareDeletedWithToken(conn *servicecatalog.ServiceCatalog, token string) (*servicecatalog.DescribePortfolioShareStatusOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{servicecatalog.ShareStatusNotStarted, servicecatalog.ShareStatusInProgress, StatusNotFound, StatusUnavailable},
Target: []string{servicecatalog.ShareStatusCompleted},
Refresh: PortfolioShareStatusWithToken(conn, token),
Timeout: PortfolioShareCreateTimeout,
}

outputRaw, err := stateConf.WaitForState()

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

return nil, err
}

func OrganizationsAccessStable(conn *servicecatalog.ServiceCatalog) (string, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{servicecatalog.AccessStatusUnderChange, StatusNotFound, StatusUnavailable},
Target: []string{servicecatalog.AccessStatusEnabled, servicecatalog.AccessStatusDisabled},
Refresh: OrganizationsAccessStatus(conn),
Timeout: OrganizationsAccessStableTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*servicecatalog.GetAWSOrganizationsAccessStatusOutput); ok {
return aws.StringValue(output.AccessStatus), err
}

return "", err
}
25 changes: 25 additions & 0 deletions aws/internal/service/sts/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package finder

import (
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func CallerIdentity(conn *sts.STS) (*sts.GetCallerIdentityOutput, error) {
input := &sts.GetCallerIdentityInput{}

output, err := conn.GetCallerIdentity(input)

if err != nil {
return nil, err
}

if output == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output, nil
}
2 changes: 2 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,9 @@ func Provider() *schema.Provider {
"aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(),
"aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(),
"aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(),
"aws_servicecatalog_organizations_access": resourceAwsServiceCatalogOrganizationsAccess(),
"aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(),
"aws_servicecatalog_portfolio_share": resourceAwsServiceCatalogPortfolioShare(),
"aws_servicecatalog_product": resourceAwsServiceCatalogProduct(),
"aws_servicecatalog_tag_option": resourceAwsServiceCatalogTagOption(),
"aws_service_discovery_http_namespace": resourceAwsServiceDiscoveryHttpNamespace(),
Expand Down
Loading