Why can't Terraform SSH into my EC2 instance? - amazon-web-services

I am trying to ssh into a newly created EC2 instance with terraform. My host is Windows 10 and I have no problems SSHing into the instance using Bitvise SSH Client from my host but Terraform can't seem to SSH in to create a directory on the instance:
My main.tf:
provider "aws" {
region = "us-west-2"
}
resource "aws_security_group" "instance" {
name = "inlets-server-instance"
description = "Security group for the inlets server"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "tunnel" {
ami = "ami-07b4f3c02c7f83d59"
instance_type = "t2.nano"
key_name = "${var.key_name}"
vpc_security_group_ids = [aws_security_group.instance.id]
tags = {
Name = "inlets-server"
}
provisioner "local-exec" {
command = "echo ${aws_instance.tunnel.public_ip} > ${var.public_ip_path}"
}
provisioner "remote-exec" {
inline = [
"mkdir /home/${var.ssh_user}/ansible",
]
connection {
type = "ssh"
host = "${file("${var.public_ip_path}")}"
user = "${var.ssh_user}"
private_key = "${file("${var.private_key_path}")}"
timeout = "1m"
agent = false
}
}
}
My variables.tf:
variable "key_name" {
description = "Name of the SSH key pair generated in Amazon EC2."
default = "aws_ssh_key"
}
variable "public_ip_path" {
description = "Path to the file that contains the instance's public IP address"
default = "ip_address.txt"
}
variable "private_key_path" {
description = "Path to the private SSH key, used to access the instance."
default = "aws_ssh_key.pem"
}
variable "ssh_user" {
description = "SSH user name to connect to your instance."
default = "ubuntu"
}
All I get are attempted connections:
aws_instance.tunnel (remote-exec): Connecting to remote host via SSH...
aws_instance.tunnel (remote-exec): Host: XX.XXX.XXX.XXX
aws_instance.tunnel (remote-exec): User: ubuntu
aws_instance.tunnel (remote-exec): Password: false
aws_instance.tunnel (remote-exec): Private key: true
aws_instance.tunnel (remote-exec): Certificate: false
aws_instance.tunnel (remote-exec): SSH Agent: false
aws_instance.tunnel (remote-exec): Checking Host Key: false
and it finally timeouts with:
Error: timeout - last error: dial tcp: lookup XX.XXX.XXX.XXX
: no such host
Any ideas?

You didn't talk about your network structure.
Is your win10 machine inside the VPC? If not, do you have internet gateway, routing table, NAT gateway properly set up?
It would be cleaner and safer to create an Elastic IP resource to access the IP address of your machine with terraform knowledge instead of trying to get it from the machine. Surely, the local exec will be quicker than the remote exec but you create an implicit dependency that might generate problems.

Related

I can't ssh into my newly created EC2 instance and can't figure it out for the life of me

I really can't figure out why I'm unable to SSH into my newly created EC2 instance and can't figure out why for the life of me.
Here is some of my code in Terraform where I created the EC2 and security groups for it.
This is my EC2 code
resource "aws_key_pair" "AzureDevOps" {
key_name = var.infra_env
public_key = var.public_ssh_key
}
# Create network inferface for EC2 instance and assign secruity groups
resource "aws_network_interface" "vm_nic_1" {
subnet_id = var.subnet_id
private_ips = ["10.0.0.100"]
tags = {
Name = "${var.infra_env}-nic-1"
}
security_groups = [
var.ssh_id
]
}
# Add elastic IP addresss for public connectivity
resource "aws_eip" "vm_eip_1" {
vpc = true
instance = aws_instance.virtualmachine_1.id
associate_with_private_ip = "10.0.0.100"
depends_on = [var.gw_1]
tags = {
Name = "${var.infra_env}-eip-1"
}
}
# Deploy virtual machine using Ubuntu ami
resource "aws_instance" "virtualmachine_1" {
ami = var.ami
instance_type = var.instance_type
key_name = aws_key_pair.AzureDevOps.id
#retrieve the Administrator password
get_password_data = true
connection {
type = "ssh"
port = 22
password = rsadecrypt(self.password_data, file("id_rsa"))
https = true
insecure = true
timeout = "10m"
}
network_interface {
network_interface_id = aws_network_interface.vm_nic_1.id
device_index = 0
}
user_data = file("./scripts/install-cwagent.ps1")
tags = {
Name = "${var.infra_env}-vm-1"
}
}
Here is the code for my security group
resource "aws_security_group" "ssh" {
name = "allow_ssh"
description = "Allow access to the instance via ssh"
vpc_id = var.vpc_id
ingress {
description = "Access the instance via ssh"
from_port = 22
to_port = 22
protocol = "TCP"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.infra_env}-allow-ssh"
}
}
If I need to provide any more code or information I can, it's my first time trying to do this and it's frustrating trying to figure it out. I'm trying to use Putty as well and not sure if I just don't know how to use it correctly or if it's something wrong with my EC2 configuration.
I used my public ssh key from my computer for the variable in my aws_key_pair resource. I saved my public ssh key pair as a .ppk file for putty and on my aws console when I go to "connect" it says to use ubuntu#10.0.0.100 for my host name in Putty which I did and when I click okay and it tries to connect it gets a network error connection timed out
I used my public ssh key
You need to use your private key, not public.
use ubuntu#10.0.0.100
10.0.0.100 is private IP address. To be able to connect to your instance over the internet you need to use public IP address.

