<p>here is a complete example:</p>
<pre><code>resource &quot;aws_key_pair&quot; &quot;my_key&quot; {
 key_name   = &quot;my_key&quot;
 public_key = file(pathexpand(&quot;~/.ssh/id_rsa.pub&quot;))
}

resource &quot;aws_instance&quot; &quot;example&quot; {
  ami = my_ami
  instance_type = &quot;t2.micro&quot;
  key_name = aws_key_pair.my_key.key_name
  ebs_block_device {
    device_name = &quot;/dev/sda1&quot;
    volume_size = 50
  }
  provisioner &quot;remote-exec&quot; {
   inline = [&quot;my_command&quot;]
  }
  connection {
   host        = coalesce(self.public_ip, self.private_ip)
   agent       = true
   type        = &quot;ssh&quot;
   user        = &quot;My_user_name&quot;
   private_key = file(pathexpand(&quot;~/.ssh/id_rsa&quot;))
  }
}
</code></pre>
<p>so I assumed that you have your public and private key located in your home directory in <code>.ssh</code> otherwise you need to hard code the path. and you need to make sure that your IP is allowd in the AWS security group.</p>

CC BY-SA 4.0
2021-03-04T21:15:28.950Z
2021-03-04T21:21:05.330Z
2021-03-04T21:21:05.330Z
LinPy
<p><a href="https://www.terraform.io/docs/language/resources/provisioners/syntax.html#provisioners-are-a-last-resort" rel="nofollow noreferrer">Terraform Provisioners are a last resort</a>, so I'd encourage you to think about other options first.</p>
<p>In this case, your example suggests that you're already using cloud-init and so if at all possible I'd suggest modifying that cloud-init configuration to include a request to install the package you want to install. If you are using the cloud-config YAML format then you can use the <a href="https://cloudinit.readthedocs.io/en/latest/topics/modules.html#package-update-upgrade-install" rel="nofollow noreferrer">Package Update Upgrade Install</a> module to get a similar effect to your <code>yum install</code> command line:</p>
<pre><code>packages:
 - coolprogram
</code></pre>
<hr />
<p>However, if you've eliminated all other options except provisioners then the typical way to use remote-exec with an EC2 instance is to pass one of the instance's own IP addresses as the hostname, which you can do by using the special <code>self</code> object in the <code>connection</code> block to refer to the attributes of the object that the provisioner is running against, like this:</p>
<pre><code>resource &quot;aws_instance&quot; &quot;example&quot; {
  ami           = &quot;ami-1234&quot;
  instance_type = &quot;t2.large&quot;
  key_name      = &quot;foobar&quot;
  user_data     = local.cloud_config_config

  connection {
    host = self.private_ip
  }

  provisioner &quot;remote-exec&quot; {
    inline = [&quot;sudo yum -y install coolprogram&quot;]
  }
}
</code></pre>
<p>The above example uses <code>self.private_ip</code>, which will work only if the host where you are running Terraform is running in or able to connect to the VPC where you're deploying this instance. Another option is to use <code>self.public_ip</code> to use the public IP address, but that will work only if your VPC has an Internet Gateway and your instance belongs to a security group that can accept incoming SSH connections from the internet.</p>
<p>In addition to supplying a suitable IP address, you'll also need to generate and assign an allowed SSH public key to the EC2 instance and provide the corresponding private key in the <code>connection</code> block, using the <code>private_key</code> argument. (Or one of the other authentication strategies that <a href="https://www.terraform.io/docs/language/resources/provisioners/connection.html" rel="nofollow noreferrer">a <code>connection</code> block</a> supports.)</p>
<p>These connection and authentication complexities are, as you hopefully saw in the Terraform documentation I linked earlier, one of the main reasons why provisioners are a last resort. Using cloud-init instead will avoid this because cloud-init runs automatically on system boot and retrieves the <code>user_data</code> string using a private API accessible from the instance's private network interface, and so it doesn't need any outside connectivity, to start any listening services, or to generate any new credentials.</p>

CC BY-SA 4.0
2021-03-04T01:43:17.320Z
When I use what you provided, I see gray text like this: "aws_instance.example (remote-exec): Private key: false...Certificate: False...Checking Host Key: false". The server never gets created either. I see this:

