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/ssm_parameter: allow adding tags to a parameter if overwrite is specified #18640

Merged
merged 2 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .changelog/18640.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_ssm_parameter: Allow `tags` to be applied to resource when `overwrite` is configured
```
184 changes: 119 additions & 65 deletions aws/resource_aws_ssm_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

const (
Expand All @@ -22,9 +24,9 @@ const (

func resourceAwsSsmParameter() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSsmParameterPut,
Create: resourceAwsSsmParameterCreate,
Read: resourceAwsSsmParameterRead,
Update: resourceAwsSsmParameterPut,
Update: resourceAwsSsmParameterUpdate,
Delete: resourceAwsSsmParameterDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
Expand Down Expand Up @@ -108,12 +110,70 @@ func resourceAwsSsmParameter() *schema.Resource {
}
}

func resourceAwsSsmParameterCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ssmconn

name := d.Get("name").(string)

paramInput := &ssm.PutParameterInput{
Name: aws.String(name),
Type: aws.String(d.Get("type").(string)),
Tier: aws.String(d.Get("tier").(string)),
Value: aws.String(d.Get("value").(string)),
Overwrite: aws.Bool(shouldUpdateSsmParameter(d)),
AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
}

if v, ok := d.GetOk("data_type"); ok {
paramInput.DataType = aws.String(v.(string))
}

if v, ok := d.GetOk("description"); ok {
paramInput.Description = aws.String(v.(string))
}

if keyID, ok := d.GetOk("key_id"); ok && d.Get("type").(string) == ssm.ParameterTypeSecureString {
paramInput.SetKeyId(keyID.(string))
}

// AWS SSM Service only supports PutParameter requests with Tags
// iff Overwrite is not provided or is false; in this resource's case,
// the Overwrite value is always set in the paramInput so we check for the value
if v, ok := d.GetOk("tags"); ok && !aws.BoolValue(paramInput.Overwrite) {
paramInput.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SsmTags()
}

_, err := conn.PutParameter(paramInput)

if tfawserr.ErrMessageContains(err, "ValidationException", "Tier is not supported") {
paramInput.Tier = nil
_, err = conn.PutParameter(paramInput)
}

if err != nil {
return fmt.Errorf("error creating SSM parameter (%s): %w", name, err)
}

// Since the AWS SSM Service does not support PutParameter requests with
// Tags and Overwrite set to true, we make an additional API call
// to Update the resource's tags if necessary
if d.HasChange("tags") && paramInput.Tags == nil {
o, n := d.GetChange("tags")

if err := keyvaluetags.SsmUpdateTags(conn, name, ssm.ResourceTypeForTaggingParameter, o, n); err != nil {
return fmt.Errorf("error updating SSM Parameter (%s) tags: %w", name, err)
}
}

d.SetId(name)

return resourceAwsSsmParameterRead(d, meta)
}

func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
conn := meta.(*AWSClient).ssmconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

log.Printf("[DEBUG] Reading SSM Parameter: %s", d.Id())

input := &ssm.GetParameterInput{
Name: aws.String(d.Id()),
WithDecryption: aws.Bool(true),
Expand All @@ -122,9 +182,9 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
var resp *ssm.GetParameterOutput
err := resource.Retry(ssmParameterCreationValidationTimeout, func() *resource.RetryError {
var err error
resp, err = ssmconn.GetParameter(input)
resp, err = conn.GetParameter(input)

if isAWSErr(err, ssm.ErrCodeParameterNotFound, "") && d.IsNewResource() && d.Get("data_type").(string) == "aws:ec2:image" {
if tfawserr.ErrCodeEquals(err, ssm.ErrCodeParameterNotFound) && d.IsNewResource() && d.Get("data_type").(string) == "aws:ec2:image" {
return resource.RetryableError(fmt.Errorf("error reading SSM Parameter (%s) after creation: this can indicate that the provided parameter value could not be validated by SSM", d.Id()))
}

Expand All @@ -135,11 +195,11 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
return nil
})

if isResourceTimeoutError(err) {
resp, err = ssmconn.GetParameter(input)
if tfresource.TimedOut(err) {
resp, err = conn.GetParameter(input)
}

if isAWSErr(err, ssm.ErrCodeParameterNotFound, "") && !d.IsNewResource() {
if tfawserr.ErrCodeEquals(err, ssm.ErrCodeParameterNotFound) && !d.IsNewResource() {
log.Printf("[WARN] SSM Parameter (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
Expand All @@ -150,7 +210,7 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
}

param := resp.Parameter
name := *param.Name
name := aws.StringValue(param.Name)
d.Set("name", name)
d.Set("type", param.Type)
d.Set("value", param.Value)
Expand All @@ -165,9 +225,9 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
},
},
}
describeResp, err := ssmconn.DescribeParameters(describeParamsInput)
describeResp, err := conn.DescribeParameters(describeParamsInput)
if err != nil {
return fmt.Errorf("error describing SSM parameter: %w", err)
return fmt.Errorf("error describing SSM parameter (%s): %w", d.Id(), err)
}

if describeResp == nil || len(describeResp.Parameters) == 0 || describeResp.Parameters[0] == nil {
Expand All @@ -186,7 +246,7 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
d.Set("allowed_pattern", detail.AllowedPattern)
d.Set("data_type", detail.DataType)

tags, err := keyvaluetags.SsmListTags(ssmconn, name, ssm.ResourceTypeForTaggingParameter)
tags, err := keyvaluetags.SsmListTags(conn, name, ssm.ResourceTypeForTaggingParameter)

if err != nil {
return fmt.Errorf("error listing tags for SSM Parameter (%s): %w", name, err)
Expand All @@ -201,76 +261,70 @@ func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error
return nil
}

func resourceAwsSsmParameterDelete(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
func resourceAwsSsmParameterUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ssmconn

if d.HasChangesExcept("tags") {
paramInput := &ssm.PutParameterInput{
Name: aws.String(d.Get("name").(string)),
Type: aws.String(d.Get("type").(string)),
Tier: aws.String(d.Get("tier").(string)),
Value: aws.String(d.Get("value").(string)),
Overwrite: aws.Bool(shouldUpdateSsmParameter(d)),
AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
}

log.Printf("[INFO] Deleting SSM Parameter: %s", d.Id())
if d.HasChange("data_type") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..turns out you can't actually change a "data_type" tho (i tried going from image -> text, but not vice versa yet)

Error: error updating SSM parameter (tf-acc-test-1338304779148665738): ValidationException: You can’t change a parameter’s data type.
        	status code: 400, request id: d166482e-29ef-4f9c-822d-6c8a0f0f99ca

paramInput.DataType = aws.String(d.Get("data_type").(string))
}

_, err := ssmconn.DeleteParameter(&ssm.DeleteParameterInput{
Name: aws.String(d.Get("name").(string)),
})
if err != nil {
return fmt.Errorf("error deleting SSM Parameter (%s): %s", d.Id(), err)
}
if d.HasChange("description") {
paramInput.Description = aws.String(d.Get("description").(string))
}

return nil
}
if d.HasChange("key_id") && d.Get("type").(string) == ssm.ParameterTypeSecureString {
paramInput.SetKeyId(d.Get("key_id").(string))
}

func resourceAwsSsmParameterPut(d *schema.ResourceData, meta interface{}) error {
ssmconn := meta.(*AWSClient).ssmconn
_, err := conn.PutParameter(paramInput)

name := d.Get("name").(string)
log.Printf("[INFO] Creating SSM Parameter: %s", name)
if tfawserr.ErrMessageContains(err, "ValidationException", "Tier is not supported") {
paramInput.Tier = nil
_, err = conn.PutParameter(paramInput)
}

paramInput := &ssm.PutParameterInput{
Name: aws.String(name),
Type: aws.String(d.Get("type").(string)),
Tier: aws.String(d.Get("tier").(string)),
Value: aws.String(d.Get("value").(string)),
Overwrite: aws.Bool(shouldUpdateSsmParameter(d)),
anGie44 marked this conversation as resolved.
Show resolved Hide resolved
AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
if err != nil {
return fmt.Errorf("error updating SSM parameter (%s): %w", d.Id(), err)
}
}

if v, ok := d.GetOk("data_type"); ok {
paramInput.DataType = aws.String(v.(string))
}
if d.HasChange("tags") {
o, n := d.GetChange("tags")

if d.HasChange("description") {
_, n := d.GetChange("description")
paramInput.Description = aws.String(n.(string))
if err := keyvaluetags.SsmUpdateTags(conn, d.Id(), ssm.ResourceTypeForTaggingParameter, o, n); err != nil {
return fmt.Errorf("error updating SSM Parameter (%s) tags: %w", d.Id(), err)
}
}

if keyID, ok := d.GetOk("key_id"); ok && d.Get("type").(string) == ssm.ParameterTypeSecureString {
paramInput.SetKeyId(keyID.(string))
}
return resourceAwsSsmParameterRead(d, meta)
}

if v, ok := d.GetOk("tags"); ok && d.IsNewResource() {
paramInput.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SsmTags()
}
func resourceAwsSsmParameterDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ssmconn

log.Printf("[DEBUG] Waiting for SSM Parameter %v to be updated", d.Get("name"))
_, err := ssmconn.PutParameter(paramInput)
_, err := conn.DeleteParameter(&ssm.DeleteParameterInput{
Name: aws.String(d.Get("name").(string)),
})

if isAWSErr(err, "ValidationException", "Tier is not supported") {
paramInput.Tier = nil
_, err = ssmconn.PutParameter(paramInput)
if tfawserr.ErrCodeEquals(err, ssm.ErrCodeParameterNotFound) {
return nil
}

if err != nil {
return fmt.Errorf("error creating SSM parameter: %w", err)
}

if !d.IsNewResource() && d.HasChange("tags") {
o, n := d.GetChange("tags")

if err := keyvaluetags.SsmUpdateTags(ssmconn, name, ssm.ResourceTypeForTaggingParameter, o, n); err != nil {
return fmt.Errorf("error updating SSM Parameter (%s) tags: %w", name, err)
}
return fmt.Errorf("error deleting SSM Parameter (%s): %s", d.Id(), err)
}

d.SetId(name)

return resourceAwsSsmParameterRead(d, meta)
return nil
}

func shouldUpdateSsmParameter(d *schema.ResourceData) bool {
Expand Down
Loading