Skip to content

Commit b918496

Browse files
authored
Merge pull request #25174 from hashicorp/b-update-paramstore-intelligent-tier
resource/aws_ssm_parameter: Allow `Intelligent-Tiering` to upgrade to `Advanced`
2 parents 557b1d0 + a85f674 commit b918496

File tree

4 files changed

+106
-31
lines changed

4 files changed

+106
-31
lines changed

.changelog/25174.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
resource/aws_ssm_parameter: Allow `Intelligent-Tiering` to upgrade to `Advanced` tier as needed.
3+
```

internal/service/ssm/parameter.go

+22-28
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ func ResourceParameter() *schema.Resource {
4949
"tier": {
5050
Type: schema.TypeString,
5151
Optional: true,
52-
Default: ssm.ParameterTierStandard,
52+
Computed: true,
5353
ValidateFunc: validation.StringInSlice(ssm.ParameterTier_Values(), false),
5454
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
55-
return d.Get("tier").(string) == ssm.ParameterTierIntelligentTiering
55+
if old != "" {
56+
return new == ssm.ParameterTierIntelligentTiering
57+
}
58+
return false
5659
},
5760
},
5861
"type": {
@@ -104,10 +107,8 @@ func ResourceParameter() *schema.Resource {
104107
CustomizeDiff: customdiff.Sequence(
105108
// Prevent the following error during tier update from Advanced to Standard:
106109
// ValidationException: This parameter uses the advanced-parameter tier. You can't downgrade a parameter from the advanced-parameter tier to the standard-parameter tier. If necessary, you can delete the advanced parameter and recreate it as a standard parameter.
107-
// In the case of Advanced to Intelligent-Tiering, a ValidationException is not thrown
108-
// but rather no change occurs without resource re-creation
109110
customdiff.ForceNewIfChange("tier", func(_ context.Context, old, new, meta interface{}) bool {
110-
return old.(string) == ssm.ParameterTierAdvanced && (new.(string) == ssm.ParameterTierStandard || new.(string) == ssm.ParameterTierIntelligentTiering)
111+
return old.(string) == ssm.ParameterTierAdvanced && new.(string) == ssm.ParameterTierStandard
111112
}),
112113
customdiff.ComputedIf("version", func(_ context.Context, diff *schema.ResourceDiff, meta interface{}) bool {
113114
return diff.HasChange("value")
@@ -127,12 +128,14 @@ func resourceParameterCreate(d *schema.ResourceData, meta interface{}) error {
127128
paramInput := &ssm.PutParameterInput{
128129
Name: aws.String(name),
129130
Type: aws.String(d.Get("type").(string)),
130-
Tier: aws.String(d.Get("tier").(string)),
131131
Value: aws.String(d.Get("value").(string)),
132-
Overwrite: aws.Bool(ShouldUpdateParameter(d)),
133132
AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
134133
}
135134

135+
if v, ok := d.GetOk("tier"); ok {
136+
paramInput.Tier = aws.String(v.(string))
137+
}
138+
136139
if v, ok := d.GetOk("data_type"); ok {
137140
paramInput.DataType = aws.String(v.(string))
138141
}
@@ -145,33 +148,20 @@ func resourceParameterCreate(d *schema.ResourceData, meta interface{}) error {
145148
paramInput.SetKeyId(keyID.(string))
146149
}
147150

148-
// AWS SSM Service only supports PutParameter requests with Tags
149-
// iff Overwrite is not provided or is false; in this resource's case,
150-
// the Overwrite value is always set in the paramInput so we check for the value
151-
if len(tags) > 0 && !aws.BoolValue(paramInput.Overwrite) {
151+
if len(tags) > 0 {
152152
paramInput.Tags = Tags(tags.IgnoreAWS())
153153
}
154154

155155
_, err := conn.PutParameter(paramInput)
156156

157157
if tfawserr.ErrMessageContains(err, "ValidationException", "Tier is not supported") {
158+
log.Printf("[WARN] Creating SSM Parameter (%s): tier %q not supported, using default", name, d.Get("tier").(string))
158159
paramInput.Tier = nil
159160
_, err = conn.PutParameter(paramInput)
160161
}
161162

162163
if err != nil {
163-
return fmt.Errorf("error creating SSM parameter (%s): %w", name, err)
164-
}
165-
166-
// Since the AWS SSM Service does not support PutParameter requests with
167-
// Tags and Overwrite set to true, we make an additional API call
168-
// to Update the resource's tags if necessary
169-
if d.HasChange("tags_all") && paramInput.Tags == nil {
170-
o, n := d.GetChange("tags_all")
171-
172-
if err := UpdateTags(conn, name, ssm.ResourceTypeForTaggingParameter, o, n); err != nil {
173-
return fmt.Errorf("error updating SSM Parameter (%s) tags: %w", name, err)
174-
}
164+
return fmt.Errorf("error creating SSM Parameter (%s): %w", name, err)
175165
}
176166

177167
d.SetId(name)
@@ -249,10 +239,7 @@ func resourceParameterRead(d *schema.ResourceData, meta interface{}) error {
249239
detail := describeResp.Parameters[0]
250240
d.Set("key_id", detail.KeyId)
251241
d.Set("description", detail.Description)
252-
d.Set("tier", ssm.ParameterTierStandard)
253-
if detail.Tier != nil {
254-
d.Set("tier", detail.Tier)
255-
}
242+
d.Set("tier", detail.Tier)
256243
d.Set("allowed_pattern", detail.AllowedPattern)
257244
d.Set("data_type", detail.DataType)
258245

@@ -291,6 +278,12 @@ func resourceParameterUpdate(d *schema.ResourceData, meta interface{}) error {
291278
AllowedPattern: aws.String(d.Get("allowed_pattern").(string)),
292279
}
293280

281+
// Retrieve the value set in the config directly to counteract the DiffSuppressFunc above
282+
tier := d.GetRawConfig().GetAttr("tier")
283+
if tier.IsKnown() && !tier.IsNull() {
284+
paramInput.Tier = aws.String(tier.AsString())
285+
}
286+
294287
if d.HasChange("data_type") {
295288
paramInput.DataType = aws.String(d.Get("data_type").(string))
296289
}
@@ -306,12 +299,13 @@ func resourceParameterUpdate(d *schema.ResourceData, meta interface{}) error {
306299
_, err := conn.PutParameter(paramInput)
307300

308301
if tfawserr.ErrMessageContains(err, "ValidationException", "Tier is not supported") {
302+
log.Printf("[WARN] Updating SSM Parameter (%s): tier %q not supported, using default", d.Get("name").(string), d.Get("tier").(string))
309303
paramInput.Tier = nil
310304
_, err = conn.PutParameter(paramInput)
311305
}
312306

313307
if err != nil {
314-
return fmt.Errorf("error updating SSM parameter (%s): %w", d.Id(), err)
308+
return fmt.Errorf("error updating SSM Parameter (%s): %w", d.Id(), err)
315309
}
316310
}
317311

internal/service/ssm/parameter_test.go

+76-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestAccSSMParameter_basic(t *testing.T) {
3333
acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "ssm", fmt.Sprintf("parameter/%s", name)),
3434
resource.TestCheckResourceAttr(resourceName, "value", "test2"),
3535
resource.TestCheckResourceAttr(resourceName, "type", "String"),
36-
resource.TestCheckResourceAttr(resourceName, "tier", "Standard"),
36+
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierStandard),
3737
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
3838
resource.TestCheckResourceAttrSet(resourceName, "version"),
3939
resource.TestCheckResourceAttr(resourceName, "data_type", "text"),
@@ -171,10 +171,11 @@ func TestAccSSMParameter_Tier_intelligentTieringToAdvanced(t *testing.T) {
171171
),
172172
},
173173
{
174+
// Intelligent-Tiering will not downgrade an existing parameter to Standard
174175
Config: testAccParameterConfig_tier(rName, ssm.ParameterTierIntelligentTiering),
175176
Check: resource.ComposeTestCheckFunc(
176177
testAccCheckParameterExists(resourceName, &parameter2),
177-
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierStandard),
178+
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierAdvanced),
178179
),
179180
},
180181
{
@@ -187,6 +188,68 @@ func TestAccSSMParameter_Tier_intelligentTieringToAdvanced(t *testing.T) {
187188
})
188189
}
189190

191+
func TestAccSSMParameter_Tier_intelligentTieringOnCreation(t *testing.T) {
192+
var parameter ssm.Parameter
193+
rName := fmt.Sprintf("%s_%s", t.Name(), sdkacctest.RandString(10))
194+
resourceName := "aws_ssm_parameter.test"
195+
196+
value := sdkacctest.RandString(5000) // Maximum size for Standard tier is 4 KB
197+
198+
resource.ParallelTest(t, resource.TestCase{
199+
PreCheck: func() { acctest.PreCheck(t) },
200+
ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID),
201+
ProviderFactories: acctest.ProviderFactories,
202+
CheckDestroy: testAccCheckParameterDestroy,
203+
Steps: []resource.TestStep{
204+
{
205+
Config: testAccParameterConfig_tierWithValue(rName, ssm.ParameterTierIntelligentTiering, value),
206+
Check: resource.ComposeTestCheckFunc(
207+
testAccCheckParameterExists(resourceName, &parameter),
208+
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierAdvanced),
209+
),
210+
},
211+
{
212+
ResourceName: resourceName,
213+
ImportState: true,
214+
ImportStateVerify: true,
215+
ImportStateVerifyIgnore: []string{"overwrite"},
216+
},
217+
},
218+
})
219+
}
220+
221+
func TestAccSSMParameter_Tier_intelligentTieringOnUpdate(t *testing.T) {
222+
var parameter ssm.Parameter
223+
rName := fmt.Sprintf("%s_%s", t.Name(), sdkacctest.RandString(10))
224+
resourceName := "aws_ssm_parameter.test"
225+
226+
standardSizedValue := sdkacctest.RandString(10)
227+
advancedSizedValue := sdkacctest.RandString(5000)
228+
229+
resource.ParallelTest(t, resource.TestCase{
230+
PreCheck: func() { acctest.PreCheck(t) },
231+
ErrorCheck: acctest.ErrorCheck(t, ssm.EndpointsID),
232+
ProviderFactories: acctest.ProviderFactories,
233+
CheckDestroy: testAccCheckParameterDestroy,
234+
Steps: []resource.TestStep{
235+
{
236+
Config: testAccParameterConfig_tierWithValue(rName, ssm.ParameterTierIntelligentTiering, standardSizedValue),
237+
Check: resource.ComposeTestCheckFunc(
238+
testAccCheckParameterExists(resourceName, &parameter),
239+
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierStandard),
240+
),
241+
},
242+
{
243+
Config: testAccParameterConfig_tierWithValue(rName, ssm.ParameterTierIntelligentTiering, advancedSizedValue),
244+
Check: resource.ComposeTestCheckFunc(
245+
testAccCheckParameterExists(resourceName, &parameter),
246+
resource.TestCheckResourceAttr(resourceName, "tier", ssm.ParameterTierAdvanced),
247+
),
248+
},
249+
},
250+
})
251+
}
252+
190253
func TestAccSSMParameter_disappears(t *testing.T) {
191254
var param ssm.Parameter
192255
name := fmt.Sprintf("%s_%s", t.Name(), sdkacctest.RandString(10))
@@ -770,6 +833,17 @@ resource "aws_ssm_parameter" "test" {
770833
`, rName, tier)
771834
}
772835