Allow SSH access to GCP VM instances provisioned with Terraform

I'm trying to create VM instances on GCP using Terraform. Instances do get created but I can't seem to have SSH access to the instances. My tf file:
# Cloud Provider
provider "google" {
version = "3.5.0"
credentials = file("./terraform-service-account.json")
project = "terraform-279210"
region = "us-central1"
zone = "us-central1-c"
}
# Virtual Private Network
resource "google_compute_network" "vpc_network" {
name = "terraform-network"
}
# VM Instance
resource "google_compute_instance" "demo-vm-instance" {
name = "demo-vm-instance"
machine_type = "f1-micro"
tags = ["demo-vm-instance"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
}
}
metadata = {
ssh-keys = "demouser:${file("./demouser.pub")}"
}
network_interface {
network = google_compute_network.vpc_network.name
access_config {
}
}
}
ssh -i demouser.pub demouser#<vm-external-ip> returns ssh: connect to host <vm-external-ip> port 22: Operation timed out
Looks like firewall rules block TCP connections through port 22 as nc -zv <vm-external-ip> 22 doesn't succeed.
Create firewall rules using following
resource "google_compute_firewall" "ssh-rule" {
name = "demo-ssh"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["22"]
}
target_tags = ["demo-vm-instance"]
source_ranges = ["0.0.0.0/0"]
}

Can't SSH into EC2 instance created with Terraform

I am working on a very simple Terraform project. I am using windows command prompt. I only have one EC2 instance for now. This is the project structure -
terraform-project
|_ec2.tf
|_vars.tf
|_test-key
|_test-key.pub
|_.terraform
|_terraform.tfstate
The ec2.tf file is as below -
provider "aws" {
region = "eu-central-1"
}
resource "aws_key_pair" "test-key"{
key_name = "test-key"
public_key = "${file("test-key.pub")}"
}
resource "aws_instance" "my-ec2"{
ami = "${var.ami}"
instance_type = "${var.instance_type}"
key_name = "${aws_key_pair.test-key.key_name}"
tags = {
Name = "Terraform"
Batch = "7am"
}
}
The vars.tf file is as below -
variable "ami" {
default = "ami-0233214e13e500f77"
}
variable "instance_type" {
default = "t2.micro"
}
Terraform Apply worked successfully and I can see the instance from the AWS management console. But when I try to SSH into the instance I get permission issues-
ssh -i test-key ec2-user#54.xx.xxx.xxx
ssh: connect to host 54.xx.xxx.xxx port 22: Permission denied
The instance has default VPC and security group. All inbound and outbound traffic is allowed.
I am working behind a company proxy. Before I started I set the proxy settings on my windows command prompt -
set HTTP_PROXY=http://proxy.companytnet.net:port
set HTTPS_PROXY=http://proxy.companynet.net:port
SSH with verbose gives this:
ssh -vvv -i test-key ec2-user#54.xx.xxx.xxx
OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5
debug1: Reading configuration data C:\\Users\\M710583/.ssh/config
debug3: Failed to open file:C:/ProgramData/ssh/ssh_config error:2
debug2: resolve_canonicalize: hostname 54.xx.xxx.xxx is address
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to 54.xx.xxx.xxx [54.xx.xxx.xxx] port 22.
debug3: finish_connect - ERROR: async io completed with error: 10013, io:00000256B95289B0
debug1: connect to address 54.xx.xxx.xxx port 22: Permission denied
ssh: connect to host 54.xx.xxx.xxx port 22: Permission denied
I able to SSH into other servers and instances but not into the one created with Terraform. What am I doing wrong ?
You need to add a proper security group. Something like:
resource "aws_security_group" "main" {
egress = [
{
cidr_blocks = [ "0.0.0.0/0", ]
description = ""
from_port = 0
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "-1"
security_groups = []
self = false
to_port = 0
}
]
ingress = [
{
cidr_blocks = [ "0.0.0.0/0", ]
description = ""
from_port = 22
ipv6_cidr_blocks = []
prefix_list_ids = []
protocol = "tcp"
security_groups = []
self = false
to_port = 22
}
]
}
resource "aws_instance" "my-ec2"{
ami = "${var.ami}"
instance_type = "${var.instance_type}"
key_name = "${aws_key_pair.test-key.key_name}"
tags = {
Name = "Terraform"
Batch = "7am"
}
vpc_security_group_ids = [aws_security_group.main.id]
}
I would recommend adding the vpc_security_group_ids = "${aws_security_group.main-sg.id}" to your EC2 resource block.
This attribute associates your VPC with the security group which you are referencing; read more about it here.

