Adding a New Tag Resource#
Adding a tag resource, similar to the aws_ecs_tag
resource, has its own implementation procedure since the resource code and initial acceptance testing functions are automatically generated. The rest of the resource acceptance testing and resource documentation must still be manually created.
- In
internal/generate
: Ensure the service is supported by all generators. Runmake gen
after any modifications. - In
internal/service/{service}/generate.go
: Add the new//go:generate
call with the correct generator directives. Runmake gen
after any modifications. - In
internal/provider/provider.go
: Add the new resource. - Run
make test
and ensure there are no failures. - Create
internal/service/{service}/tag_gen_test.go
with initial acceptance testing similar to the following (where the parent resource is simple to provision):
import (
"fmt"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-aws/names"
)
func TestAcc{Service}Tag_basic(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_{service}_tag.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.{Service}ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheck{Service}TagDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAcc{Service}TagConfig(rName, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccCheck{Service}TagExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "key", "key1"),
resource.TestCheckResourceAttr(resourceName, "value", "value1"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAcc{Service}Tag_disappears(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_{service}_tag.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.{Service}ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheck{Service}TagDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAcc{Service}TagConfig(rName, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccCheck{Service}TagExists(ctx, resourceName),
acctest.CheckResourceDisappears(ctx, acctest.Provider, resourceAws{Service}Tag(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}
func TestAcc{Service}Tag_Value(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_{service}_tag.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.{Service}ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheck{Service}TagDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAcc{Service}TagConfig(rName, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccCheck{Service}TagExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "key", "key1"),
resource.TestCheckResourceAttr(resourceName, "value", "value1"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAcc{Service}TagConfig(rName, "key1", "value1updated"),
Check: resource.ComposeTestCheckFunc(
testAccCheck{Service}TagExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "key", "key1"),
resource.TestCheckResourceAttr(resourceName, "value", "value1updated"),
),
},
},
})
}
func testAcc{Service}TagConfig(rName string, key string, value string) string {
return fmt.Sprintf(`
resource "aws_{service}_{thing}" "test" {
name = %[1]q
lifecycle {
ignore_changes = [tags]
}
}
resource "aws_{service}_tag" "test" {
resource_arn = aws_{service}_{thing}.test.arn
key = %[2]q
value = %[3]q
}
`, rName, key, value)
}
- Run
make testacc TESTS=TestAcc{Service}Tags_ PKG={Service}
and ensure there are no failures. - Create
website/docs/r/{service}_tag.html.markdown
with initial documentation similar to the following:
---
subcategory: "{SERVICE}"
layout: "aws"
page_title: "AWS: aws_{service}_tag"
description: |-
Manages an individual {SERVICE} resource tag
---
# Resource: aws_{service}_tag
Manages an individual {SERVICE} resource tag. This resource should only be used in cases where {SERVICE} resources are created outside Terraform (e.g., {SERVICE} {THING}s implicitly created by {OTHER SERVICE THING}).
~> **NOTE:** This tagging resource should not be combined with the Terraform resource for managing the parent resource. For example, using `aws_{service}_{thing}` and `aws_{service}_tag` to manage tags of the same {SERVICE} {THING} will cause a perpetual difference where the `aws_{service}_{thing}` resource will try to remove the tag being added by the `aws_{service}_tag` resource.
~> **NOTE:** This tagging resource does not use the [provider `ignore_tags` configuration](/docs/providers/aws/index.html#ignore_tags).
## Example Usage
```terraform
resource "aws_{service}_tag" "example" {
resource_arn = "..."
key = "Name"
value = "Hello World"
}
```
## Argument Reference
This resource supports the following arguments:
* `resource_arn` - (Required) ARN of the {SERVICE} resource to tag.
* `key` - (Required) Tag name.
* `value` - (Required) Tag value.
## Attribute Reference
This resource exports the following attributes in addition to the arguments above:
* `id` - {SERVICE} resource identifier and key, separated by a comma (`,`)
## Import
Import `aws_{service}_tag` using the {SERVICE} resource identifier and key, separated by a comma (`,`). For example:
```console
$ terraform import aws_{service}_tag.example arn:aws:{service}:us-east-1:123456789012:{thing}/example,Name
```