"Error: timeout - last error: SSH authentication failed (root@1.1.1.1:22): ssh: handshake failed: ssh: unable to authenticate, attempted methods [none], no supported methods remain"
Kamal S.
CC BY-SA 4.0
2021-03-04T18:38:10.107Z
Later in my answer I talked about authentication, in the paragraph starting with "In addition to supplying a suitable IP address". My example focused only on the direct answer to the problem you asked; if you have follow-up questions about authentication I'd suggest starting a new question on this site so that you can describe what constraints you have on how to set up authentication and so answerers can go into some more detail about that new question than I can in comments here.
Martin Atkins
CC BY-SA 4.0
2021-03-04T01:21:57.227Z
2021-03-04T01:21:57.227Z
Martin Atkins
<p>As it was pointed out by Martin, remote-exec provisioner may not be able to connect to the target EC2 instance because of networking or security issues.</p>
<p>If the EC2 instance is <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/managed_instances.html" rel="nofollow noreferrer">SSM managed</a>, you can use <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/run-command.html" rel="nofollow noreferrer">AWS Systems Manager Run Command</a> to execute command on the instance.</p>
<p>Resource <a href="https://registry.terraform.io/providers/pbobov/ssm/latest/docs/resources/command" rel="nofollow noreferrer">ssm_command</a> from 'pbobov/ssm' provider can be used to run SSM commands by Terraform. It was specifically designed as a workaround for using remote-exec provisioner.</p>

CC BY-SA 4.0
2023-02-09T23:18:30.863Z
2023-02-09T23:19:59.407Z
2023-02-09T23:19:59.407Z
pbobov
<p>I am using Terraform 0.14 and AWS.  I am trying to write a .tf file that will invoke a remote-exec command via Terraform's provisioner.</p>
<p>This code does not work:</p>
<pre><code>resource &quot;aws_instance&quot; &quot;example&quot; {
  ami           = &quot;ami-1234&quot;
  instance_type = &quot;t2.large&quot;
  key_name      = &quot;foobar&quot;
  user_data     = local.cloud_config_config

  provisioner &quot;remote-exec&quot; {
    inline = [&quot;sudo yum -y install coolprogram&quot;]
  }
}
</code></pre>
<p>The above will create the EC-2 instance.  But it won't run the inline command.  After I run &quot;terraform apply&quot; with the above content in my .tf file, I see &quot;Error: host for provisioner cannot be empty&quot;</p>
<p>I tried this .tf file variation, but I get an error about the variable:</p>
<pre><code>resource &quot;aws_instance&quot; &quot;example&quot; {
  ami           = &quot;ami-1234&quot;
  instance_type = &quot;t2.large&quot;
  key_name      = &quot;foobar&quot;
  user_data     = local.cloud_config_config

  provisioner &quot;remote-exec&quot; {
    inline = [&quot;sudo yum -y install coolprogram&quot;]
  }
  connection {
    host     = &quot;${var.host_name}&quot;
  }
}
</code></pre>
<p>The hostname will be created dynamically.  Terraform's <a href="https://www.terraform.io/docs/language/resources/provisioners/remote-exec.html" rel="nofollow noreferrer">documentation</a> does not mention a hostname.  I have tried different variables.tf files.</p>
<p>But I always get something like this:</p>
<blockquote>
<p>aws_instance.example (remote-exec): Connecting to remote host via
SSH... aws_instance.example (remote-exec):   Host: example
aws_instance.example (remote-exec):   User: root aws_instance.example
(remote-exec):   Password: false aws_instance.example (remote-exec):<br />
Private key: false aws_instance.example (remote-exec):   Certificate:
false aws_instance.example (remote-exec):   SSH Agent: false
aws_instance.example (remote-exec):   Checking Host Key: false
aws_instance.example (remote-exec):   Target Platform: unix</p>
</blockquote>
<p>How do I use the remote-exec provisioner on an EC-2 server?</p>

<amazon-web-services><terraform><amazon-ec2>
How do I use the remote-exec provisioner with Terraform?
Kamal S.
# How do I use the remote-exec provisioner with Terraform?

(Asked by: username_0 on 2021-03-03)

I am using Terraform 0.14 and AWS.  I am trying to write a .tf file that will invoke a remote-exec command via Terraform's provisioner.
This code does not work:
resource "aws_instance" "example" {
  ami           = "ami-1234"
  instance_type = "t2.large"
  key_name      = "foobar"
  user_data     = local.cloud_config_config

  provisioner "remote-exec" {
    inline = ["sudo yum -y install coolprogram"]
  }
}

The above will create the EC-2 instance.  But it won't run the inline command.  After I run "terraform apply" with the above content in my .tf file, I see "Error: host for provisioner cannot be empty"
I tried this .tf file variation, but I get an error about the variable:
resource "aws_instance" "example" {
  ami           = "ami-1234"
  instance_type = "t2.large"
  key_name      = "foobar"
  user_data     = local.cloud_config_config

  provisioner "remote-exec" {
    inline = ["sudo yum -y install coolprogram"]
  }
  connection {
    host     = "${var.host_name}"
  }
}

The hostname will be created dynamically.  Terraform's documentation does not mention a hostname.  I have tried different variables.tf files.
But I always get something like this:

