provider/aws Add support for updating SSM documents (#13491)

* Add schema_version as computed ssm document attribute

* Update the SSM document itself if the content has changed and it has a schema >= 2.0

* Add default_version as DocumentVersion in SSM doc update

* Acceptance test for updating an SSM document

* Better error handling in updating SSM documents

* Add SSM document update documentation

* Better names for SSM input params
This commit is contained in:
Peter Goodman 2017-04-10 23:13:43 +12:00 committed by Paul Stack
parent 113c06a81c
commit 6ad4b89b8a
3 changed files with 168 additions and 0 deletions

View File

@ -3,6 +3,7 @@ package aws
import (
"fmt"
"log"
"strconv"
"strings"
"time"
@ -14,6 +15,10 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)
const (
MINIMUM_VERSIONED_SCHEMA = 2.0
)
func resourceAwsSsmDocument() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSsmDocumentCreate,
@ -35,6 +40,10 @@ func resourceAwsSsmDocument() *schema.Resource {
Required: true,
ValidateFunc: validateAwsSSMDocumentType,
},
"schema_version": {
Type: schema.TypeString,
Computed: true,
},
"created_date": {
Type: schema.TypeString,
Computed: true,
@ -173,6 +182,7 @@ func resourceAwsSsmDocumentRead(d *schema.ResourceData, meta interface{}) error
d.Set("created_date", doc.CreatedDate)
d.Set("default_version", doc.DefaultVersion)
d.Set("description", doc.Description)
d.Set("schema_version", doc.SchemaVersion)
if _, ok := d.GetOk("document_type"); ok {
d.Set("document_type", doc.DocumentType)
@ -238,6 +248,23 @@ func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) erro
log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
}
if !d.HasChange("content") {
return nil
}
if schemaVersion, ok := d.GetOk("schemaVersion"); ok {
schemaNumber, _ := strconv.ParseFloat(schemaVersion.(string), 64)
if schemaNumber < MINIMUM_VERSIONED_SCHEMA {
log.Printf("[DEBUG] Skipping document update because document version is not 2.0 %q", d.Id())
return nil
}
}
if err := updateAwsSSMDocument(d, meta); err != nil {
return err
}
return resourceAwsSsmDocumentRead(d, meta)
}
@ -381,6 +408,47 @@ func deleteDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
return nil
}
func updateAwsSSMDocument(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Updating SSM Document: %s", d.Id())
name := d.Get("name").(string)
updateDocInput := &ssm.UpdateDocumentInput{
Name: aws.String(name),
Content: aws.String(d.Get("content").(string)),
DocumentVersion: aws.String(d.Get("default_version").(string)),
}
newDefaultVersion := d.Get("default_version").(string)
ssmconn := meta.(*AWSClient).ssmconn
updated, err := ssmconn.UpdateDocument(updateDocInput)
if isAWSErr(err, "DuplicateDocumentContent", "") {
log.Printf("[DEBUG] Content is a duplicate of the latest version so update is not necessary: %s", d.Id())
log.Printf("[INFO] Updating the default version to the latest version %s: %s", newDefaultVersion, d.Id())
newDefaultVersion = d.Get("latest_version").(string)
} else if err != nil {
return errwrap.Wrapf("Error updating SSM document: {{err}}", err)
} else {
log.Printf("[INFO] Updating the default version to the new version %s: %s", newDefaultVersion, d.Id())
newDefaultVersion = *updated.DocumentDescription.DocumentVersion
}
updateDefaultInput := &ssm.UpdateDocumentDefaultVersionInput{
Name: aws.String(name),
DocumentVersion: aws.String(newDefaultVersion),
}
_, err = ssmconn.UpdateDocumentDefaultVersion(updateDefaultInput)
if err != nil {
return errwrap.Wrapf("Error updating the default document version to that of the updated document: {{err}}", err)
}
return nil
}
func validateAwsSSMDocumentType(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
types := map[string]bool{

View File

@ -29,6 +29,39 @@ func TestAccAWSSSMDocument_basic(t *testing.T) {
})
}
func TestAccAWSSSMDocument_update(t *testing.T) {
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMDocumentDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSSSMDocument20Config(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMDocumentExists("aws_ssm_document.foo"),
resource.TestCheckResourceAttr(
"aws_ssm_document.foo", "schema_version", "2.0"),
resource.TestCheckResourceAttr(
"aws_ssm_document.foo", "latest_version", "1"),
resource.TestCheckResourceAttr(
"aws_ssm_document.foo", "default_version", "1"),
),
},
resource.TestStep{
Config: testAccAWSSSMDocument20UpdatedConfig(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMDocumentExists("aws_ssm_document.foo"),
resource.TestCheckResourceAttr(
"aws_ssm_document.foo", "latest_version", "2"),
resource.TestCheckResourceAttr(
"aws_ssm_document.foo", "default_version", "2"),
),
},
},
})
}
func TestAccAWSSSMDocument_permission(t *testing.T) {
name := acctest.RandString(10)
resource.Test(t, resource.TestCase{
@ -186,6 +219,66 @@ DOC
`, rName)
}
func testAccAWSSSMDocument20Config(rName string) string {
return fmt.Sprintf(`
resource "aws_ssm_document" "foo" {
name = "test_document-%s"
document_type = "Command"
content = <<DOC
{
"schemaVersion": "2.0",
"description": "Sample version 2.0 document v2",
"parameters": {
},
"mainSteps": [
{
"action": "aws:runPowerShellScript",
"name": "runPowerShellScript",
"inputs": {
"runCommand": [
"Get-Process"
]
}
}
]
}
DOC
}
`, rName)
}
func testAccAWSSSMDocument20UpdatedConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_ssm_document" "foo" {
name = "test_document-%s"
document_type = "Command"
content = <<DOC
{
"schemaVersion": "2.0",
"description": "Sample version 2.0 document v2",
"parameters": {
},
"mainSteps": [
{
"action": "aws:runPowerShellScript",
"name": "runPowerShellScript",
"inputs": {
"runCommand": [
"Get-Process -Verbose"
]
}
}
]
}
DOC
}
`, rName)
}
func testAccAWSSSMDocumentPermissionConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_ssm_document" "foo" {

View File

@ -10,6 +10,10 @@ description: |-
Provides an SSM Document resource
~> **NOTE on updating SSM documents:** Only documents with a schema version of 2.0
or greater can update their content once created, see [SSM Schema Features][1]. To update a document with an older
schema version you must recreate the resource.
## Example Usage
```
@ -56,6 +60,7 @@ The following attributes are exported:
* `content` - The json content of the document.
* `created_date` - The date the document was created.
* `description` - The description of the document.
* `schema_version` - The schema version of the document.
* `document_type` - The type of document created.
* `default_version` - The default version of the document.
* `hash` - The sha1 or sha256 of the document content
@ -67,6 +72,8 @@ The following attributes are exported:
* `permissions` - The permissions of how this document should be shared.
* `platform_types` - A list of OS platforms compatible with this SSM document, either "Windows" or "Linux".
[1]: http://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-ssm-docs.html#document-schemas-features
## Permissions
The permissions attribute specifies how you want to share the document. If you share a document privately,