Cannot provision aws_spot_instance via terraform

I am attempting to spin up a spot instance via terraform. When I try to use a provisioner block (either "remote-exec" or "file"), it fails and I see an SSH error in DEBUG level output. When I switch from a spot instance request to a standard aws instance resource declaration, the provisioning works fine.
Code not working:
resource "aws_spot_instance_request" "worker01" {
ami = "ami-0cb95574"
spot_price = "0.02"
instance_type = "m3.medium"
vpc_security_group_ids = [ "${aws_security_group.ssh_access.id}", "${aws_security_group.tcp_internal_access.id}","${aws_security_group.splunk_access.id}","${aws_security_group.internet_access.id}" ]
subnet_id = "..."
associate_public_ip_address = true
connection {
type = "ssh"
user = "ec2-user"
private_key = "${file("${var.private_key_path}")}"
}
provisioner "remote-exec" {
inline = [
"touch foo",
]
}
}
Error:
aws_spot_instance_request.worker01 (remote-exec): Connecting to remote host via SSH...
aws_spot_instance_request.worker01 (remote-exec): Host:
aws_spot_instance_request.worker01 (remote-exec): User: ec2-user
2017/09/01 16:17:52 [DEBUG] plugin: terraform: remote-exec-provisioner (internal) 2017/09/01 16:17:52 handshaking with SSH
aws_spot_instance_request.worker01 (remote-exec): Password: false
aws_spot_instance_request.worker01 (remote-exec): Private key: true
aws_spot_instance_request.worker01 (remote-exec): SSH Agent: true
2017/09/01 16:17:52 [DEBUG] plugin: terraform: remote-exec-provisioner (internal) 2017/09/01 16:17:52 handshake error: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
2017/09/01 16:17:52 [DEBUG] plugin: terraform: remote-exec-provisioner (internal) 2017/09/01 16:17:52 Retryable error: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
Working code:
resource "aws_instance" "worker01" {
ami = "ami-0cb95574"
instance_type = "m3.medium"
vpc_security_group_ids = [ "${aws_security_group.ssh_access.id}", "${aws_security_group.tcp_internal_access.id}","${aws_security_group.splunk_access.id}","${aws_security_group.internet_access.id}" ]
subnet_id = "..."
associate_public_ip_address = true
connection {
type = "ssh"
user = "ec2-user"
private_key = "${file("${var.private_key_path}")}"
}
provisioner "remote-exec" {
inline = [
"touch foo",
]
}
}
I have tried a few different iterations of the non-working code (including an silly attempt to hard-code a public ip for a spot instance and an attempted self-reference to the spot instances public ip - which gave an no such attribute error). Unfortunately, I could not find anyone with similar issues via google. From what I have read, I should be able to provision a spot instance in this manner.
Thanks for any help you can provide.
You need to add wait_for_fulfillment = true to your spot instance request or the resource will return before the instance is created.

