Skip to content

Commit

Permalink
Merge pull request #28308 from DrFaust92/grafana-vpc
Browse files Browse the repository at this point in the history
VPC support for Grafana workspaces
  • Loading branch information
ewbankkit authored Dec 13, 2022
2 parents 42d6731 + 54414dc commit e9ea507
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 45 deletions.
3 changes: 3 additions & 0 deletions .changelog/28308.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_grafana_workspace: Add `vpc_configuration` argument.
```
66 changes: 66 additions & 0 deletions internal/service/grafana/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ func ResourceWorkspace() *schema.Resource {
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
"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},
},
},
},
},
},

CustomizeDiff: verify.SetTagsDiff,
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
}
141 changes: 96 additions & 45 deletions internal/service/grafana/workspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
),
},
{
Expand Down Expand Up @@ -371,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
Expand All @@ -394,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]
Expand All @@ -405,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"]
Expand All @@ -424,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"]
Expand All @@ -441,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"]
Expand All @@ -459,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"]
Expand All @@ -473,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"]
Expand All @@ -484,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"]
Expand All @@ -497,44 +569,23 @@ 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 testAccWorkspaceConfig_vpc(rName string) string {
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
}
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
}
resource "aws_grafana_workspace" "test" {
account_access_type = "CURRENT_ACCOUNT"
authentication_providers = ["SAML"]
permission_type = "SERVICE_MANAGED"
role_arn = aws_iam_role.test.arn
return fmt.Errorf("Grafana Workspace %s still exists", rs.Primary.ID)
}
return nil
vpc_configuration {
subnet_ids = aws_subnet.test[*].id
security_group_ids = [aws_security_group.test.id]
}
}
`, rName))
}
6 changes: 6 additions & 0 deletions website/docs/r/grafana_workspace.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit e9ea507

Please sign in to comment.