Multi-line PowerShell in Terraform
Running a inline PowerShell statement
You most likely know that you can run arbitrary scripts using local-exec provisioner within Terraform. These scripts are run on the local machine running Terraform, not on the resource that has just been created.
Say we have a script that just runs some PowerShell that prints out a name variable that was passed in we’d script it out like the following:
variable "name" { }
resource "null_resource" "script1" {
provisioner "local-exec" {
command = "Write-Host \"Hello ${var.name}\""
interpreter = ["PowerShell", "-Command"]
}
}
Then when we apply these changes we’ll get the following output.
$ terraform apply
var.name
Enter a value: Bob
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ null_resource.script1
id: <computed>
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
null_resource.script1: Creating...
null_resource.script1: Provisioning with 'local-exec'...
null_resource.script1 (local-exec): Executing: ["PowerShell" "-Command" "Write-Host \"Hello Bob\""]
null_resource.script1 (local-exec): Hello Bob
null_resource.script1: Creation complete after 0s (ID: 2394746270154995733)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
As we’d expect we output Hello Bob
to the console.
But how do we go about running multiple statements?
Running multiple inline PowerShell statements
One mistake which is common is to set the command
property multiple times:
provisioner "local-exec" {
command = "Write-Host \"Hello ${var.name}\""
command = "Write-Host \"Bye \""
interpreter = ["PowerShell", "-Command"]
}
However when executed this will just output the last command that was given.
null_resource.script1 (local-exec): Executing: ["PowerShell" "-Command" "Write-Host \"Goodbye\""]
null_resource.script1 (local-exec): Goodbye
To write a multi-line powershell statement in Terraform we can use the heredoc syntax, this is where we start the string off with a <<
followed by a delimiting identifier.
Knowing this we can change around the above script in to the following:
provisioner "local-exec" {
command = <<EOT
Write-Host "Hello ${var.name}"
Write-Host "Bye"
EOT
interpreter = ["PowerShell", "-Command"]
}
This will then output the expected results:
null_resource.script1 (local-exec): Executing: ["PowerShell" "-Command" " Write-Host \"Hello Bob\"\n Write-Host \"Bye\"\n "]
null_resource.script1 (local-exec): Hello Bob
null_resource.script1 (local-exec): Bye
As you can see the script is much cleaner now as we’re not having to escape the quotes within the powershell script.
Wrapping in to a file?
It might also be ideal to abstract away your provisioning scripts, this makes it easier to run locally to test and also allows you to wrap Pester tests around them.
On the interpreter
property on the provisioner
we can pass in -File
instead of -Command
and then just reference a .ps
file that we wish to execute.
provisioner "local-exec" {
command = "external.ps1"
interpreter = ["PowerShell", "-File"]
}