From 80246e19262f491f9be868c3f5f85d31096c718c Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 12 Dec 2022 20:00:55 +0200 Subject: [PATCH 1/6] grafana vpc support --- internal/service/grafana/workspace.go | 66 +++++++++++++++++++ internal/service/grafana/workspace_test.go | 51 ++++++++++++++ .../docs/r/grafana_workspace.html.markdown | 6 ++ 3 files changed, 123 insertions(+) diff --git a/internal/service/grafana/workspace.go b/internal/service/grafana/workspace.go index 7bf65964bf0..ddea340e838 100644 --- a/internal/service/grafana/workspace.go +++ b/internal/service/grafana/workspace.go @@ -114,6 +114,28 @@ func ResourceWorkspace() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "vpc_configuration": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeSet, + Required: true, + MaxItems: 100, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "subnet_ids": { + Type: schema.TypeSet, + Required: true, + MinItems: 2, + MaxItems: 100, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -167,6 +189,10 @@ func resourceWorkspaceCreate(d *schema.ResourceData, meta interface{}) error { input.WorkspaceRoleArn = aws.String(v.(string)) } + if v, ok := d.GetOk("vpc_configuration"); ok { + input.VpcConfiguration = expandVpcConfiguration(v.([]interface{})) + } + log.Printf("[DEBUG] Creating Grafana Workspace: %s", input) output, err := conn.CreateWorkspace(input) @@ -224,6 +250,10 @@ func resourceWorkspaceRead(d *schema.ResourceData, meta interface{}) error { d.Set("saml_configuration_status", workspace.Authentication.SamlConfigurationStatus) d.Set("stack_set_name", workspace.StackSetName) + if err := d.Set("vpc_configuration", flattenVpcConfiguration(workspace.VpcConfiguration)); err != nil { + return fmt.Errorf("error setting vpc_configuration: %w", err) + } + tags := KeyValueTags(workspace.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 @@ -330,3 +360,39 @@ func resourceWorkspaceDelete(d *schema.ResourceData, meta interface{}) error { return nil } + +func expandVpcConfiguration(cfg []interface{}) *managedgrafana.VpcConfiguration { + if len(cfg) < 1 { + return nil + } + + conf := cfg[0].(map[string]interface{}) + + out := managedgrafana.VpcConfiguration{} + + if v, ok := conf["security_group_ids"].(*schema.Set); ok && v.Len() > 0 { + out.SecurityGroupIds = flex.ExpandStringSet(v) + } + + if v, ok := conf["subnet_ids"].(*schema.Set); ok && v.Len() > 0 { + out.SubnetIds = flex.ExpandStringSet(v) + } + + return &out +} + +func flattenVpcConfiguration(rs *managedgrafana.VpcConfiguration) []interface{} { + if rs == nil { + return []interface{}{} + } + + m := make(map[string]interface{}) + if rs.SecurityGroupIds != nil { + m["security_group_ids"] = flex.FlattenStringSet(rs.SecurityGroupIds) + } + if rs.SubnetIds != nil { + m["subnet_ids"] = flex.FlattenStringSet(rs.SubnetIds) + } + + return []interface{}{m} +} diff --git a/internal/service/grafana/workspace_test.go b/internal/service/grafana/workspace_test.go index 801529d0041..48b44724c06 100644 --- a/internal/service/grafana/workspace_test.go +++ b/internal/service/grafana/workspace_test.go @@ -26,6 +26,7 @@ func TestAccGrafana_serial(t *testing.T) { "permissionType": testAccWorkspace_permissionType, "notificationDestinations": testAccWorkspace_notificationDestinations, "tags": testAccWorkspace_tags, + "vpc": testAccWorkspace_vpc, }, "ApiKey": { "basic": testAccWorkspaceAPIKey_basic, @@ -96,6 +97,35 @@ func testAccWorkspace_saml(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "saml_configuration_status", managedgrafana.SamlConfigurationStatusNotConfigured), resource.TestCheckResourceAttr(resourceName, "stack_set_name", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vpc_configuration.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccWorkspace_vpc(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_grafana_workspace.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(managedgrafana.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, managedgrafana.EndpointsID), + CheckDestroy: testAccCheckWorkspaceDestroy, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccWorkspaceConfig_vpc(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckWorkspaceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "vpc_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_configuration.0.security_group_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_configuration.0.subnet_ids.#", "2"), ), }, { @@ -497,6 +527,27 @@ resource "aws_grafana_workspace" "test" { `, rName)) } +func testAccWorkspaceConfig_vpc(rName string) string { + return acctest.ConfigCompose(testAccWorkspaceRole(rName), acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_security_group" "test" { + description = %[1]q + vpc_id = aws_vpc.test.id +} + +resource "aws_grafana_workspace" "test" { + account_access_type = "CURRENT_ACCOUNT" + authentication_providers = ["SAML"] + permission_type = "SERVICE_MANAGED" + role_arn = aws_iam_role.test.arn + + vpc_configuration { + subnet_ids = aws_subnet.test[*].id + security_group_ids = [aws_security_group.test.id] + } +} +`, rName)) +} + func testAccCheckWorkspaceExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] diff --git a/website/docs/r/grafana_workspace.html.markdown b/website/docs/r/grafana_workspace.html.markdown index 59830e7bb56..938dbe44428 100644 --- a/website/docs/r/grafana_workspace.html.markdown +++ b/website/docs/r/grafana_workspace.html.markdown @@ -59,6 +59,12 @@ The following arguments are optional: * `role_arn` - (Optional) The IAM role ARN that the workspace assumes. * `stack_set_name` - (Optional) The AWS CloudFormation stack set name that provisions IAM roles to be used by the workspace. * `tags` - (Optional) Key-value mapping of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `vpc_configuration` - (Optional) The configuration settings for an Amazon VPC that contains data sources for your Grafana workspace to connect to. See [VPC Configuration](#vpc-configuration) below. + +### VPC Configuration + +* `security_group_ids` - (Required) - The list of Amazon EC2 security group IDs attached to the Amazon VPC for your Grafana workspace to connect. +* `subnet_ids` - (Required) - The list of Amazon EC2 subnet IDs created in the Amazon VPC for your Grafana workspace to connect. ## Attributes Reference From e1acc4cf071f0751d323cbf97a12fdf8e7c61ee9 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 12 Dec 2022 20:04:10 +0200 Subject: [PATCH 2/6] changelog --- .changelog/28308.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/28308.txt diff --git a/.changelog/28308.txt b/.changelog/28308.txt new file mode 100644 index 00000000000..ed011ef2815 --- /dev/null +++ b/.changelog/28308.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_grafana_workspace: Add `vpc_configuration` argument. +``` \ No newline at end of file From 4966c2b63c490ae0d00e3720e1d19bd9a78482d9 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 12 Dec 2022 20:04:30 +0200 Subject: [PATCH 3/6] changelog --- internal/service/grafana/workspace_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/grafana/workspace_test.go b/internal/service/grafana/workspace_test.go index 48b44724c06..c7f84a5e07a 100644 --- a/internal/service/grafana/workspace_test.go +++ b/internal/service/grafana/workspace_test.go @@ -542,7 +542,7 @@ resource "aws_grafana_workspace" "test" { vpc_configuration { subnet_ids = aws_subnet.test[*].id - security_group_ids = [aws_security_group.test.id] + security_group_ids = [aws_security_group.test.id] } } `, rName)) From 085ea3ae85fbe4b81912a8539ab8166df1ed99d3 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Mon, 12 Dec 2022 20:09:02 +0200 Subject: [PATCH 4/6] fmt --- internal/service/grafana/workspace.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/grafana/workspace.go b/internal/service/grafana/workspace.go index ddea340e838..15eda4dd302 100644 --- a/internal/service/grafana/workspace.go +++ b/internal/service/grafana/workspace.go @@ -190,7 +190,7 @@ func resourceWorkspaceCreate(d *schema.ResourceData, meta interface{}) error { } if v, ok := d.GetOk("vpc_configuration"); ok { - input.VpcConfiguration = expandVpcConfiguration(v.([]interface{})) + input.VpcConfiguration = expandVPCConfiguration(v.([]interface{})) } log.Printf("[DEBUG] Creating Grafana Workspace: %s", input) @@ -250,7 +250,7 @@ func resourceWorkspaceRead(d *schema.ResourceData, meta interface{}) error { d.Set("saml_configuration_status", workspace.Authentication.SamlConfigurationStatus) d.Set("stack_set_name", workspace.StackSetName) - if err := d.Set("vpc_configuration", flattenVpcConfiguration(workspace.VpcConfiguration)); err != nil { + if err := d.Set("vpc_configuration", flattenVPCConfiguration(workspace.VpcConfiguration)); err != nil { return fmt.Errorf("error setting vpc_configuration: %w", err) } @@ -361,7 +361,7 @@ func resourceWorkspaceDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func expandVpcConfiguration(cfg []interface{}) *managedgrafana.VpcConfiguration { +func expandVPCConfiguration(cfg []interface{}) *managedgrafana.VpcConfiguration { if len(cfg) < 1 { return nil } @@ -381,7 +381,7 @@ func expandVpcConfiguration(cfg []interface{}) *managedgrafana.VpcConfiguration return &out } -func flattenVpcConfiguration(rs *managedgrafana.VpcConfiguration) []interface{} { +func flattenVPCConfiguration(rs *managedgrafana.VpcConfiguration) []interface{} { if rs == nil { return []interface{}{} } From 9d347fa41b10750f528ffe1d9fc917badae53218 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Dec 2022 11:09:41 -0500 Subject: [PATCH 5/6] r/aws_grafana_workspace: Alphabetize attributes. --- internal/service/grafana/workspace.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/grafana/workspace.go b/internal/service/grafana/workspace.go index 15eda4dd302..d024f47cd0d 100644 --- a/internal/service/grafana/workspace.go +++ b/internal/service/grafana/workspace.go @@ -114,6 +114,8 @@ func ResourceWorkspace() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), "vpc_configuration": { Type: schema.TypeList, MaxItems: 1, @@ -136,8 +138,6 @@ func ResourceWorkspace() *schema.Resource { }, }, }, - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), }, CustomizeDiff: verify.SetTagsDiff, From 54414dcf74935d9e7b99d7fa857c014c7583296a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Dec 2022 11:12:37 -0500 Subject: [PATCH 6/6] r/aws_grafana_workspace: Tidy up acceptance test configurations. --- internal/service/grafana/workspace_test.go | 102 ++++++++++----------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/internal/service/grafana/workspace_test.go b/internal/service/grafana/workspace_test.go index c7f84a5e07a..55f7a8638c7 100644 --- a/internal/service/grafana/workspace_test.go +++ b/internal/service/grafana/workspace_test.go @@ -401,7 +401,49 @@ func testAccWorkspace_notificationDestinations(t *testing.T) { }) } -func testAccWorkspaceRole(rName string) string { +func testAccCheckWorkspaceExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Grafana Workspace ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).GrafanaConn + + _, err := tfgrafana.FindWorkspaceByID(conn, rs.Primary.ID) + + return err + } +} + +func testAccCheckWorkspaceDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).GrafanaConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_grafana_workspace" { + continue + } + + _, err := tfgrafana.FindWorkspaceByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Grafana Workspace %s still exists", rs.Primary.ID) + } + return nil +} + +func testAccWorkspaceConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_iam_role" "test" { name = %[1]q @@ -424,7 +466,7 @@ resource "aws_iam_role" "test" { } func testAccWorkspaceConfig_authenticationProvider(rName, authenticationProvider string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = [%[1]q] @@ -435,7 +477,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_organization(rName string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "ORGANIZATION" authentication_providers = ["SAML"] @@ -454,7 +496,7 @@ resource "aws_organizations_organizational_unit" "test" { } func testAccWorkspaceConfig_tags1(rName, tagKey1, tagValue1 string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = ["SAML"] @@ -471,7 +513,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = ["SAML"] @@ -489,7 +531,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_dataSources(rName string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = ["SAML"] @@ -503,7 +545,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_permissionType(rName, permissionType string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = ["SAML"] @@ -514,7 +556,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_notificationDestinations(rName string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), fmt.Sprintf(` resource "aws_grafana_workspace" "test" { account_access_type = "CURRENT_ACCOUNT" authentication_providers = ["SAML"] @@ -528,7 +570,7 @@ resource "aws_grafana_workspace" "test" { } func testAccWorkspaceConfig_vpc(rName string) string { - return acctest.ConfigCompose(testAccWorkspaceRole(rName), acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` + return acctest.ConfigCompose(testAccWorkspaceConfig_base(rName), acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` resource "aws_security_group" "test" { description = %[1]q vpc_id = aws_vpc.test.id @@ -547,45 +589,3 @@ resource "aws_grafana_workspace" "test" { } `, rName)) } - -func testAccCheckWorkspaceExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No Grafana Workspace ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).GrafanaConn - - _, err := tfgrafana.FindWorkspaceByID(conn, rs.Primary.ID) - - return err - } -} - -func testAccCheckWorkspaceDestroy(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).GrafanaConn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_grafana_workspace" { - continue - } - - _, err := tfgrafana.FindWorkspaceByID(conn, rs.Primary.ID) - - if tfresource.NotFound(err) { - continue - } - - if err != nil { - return err - } - - return fmt.Errorf("Grafana Workspace %s still exists", rs.Primary.ID) - } - return nil -}