diff --git a/builtin/providers/aws/resource_aws_ssm_document.go b/builtin/providers/aws/resource_aws_ssm_document.go index 499c18f33..865871560 100644 --- a/builtin/providers/aws/resource_aws_ssm_document.go +++ b/builtin/providers/aws/resource_aws_ssm_document.go @@ -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{ diff --git a/builtin/providers/aws/resource_aws_ssm_document_test.go b/builtin/providers/aws/resource_aws_ssm_document_test.go index 2242c0b99..dc34276e1 100644 --- a/builtin/providers/aws/resource_aws_ssm_document_test.go +++ b/builtin/providers/aws/resource_aws_ssm_document_test.go @@ -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 = < **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,