aws_instance.example (remote-exec): Connecting to remote host via
SSH... aws_instance.example (remote-exec):   Host: example
aws_instance.example (remote-exec):   User: root aws_instance.example
(remote-exec):   Password: false aws_instance.example (remote-exec):
Private key: false aws_instance.example (remote-exec):   Certificate:
false aws_instance.example (remote-exec):   SSH Agent: false
aws_instance.example (remote-exec):   Checking Host Key: false
aws_instance.example (remote-exec):   Target Platform: unix

How do I use the remote-exec provisioner on an EC-2 server?

**Tags:** amazon-web-services, terraform, amazon-ec2


---

## 3 answers

### Answer by username_1 on 2021-03-04. Score: 2

here is a complete example:
resource "aws_key_pair" "my_key" {
 key_name   = "my_key"
 public_key = file(pathexpand("~/.ssh/id_rsa.pub"))
}

resource "aws_instance" "example" {
  ami = my_ami
  instance_type = "t2.micro"
  key_name = aws_key_pair.my_key.key_name
  ebs_block_device {
    device_name = "/dev/sda1"
    volume_size = 50
  }
  provisioner "remote-exec" {
   inline = ["my_command"]
  }
  connection {
   host        = coalesce(self.public_ip, self.private_ip)
   agent       = true
   type        = "ssh"
   user        = "My_user_name"
   private_key = file(pathexpand("~/.ssh/id_rsa"))
  }
}

so I assumed that you have your public and private key located in your home directory in .ssh otherwise you need to hard code the path. and you need to make sure that your IP is allowd in the AWS security group.


---

### Answer by username_2 on 2021-03-04. Score: 2

Terraform Provisioners are a last resort, so I'd encourage you to think about other options first.
In this case, your example suggests that you're already using cloud-init and so if at all possible I'd suggest modifying that cloud-init configuration to include a request to install the package you want to install. If you are using the cloud-config YAML format then you can use the Package Update Upgrade Install module to get a similar effect to your yum install command line:
packages:
 - coolprogram


However, if you've eliminated all other options except provisioners then the typical way to use remote-exec with an EC2 instance is to pass one of the instance's own IP addresses as the hostname, which you can do by using the special self object in the connection block to refer to the attributes of the object that the provisioner is running against, like this:
resource "aws_instance" "example" {
  ami           = "ami-1234"
  instance_type = "t2.large"
  key_name      = "foobar"
  user_data     = local.cloud_config_config

  connection {
    host = self.private_ip
  }

  provisioner "remote-exec" {
    inline = ["sudo yum -y install coolprogram"]
  }
}

The above example uses self.private_ip, which will work only if the host where you are running Terraform is running in or able to connect to the VPC where you're deploying this instance. Another option is to use self.public_ip to use the public IP address, but that will work only if your VPC has an Internet Gateway and your instance belongs to a security group that can accept incoming SSH connections from the internet.
In addition to supplying a suitable IP address, you'll also need to generate and assign an allowed SSH public key to the EC2 instance and provide the corresponding private key in the connection block, using the private_key argument. (Or one of the other authentication strategies that a connection block supports.)
These connection and authentication complexities are, as you hopefully saw in the Terraform documentation I linked earlier, one of the main reasons why provisioners are a last resort. Using cloud-init instead will avoid this because cloud-init runs automatically on system boot and retrieves the user_data string using a private API accessible from the instance's private network interface, and so it doesn't need any outside connectivity, to start any listening services, or to generate any new credentials.

> **Comments:**
> **username_0**: When I use what you provided, I see gray text like this: "aws_instance.example (remote-exec): Private key: false...Certificate: False...Checking Host Key: false". The server never gets created either. I see this:  "Error: timeout - last error: SSH authentication failed (root@1.1.1.1:22): ssh: handshake failed: ssh: unable to authenticate, attempted methods [none], no supported methods remain"  
> **username_2**: Later in my answer I talked about authentication, in the paragraph starting with "In addition to supplying a suitable IP address". My example focused only on the direct answer to the problem you asked; if you have follow-up questions about authentication I'd suggest starting a new question on this site so that you can describe what constraints you have on how to set up authentication and so answerers can go into some more detail about that new question than I can in comments here.  

---

### Answer by username_3 on 2023-02-09. Score: 0

As it was pointed out by Martin, remote-exec provisioner may not be able to connect to the target EC2 instance because of networking or security issues.

If the EC2 instance is [SSM managed](https://docs.aws.amazon.com/systems-manager/latest/userguide/managed_instances.html), you can use [AWS Systems Manager Run Command](https://docs.aws.amazon.com/systems-manager/latest/userguide/run-command.html) to execute command on the instance.

Resource [ssm\_command](https://registry.terraform.io/providers/username_3/ssm/latest/docs/resources/command) from 'username_3/ssm' provider can be used to run SSM commands by Terraform. It was specifically designed as a workaround for using remote-exec provisioner.


---

