-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
provider/aws: New SSM Parameter resource #14043
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/hashicorp/errwrap" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsSsmParameter() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsSsmParameterCreate, | ||
Read: resourceAwsSsmParameterRead, | ||
Update: resourceAwsSsmParameterUpdate, | ||
Delete: resourceAwsSsmParameterDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateSsmParameterType, | ||
}, | ||
"value": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsSsmParameterCreate(d *schema.ResourceData, meta interface{}) error { | ||
return putAwsSSMParameter(d, meta, false) | ||
} | ||
|
||
func resourceAwsSsmParameterRead(d *schema.ResourceData, meta interface{}) error { | ||
ssmconn := meta.(*AWSClient).ssmconn | ||
|
||
log.Printf("[DEBUG] Reading SSM Parameter: %s", d.Id()) | ||
|
||
paramInput := &ssm.GetParametersInput{ | ||
Names: []*string{ | ||
aws.String(d.Get("name").(string)), | ||
}, | ||
WithDecryption: aws.Bool(true), | ||
} | ||
|
||
resp, err := ssmconn.GetParameters(paramInput) | ||
|
||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error describing SSM parameter: {{err}}", err) | ||
} | ||
|
||
if len(resp.InvalidParameters) > 0 { | ||
return fmt.Errorf("[ERROR] SSM Parameter %s is invalid", d.Id()) | ||
} | ||
|
||
param := resp.Parameters[0] | ||
d.Set("name", param.Name) | ||
d.Set("type", param.Type) | ||
d.Set("value", param.Value) | ||
|
||
return nil | ||
} | ||
|
||
func resourceAwsSsmParameterUpdate(d *schema.ResourceData, meta interface{}) error { | ||
return putAwsSSMParameter(d, meta, true) | ||
} | ||
|
||
func resourceAwsSsmParameterDelete(d *schema.ResourceData, meta interface{}) error { | ||
ssmconn := meta.(*AWSClient).ssmconn | ||
|
||
log.Printf("[INFO] Deleting SSM Parameter: %s", d.Id()) | ||
|
||
paramInput := &ssm.DeleteParameterInput{ | ||
Name: aws.String(d.Get("name").(string)), | ||
} | ||
|
||
_, err := ssmconn.DeleteParameter(paramInput) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
|
||
return nil | ||
} | ||
|
||
func putAwsSSMParameter(d *schema.ResourceData, meta interface{}, overwrite bool) error { | ||
ssmconn := meta.(*AWSClient).ssmconn | ||
|
||
log.Printf("[INFO] Creating SSM Parameter: %s", d.Get("name").(string)) | ||
|
||
paramInput := &ssm.PutParameterInput{ | ||
Name: aws.String(d.Get("name").(string)), | ||
Type: aws.String(d.Get("type").(string)), | ||
Value: aws.String(d.Get("value").(string)), | ||
Overwrite: aws.Bool(overwrite), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just FYI - there's |
||
} | ||
|
||
log.Printf("[DEBUG] Waiting for SSM Parameter %q to be updated", d.Get("name").(string)) | ||
err := resource.Retry(5*time.Minute, func() *resource.RetryError { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is the reason for retrying this operation if there's no retryable error? 🤔 |
||
_, err := ssmconn.PutParameter(paramInput) | ||
|
||
if err != nil { | ||
return resource.NonRetryableError(err) | ||
} | ||
|
||
d.SetId(d.Get("name").(string)) | ||
return nil | ||
}) | ||
|
||
if err != nil { | ||
return errwrap.Wrapf("[ERROR] Error creating SSM parameter: {{err}}", err) | ||
} | ||
|
||
return resourceAwsSsmParameterRead(d, meta) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAWSSSMParameter_basic(t *testing.T) { | ||
name := acctest.RandString(10) | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSSMParameterDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSSSMParameterBasicConfig(name, "bar"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSSMParameterHasValue("aws_ssm_parameter.foo", "bar"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAWSSSMParameter_update(t *testing.T) { | ||
name := acctest.RandString(10) | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSSMParameterDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSSSMParameterBasicConfig(name, "bar"), | ||
}, | ||
{ | ||
Config: testAccAWSSSMParameterBasicConfig(name, "baz"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSSMParameterHasValue("aws_ssm_parameter.foo", "baz"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAWSSSMParameter_secure(t *testing.T) { | ||
name := acctest.RandString(10) | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSSMParameterDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSSSMParameterSecureConfig(name, "secret"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSSMParameterHasValue("aws_ssm_parameter.secret_foo", "secret"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAWSSSMParameterHasValue(n string, v string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("No SSM Parameter ID is set") | ||
} | ||
|
||
conn := testAccProvider.Meta().(*AWSClient).ssmconn | ||
|
||
paramInput := &ssm.GetParametersInput{ | ||
Names: []*string{ | ||
aws.String(rs.Primary.Attributes["name"]), | ||
}, | ||
WithDecryption: aws.Bool(true), | ||
} | ||
|
||
resp, _ := conn.GetParameters(paramInput) | ||
|
||
if len(resp.Parameters) == 0 { | ||
return fmt.Errorf("Expected AWS SSM Parameter to be created, but wasn't found") | ||
} | ||
|
||
parameterValue := resp.Parameters[0].Value | ||
|
||
if *parameterValue != v { | ||
return fmt.Errorf("Expected AWS SSM Parameter to have value %s but had %s", v, *parameterValue) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckAWSSSMParameterDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).ssmconn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_ssm_parameter" { | ||
continue | ||
} | ||
|
||
paramInput := &ssm.GetParametersInput{ | ||
Names: []*string{ | ||
aws.String(rs.Primary.Attributes["name"]), | ||
}, | ||
} | ||
|
||
resp, _ := conn.GetParameters(paramInput) | ||
|
||
if len(resp.Parameters) > 0 { | ||
return fmt.Errorf("Expected AWS SSM Parameter to be gone, but was still found") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
return fmt.Errorf("Default error in SSM Parameter Test") | ||
} | ||
|
||
func testAccAWSSSMParameterBasicConfig(rName string, value string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_ssm_parameter" "foo" { | ||
name = "test_parameter-%s" | ||
type = "String" | ||
value = "%s" | ||
} | ||
`, rName, value) | ||
} | ||
|
||
func testAccAWSSSMParameterSecureConfig(rName string, value string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_ssm_parameter" "secret_foo" { | ||
name = "test_secure_parameter-%s" | ||
type = "SecureString" | ||
value = "%s" | ||
} | ||
`, rName, value) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
layout: "aws" | ||
page_title: "AWS: aws_ssm_parameter" | ||
sidebar_current: "docs-aws-resource-ssm-parameter" | ||
description: |- | ||
Provides an SSM Parameter resource | ||
--- | ||
|
||
# aws\_ssm\_parameter | ||
|
||
Provides an SSM Parameter resource. | ||
|
||
## Example Usage | ||
|
||
To store a basic string parameter: | ||
|
||
```hcl | ||
resource "aws_ssm_parameter" "foo" { | ||
name = "foo" | ||
type = "String" | ||
value = "bar" | ||
} | ||
``` | ||
|
||
To store an encrypted string using the default SSM KMS key: | ||
|
||
```hcl | ||
resource "aws_db_instance" "default" { | ||
allocated_storage = 10 | ||
storage_type = "gp2" | ||
engine = "mysql" | ||
engine_version = "5.7.16" | ||
instance_class = "db.t2.micro" | ||
name = "mydb" | ||
username = "foo" | ||
password = "${var.database_master_password}" | ||
db_subnet_group_name = "my_database_subnet_group" | ||
parameter_group_name = "default.mysql5.7" | ||
} | ||
|
||
resource "aws_ssm_parameter" "secret" { | ||
name = "${var.environment}/database/password/master" | ||
type = "SecureString" | ||
value = "${var.database_master_password}" | ||
} | ||
``` | ||
|
||
~> **Note:** The unencrypted value of a SecureString will be stored in the raw state as plain-text. | ||
[Read more about sensitive data in state](/docs/state/sensitive-data.html). | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `name` - (Required) The name of the parameter. | ||
* `type` - (Required) The type of the parameter. Valid types are `String`, `StringList` and `SecureString`. | ||
* `value` - (Required) The value of the parameter. | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `name` - (Required) The name of the parameter. | ||
* `type` - (Required) The type of the parameter. Valid types are `String`, `StringList` and `SecureString`. | ||
* `value` - (Required) The value of the parameter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth marking this field as
Sensitive
as it may contain sensitive data?