How to execute PowerShell command through Terraform

29,345

Solution 1

You are using a local-exec provisioner which runs the request powershell code on the workstation running Terraform:

The local-exec provisioner invokes a local executable after a resource is created. This invokes a process on the machine running Terraform, not on the resource.

It sounds like you want to execute the powershell script on the resulting instance in which case you'll need to use a remote-exec provisioner which will run your powershell on the target resource:

The remote-exec provisioner invokes a script on a remote resource after it is created. This can be used to run a configuration management tool, bootstrap into a cluster, etc.

You will also need to include connection details, for example:

  provisioner "remote-exec" {
    command = "C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeInstance.ps1 -Schedule",
    interpreter = ["PowerShell"]
    connection {
      type     = "winrm"
      user     = "Administrator"
      password = "${var.admin_password}"
    }
  }

Which means this instance must also be ready to accept WinRM connections.

There are other options for completing this task though. Such as using userdata, which Terraform also supports. This might look like the following example:

Example of using a userdata file in Terraform

File named userdata.txt:

<powershell>
C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeInstance.ps1 -Schedule
</powershell>

Launch instance using the userdata file:

resource "aws_instance" "my-test-instance" {
  ami             = "${data.aws_ami.ec2-worker-initial-encrypted-ami.id}"
  instance_type   = "t2.micro"

  tags {
    Name = "my-test-instance"
  }

  user_data = "${file(userdata.txt)}"
}

The file interpolation will read the contents of the userdata file as string to pass to userdata for the instance launch. Once the instance launches it should run the script as you expect.

Solution 2

What Brian is claiming is correct, you will get "invalid or unknown key: interpreter" error.

To correctly run powershell you will need to run it as following, based on Brandon's answer:

provisioner "remote-exec" {
    connection {
      type     = "winrm"
      user     = "Administrator"
      password = "${var.admin_password}"
    }

inline = [
         "powershell -ExecutionPolicy Unrestricted -File C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeInstance.ps1 -Schedule"
        ]
      }

Edit To copy the files over to the machine use the below:

  provisioner "file" {
    source      = "${path.module}/some_path"
    destination = "C:/some_path"

    connection {
      host = "${azurerm_network_interface.vm_nic.private_ip_address}"
      timeout  = "3m"
      type     = "winrm"
      https    = true
      port     = 5986
      use_ntlm = true
      insecure = true

      #cacert = "${azurerm_key_vault_certificate.vm_cert.certificate_data}"
      user     = var.admin_username
      password = var.admin_password
    }
  }

Update: Currently provisioners are not recommended by hashicorp, full instructions and explanation (it is long) can be found at: terraform.io/docs/provisioners/index.html

Solution 3

FTR: Brandon's answer is correct, except the example code provided for the remote-exec includes keys that are unsupported by the provisioner.

Neither command nor interpreter are supported keys.

https://www.terraform.io/docs/provisioners/remote-exec.html

Share:
29,345
Akshay Tilekar
Author by

Akshay Tilekar

Ruling the world of Big Data Analytics and Machine Learning.

Updated on May 22, 2020

Comments

  • Akshay Tilekar
    Akshay Tilekar almost 4 years

    I am trying to create a Windows Ec2 instance from AMI and executing a powershell command on that as :

    data "aws_ami" "ec2-worker-initial-encrypted-ami" {
        filter {
            name   = "tag:Name"
            values = ["ec2-worker-initial-encrypted-ami"]
        }  
    }
    
    resource "aws_instance" "my-test-instance" {
      ami             = "${data.aws_ami.ec2-worker-initial-encrypted-ami.id}"
      instance_type   = "t2.micro"
    
      tags {
        Name = "my-test-instance"
      }
    
      provisioner "local-exec" {
        command = "C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeInstance.ps1 -Schedule",
        interpreter = ["PowerShell"]
      }
    
    }
    

    and I am facing following error :

    • aws_instance.my-test-instance: Error running command 'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule': exit status 1. Output: The term 'C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:72
    • C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 <<<< -Schedule
      • CategoryInfo : ObjectNotFound: (C:\ProgramData...izeInstance.ps1:String) [], CommandNotFoundException
      • FullyQualifiedErrorId : CommandNotFoundException