diff --git a/aws/config.go b/aws/config.go index fa00b95a3321..d357fb406356 100644 --- a/aws/config.go +++ b/aws/config.go @@ -68,6 +68,7 @@ import ( "github.com/aws/aws-sdk-go/service/mediastore" "github.com/aws/aws-sdk-go/service/mq" "github.com/aws/aws-sdk-go/service/opsworks" + "github.com/aws/aws-sdk-go/service/organizations" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/route53" @@ -194,6 +195,7 @@ type AWSClient struct { lightsailconn *lightsail.Lightsail mqconn *mq.MQ opsworksconn *opsworks.OpsWorks + organizationsconn *organizations.Organizations glacierconn *glacier.Glacier guarddutyconn *guardduty.GuardDuty codebuildconn *codebuild.CodeBuild @@ -444,6 +446,7 @@ func (c *Config) Client() (interface{}, error) { client.lightsailconn = lightsail.New(sess) client.mqconn = mq.New(sess) client.opsworksconn = opsworks.New(sess) + client.organizationsconn = organizations.New(sess) client.r53conn = route53.New(r53Sess) client.rdsconn = rds.New(awsRdsSess) client.redshiftconn = redshift.New(sess) diff --git a/aws/import_aws_organizations_organization_test.go b/aws/import_aws_organizations_organization_test.go new file mode 100644 index 000000000000..692118f224c0 --- /dev/null +++ b/aws/import_aws_organizations_organization_test.go @@ -0,0 +1,28 @@ +package aws + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func testAccAwsOrganizationsOrganization_importBasic(t *testing.T) { + resourceName := "aws_organizations_organization.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOrganizationsOrganizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsOrganizationsOrganizationConfig, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/aws/provider.go b/aws/provider.go index 81bd9ffc8775..3f328c6761b9 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -455,6 +455,7 @@ func Provider() terraform.ResourceProvider { "aws_opsworks_user_profile": resourceAwsOpsworksUserProfile(), "aws_opsworks_permission": resourceAwsOpsworksPermission(), "aws_opsworks_rds_db_instance": resourceAwsOpsworksRdsDbInstance(), + "aws_organizations_organization": resourceAwsOrganizationsOrganization(), "aws_placement_group": resourceAwsPlacementGroup(), "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), "aws_rds_cluster": resourceAwsRDSCluster(), diff --git a/aws/resource_aws_organizations_organization.go b/aws/resource_aws_organizations_organization.go new file mode 100644 index 000000000000..c05c46d9f9ab --- /dev/null +++ b/aws/resource_aws_organizations_organization.go @@ -0,0 +1,102 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/organizations" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsOrganizationsOrganization() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsOrganizationsOrganizationCreate, + Read: resourceAwsOrganizationsOrganizationRead, + Delete: resourceAwsOrganizationsOrganizationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "master_account_arn": { + Type: schema.TypeString, + Computed: true, + }, + "master_account_email": { + Type: schema.TypeString, + Computed: true, + }, + "master_account_id": { + Type: schema.TypeString, + Computed: true, + }, + "feature_set": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: organizations.OrganizationFeatureSetAll, + ValidateFunc: validation.StringInSlice([]string{organizations.OrganizationFeatureSetAll, organizations.OrganizationFeatureSetConsolidatedBilling}, true), + }, + }, + } +} + +func resourceAwsOrganizationsOrganizationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).organizationsconn + + createOpts := &organizations.CreateOrganizationInput{ + FeatureSet: aws.String(d.Get("feature_set").(string)), + } + log.Printf("[DEBUG] Creating Organization: %#v", createOpts) + + resp, err := conn.CreateOrganization(createOpts) + if err != nil { + return fmt.Errorf("Error creating organization: %s", err) + } + + org := resp.Organization + d.SetId(*org.Id) + + return resourceAwsOrganizationsOrganizationRead(d, meta) +} + +func resourceAwsOrganizationsOrganizationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).organizationsconn + + log.Printf("[INFO] Reading Organization: %s", d.Id()) + org, err := conn.DescribeOrganization(&organizations.DescribeOrganizationInput{}) + if err != nil { + if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { + log.Printf("[WARN] Organization does not exist, removing from state: %s", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("arn", org.Organization.Arn) + d.Set("feature_set", org.Organization.FeatureSet) + d.Set("master_account_arn", org.Organization.MasterAccountArn) + d.Set("master_account_email", org.Organization.MasterAccountEmail) + d.Set("master_account_id", org.Organization.MasterAccountId) + return nil +} + +func resourceAwsOrganizationsOrganizationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).organizationsconn + + log.Printf("[INFO] Deleting Organization: %s", d.Id()) + + _, err := conn.DeleteOrganization(&organizations.DeleteOrganizationInput{}) + if err != nil { + return fmt.Errorf("Error deleting Organization: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_organizations_organization_test.go b/aws/resource_aws_organizations_organization_test.go new file mode 100644 index 000000000000..dc951045e366 --- /dev/null +++ b/aws/resource_aws_organizations_organization_test.go @@ -0,0 +1,121 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/organizations" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testAccAwsOrganizationsOrganization_basic(t *testing.T) { + var organization organizations.Organization + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOrganizationsOrganizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsOrganizationsOrganizationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsOrganizationsOrganizationExists("aws_organizations_organization.test", &organization), + resource.TestCheckResourceAttr("aws_organizations_organization.test", "feature_set", organizations.OrganizationFeatureSetAll), + resource.TestCheckResourceAttrSet("aws_organizations_organization.test", "arn"), + resource.TestCheckResourceAttrSet("aws_organizations_organization.test", "master_account_arn"), + resource.TestCheckResourceAttrSet("aws_organizations_organization.test", "master_account_email"), + resource.TestCheckResourceAttrSet("aws_organizations_organization.test", "feature_set"), + ), + }, + }, + }) +} + +func testAccAwsOrganizationsOrganization_consolidatedBilling(t *testing.T) { + var organization organizations.Organization + + feature_set := organizations.OrganizationFeatureSetConsolidatedBilling + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOrganizationsOrganizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsOrganizationsOrganizationConfigConsolidatedBilling(feature_set), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsOrganizationsOrganizationExists("aws_organizations_organization.test", &organization), + resource.TestCheckResourceAttr("aws_organizations_organization.test", "feature_set", feature_set), + ), + }, + }, + }) +} + +func testAccCheckAwsOrganizationsOrganizationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).organizationsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_organizations_organization" { + continue + } + + params := &organizations.DescribeOrganizationInput{} + + resp, err := conn.DescribeOrganization(params) + + if err != nil { + if isAWSErr(err, organizations.ErrCodeAWSOrganizationsNotInUseException, "") { + return nil + } + return err + } + + if resp != nil && resp.Organization != nil { + return fmt.Errorf("Bad: Organization still exists: %q", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAwsOrganizationsOrganizationExists(n string, a *organizations.Organization) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Organization ID not set") + } + + conn := testAccProvider.Meta().(*AWSClient).organizationsconn + params := &organizations.DescribeOrganizationInput{} + + resp, err := conn.DescribeOrganization(params) + + if err != nil { + return err + } + + if resp == nil || resp.Organization == nil { + return fmt.Errorf("Organization %q does not exist", rs.Primary.ID) + } + + a = resp.Organization + + return nil + } +} + +const testAccAwsOrganizationsOrganizationConfig = "resource \"aws_organizations_organization\" \"test\" {}" + +func testAccAwsOrganizationsOrganizationConfigConsolidatedBilling(feature_set string) string { + return fmt.Sprintf(` +resource "aws_organizations_organization" "test" { + feature_set = "%s" +} +`, feature_set) +} diff --git a/aws/resource_aws_organizations_test.go b/aws/resource_aws_organizations_test.go new file mode 100644 index 000000000000..33fef61b690f --- /dev/null +++ b/aws/resource_aws_organizations_test.go @@ -0,0 +1,27 @@ +package aws + +import ( + "testing" +) + +func TestAccAWSOrganizations(t *testing.T) { + testCases := map[string]map[string]func(t *testing.T){ + "Organization": { + "basic": testAccAwsOrganizationsOrganization_basic, + "importBasic": testAccAwsOrganizationsOrganization_importBasic, + "consolidatedBilling": testAccAwsOrganizationsOrganization_consolidatedBilling, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } + }) + } +} diff --git a/website/aws.erb b/website/aws.erb index 67687148b5c0..235f8f196c12 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1370,6 +1370,16 @@ + > + Organizations Resources + + + > RDS Resources