Terraform Remote-Exec Provisioner Timeout

I'm creating a server in AWS using Terraform.
My remote-exec provisioner will not connect and execute, continually giving me the output:
aws_spot_instance_request.single_server_instance (remote-exec): Connecting to remote host via WinRM...
aws_spot_instance_request.single_server_instance (remote-exec): Host: 54.219.179.241
aws_spot_instance_request.single_server_instance (remote-exec): Port: 5985
aws_spot_instance_request.single_server_instance (remote-exec): User: Administrator
aws_spot_instance_request.single_server_instance (remote-exec): Password: true
aws_spot_instance_request.single_server_instance (remote-exec): HTTPS: false
aws_spot_instance_request.single_server_instance (remote-exec): Insecure: false
aws_spot_instance_request.single_server_instance (remote-exec): CACert: false
Before failing with:
Error applying plan:
1 error(s) occurred:
* aws_spot_instance_request.single_server_instance: 1 error(s) occurred:
* timeout
My Resource is as follows:
resource "aws_spot_instance_request" "single_server_instance" {
# The connection block tells our provisioner how to
# communicate with the resource (instance)
connection {
type = "winrm"
user = "Administrator"
password = "${var.admin_password}"
#insecure = true
#port = 5985
host = "${self.public_ip}"
#timeout = "5m"
}
wait_for_fulfillment = true
associate_public_ip_address = true
instance_type = "${var.aws_instance_type}"
ami = "${lookup(var.aws_amis, var.aws_region)}"
spot_price = "1.00"
vpc_security_group_ids = [
"${aws_security_group.WinRM.id}",
"${aws_security_group.RDP.id}"
]
key_name = "sdlweb85"
provisioner "remote-exec" {
inline = [
"mkdir c:\\installs"
#"powershell.exe Invoke-WebRequest -Uri 'https://www.dropbox.com/s/meno68gl3rfbtio/install.ps1?dl=0' -OutFile 'C:/installs/install.ps1'"
]
}
#provisioner "file" {
# source = "scripts/"
# destination = "c:/install_scripts/"
#}
user_data = <<EOF
<powershell>
# Configure a Windows host for remote management (this works for both Ansible and Chef)
# You will want to copy this script to a location you own (e.g. s3 bucket) or paste it here
Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1'))
# Set Administrator password
$admin = [adsi]("WinNT://./administrator, user")
$admin.psbase.invoke("SetPassword", "${var.admin_password}")
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
New-SelfSignedCertificate -DnsName "*.amazonaws.com" -CertStoreLocation "cert:\LocalMachine\My"
#winrm quickconfig -quiet
</powershell>
EOF
}
Security Groups
resource "aws_security_group" "WinRM" {
name = "WinRM"
# WinRM access from anywhere
ingress {
from_port = 5985
to_port = 5986
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# outbound internet access
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "RDP" {
name = "RDP"
# RDP access from anywhere
ingress {
from_port = 3389
to_port = 3389
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# outbound internet access
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
I cannot figure out the problem. From my local machine, I can connect to the remote server using
Enter-PSSession -ComputerName $ip -Credential ~\Administrator
Winrm HTTP is disabled by default on Amazon Windows AMI. You have to run a powershell command to enable it, ansible has a nice one to do it. You can create an AMI that has winrm enabled and use that one to launch spot instances.
You can also set the provisioner in terraform to HTTPS true
https://www.terraform.io/docs/provisioners/connection.html#https
Note that terraform uses Go WinRM which doesn't support https at this time.
I had to stick with the following:
user_data = <<EOF
<script>
winrm quickconfig -q & winrm set winrm/config #{MaxTimeoutms="1800000"} & winrm set winrm/config/service #{AllowUnencrypted="true"} & winrm set winrm/config/service/auth #{Basic="true"}
</script>
<powershell>
netsh advfirewall firewall add rule name="WinRM in" protocol=TCP dir=in profile=any localport=5985 remoteip=any localip=any action=allow
# Set Administrator password
$admin = [adsi]("WinNT://./administrator, user")
$admin.psbase.invoke("SetPassword", "${var.admin_password}")
</powershell>
EOF