836+
func testAccParameterConfig_tierWithValue(rName, tier, value string) string {
837+
return fmt.Sprintf(`
838+
resource "aws_ssm_parameter" "test" {
839+
name = %[1]q
840+
tier = %[2]q
841+
type = "String"
842+
value = %[3]q
843+
}
844+
`, rName, tier, value)
845+
}
846+
773847
func testAccParameterConfig_dataTypeEC2Image(rName string) string {
774848
return acctest.ConfigCompose(
775849
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),

website/docs/r/ssm_parameter.html.markdown

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ The following arguments are supported:
6161
* `type` - (Required) The type of the parameter. Valid types are `String`, `StringList` and `SecureString`.
6262
* `value` - (Required) The value of the parameter. This value is always marked as sensitive in the Terraform plan output, regardless of `type`. In Terraform CLI version 0.15 and later, this may require additional configuration handling for certain scenarios. For more information, see the [Terraform v0.15 Upgrade Guide](https://www.terraform.io/upgrade-guides/0-15.html#sensitive-output-values).
6363
* `description` - (Optional) The description of the parameter.
64-
* `tier` - (Optional) The tier of the parameter. If not specified, will default to `Standard`. Valid tiers are `Standard`, `Advanced`, and `Intelligent-Tiering`. For more information on parameter tiers, see the [AWS SSM Parameter tier comparison and guide](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-advanced-parameters.html).
64+
* `tier` - (Optional) The parameter tier to assign to the parameter.
65+
If not specified, will use the default parameter tier for the region.
66+
Valid tiers are `Standard`, `Advanced`, and `Intelligent-Tiering`.
67+
Downgrading an `Advanced` tier parameter to `Standard` will recreate the resource.
68+
For more information on parameter tiers, see the [AWS SSM Parameter tier comparison and guide](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-advanced-parameters.html).
6569
* `key_id` - (Optional) The KMS key id or arn for encrypting a SecureString.
6670
* `overwrite` - (Optional) Overwrite an existing parameter. If not specified, will default to `false` if the resource has not been created by terraform to avoid overwrite of existing resource and will default to `true` otherwise (terraform lifecycle rules should then be used to manage the update behavior).
6771
* `allowed_pattern` - (Optional) A regular expression used to validate the parameter value.

0 commit comments

Comments
 (0)