Open hryamzik opened 5 years ago
OK, I managed to get this working. Almost... As soon as name
parameter for aws_ssm_association
forces new resource:
"name": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
It's possible to generate new document on every script change and reference it in aws_ssm_association
:
variable scripts {
default = [
"myscript.sh"
]
}
resource "aws_s3_bucket_object" "scripts" {
key = "${var.scripts[count.index]}"
bucket = "${aws_s3_bucket.scripts.bucket}"
source = "${path.module}/scripts/${var.scripts[count.index]}"
etag = "${md5(file("${path.module}/scripts/${var.scripts[count.index]}"))}"
acl = "private"
count = "${length(var.scripts)}"
}
resource "aws_ssm_document" "run-remote-script" {
name = "RunRemoteScript-${element(aws_s3_bucket_object.scripts.*.etag, count.index)}"
document_type = "Command"
content = "${file("${path.module}/documents/RunRemoteScript.yaml")}"
count = "${length(var.scripts)}"
document_format = "YAML"
}
resource "aws_ssm_association" "script" {
name = "AWS-RunRemoteScript-${element(aws_s3_bucket_object.scripts.*.etag, index(var.scripts, "myscript.sh"))}"
association_name = "s3-script"
parameters {
sourceType = "S3"
sourceInfo = <<EOJ
{
"path": "https://s3.amazonaws.com/${aws_s3_bucket.scripts.bucket}/myscript.sh"
}
EOJ
commandLine = "myscript.sh"
}
}
That configuration seem to be valid but terraform fails to apply changes on the first run, see #10004
👍
I'm still thinking of implementing a separate optional parameter to force new resource when it changes.
AWS's syntax for SourceInfo
is really bad. Been looking for to deploy my script from s3 while also interpolating the bucket id which is how I ended up here. It does seem odd though that an update to the s3 object that the ssm association reads as source doesn't trigger a change
I think the real issue here might be that default_version
and latest_version
attributes of aws_ssm_document
are treated as args by AWS provider, for some reason. So when I use them to set document_version
arg of aws_ssm_association
, that doesn't result in the update of aws_ssm_association
when the document_version
gets updated. I have to re-apply TF a 2nd time to get the version propagated..
(This solution avoids the necessity to change document name
on every run)
Found another potential workaround:
# document.yml
schemaVersion: "2.2"
parameters:
version:
type: String
description: Version tag for the document content, used for updates
...
resource "aws_ssm_association" "test" {
name = aws_ssm_document.test.name
targets {
...
}
parameters = {
version = md5(aws_ssm_document.test[0].content)
}
}
This way, whenever the doc content gets updated (including any Etags/hashes stored within), the aws_ssm_association
also gets updated, since content
does get re-computed by TF on every run.
@hryamzik , do you have an example of your "RunRemoteScript.yaml", please? 🙏
We're trying to do something similar, but were using the AWS provided document AWS-ApplyAnsiblePlaybooks
.
We're uploading the zip file containing the ansible playbooks to s3 with terraform, and the goal is to have the association re-apply when the zip file changes.
The AWS-ApplyAnsiblePlaybooks
document doesn't contain a version param.
Is there a workaround for this?
edit: Turns out this is rather simple:
data "archive_file" "ansible_playbooks_bundle" {
type = "zip"
source_dir = "path/to/ansible/dir"
output_path = "${path.module}/ansible.zip"
}
locals {
ansible_bundle_md5 = filemd5(data.archive_file.ansible_playbooks_bundle.output_path)
}
resource "aws_s3_bucket_object" "ansible_playbooks_bundle" {
key = "ansible-${local.ansible_bundle_md5}.zip"
bucket = aws_s3_bucket.playbooks_bucket.id
source = data.archive_file.ansible_playbooks_bundle.output_path
etag = local.ansible_bundle_md5
acl = "private"
}
output "ansible_playbooks_bundle_s3_object" {
value = aws_s3_bucket_object.ansible_playbooks_bundle
}
output "ansible_playbooks_s3_bucket" {
value = module.ssm_setup.playbooks_bucket_id
}
output "ansible_playbooks_bundle_md5" {
value = local.ansible_bundle_md5
}
I'm still thinking of implementing a separate optional parameter to force new resource when it changes.
That would probably be useful for other use cases.
Yes, we've accomplished it very similarly
not sure if this helps anyone, but I managed to force the association using the ExtraVariables to add a "version" by generating an md5 hash of the playbook.yml file so whenever we change the yaml it will update the association: ` resource "aws_ssm_association" "ansible" { name = "AWS-ApplyAnsiblePlaybooks" association_name = "ansible" parameters = { SourceType = "S3" SourceInfo = "{\"path\": \"https://${module.s3_playbooks.s3_bucket_bucket_domain_name}/playbook.yml\"}" PlaybookFile = "playbook.yml" InstallDependencies = "True" Verbose = "-v" ExtraVariables = "Version=${filemd5("ansible/playbook.yml")}" } output_location { s3_bucket_name = module.s3_logging.s3_bucket_id }
targets { key = "tag:Name" values = ["web"] } } `
well. following trick works:
resource "null_resource" "always_run" { triggers = { timestamp = "${timestamp()}" } }
resource "aws_ssm_document" "document" { .. lifecycle { replace_triggered_by = [ null_resource.always_run ] }
resource "aws_ssm_association" "run" { .. lifecycle { replace_triggered_by = [ null_resource.always_run ] }
in that way. it re creates aws document and association on each run.
Eero
I have a
aws_ssm_association
with a document that downloads script from s3 and executes it. I want to get that association regenerated each time I change the script so that it was re-run on all existing instances.I was looking for a terraform-wide solution but didn't find one. Here's what I'd expect: