Can't SSH into EC2 instance created with Terraform - amazon-web-services

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.

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.

When I am running terraform apply

I am creating ec2 instance and this is my main.tf file
variable "aws_key_pair" {
default = "~/aws/aws_keys/terraform-ec2.pem"
}
provider "aws" {
region = "us-east-1"
version = "~>2.46"
}
resource "aws_security_group" "http_server_sg" {
name = "http_server_sg"
vpc_id = "vpc-c5f40fb8"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
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"]
}
tags = {
name = "http_server_sg"
}
}
resource "aws_instance" "http_server" {
ami = "ami-0947d2ba12ee1ff75"
key_name = "terraform-ec2"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.http_server_sg.id]
subnet_id = "subnet-1169c14e"
connection {
type = "ssh"
host = self.public_ip
user = "ec2_user"
private_key = file(var.aws_key_pair)
}
provisioner "remote_exec" {
inline = [
"sudo yum install httpd -y",
"sudo service httpd start",
"echo Welcome to virtual server setup by terraform , IP address ${self.public_dns} | sudo tee /var/www/html/index.html"
]
}
}
When I am running : terraform apply I am getting following error
Could not load plugin
Plugin reinitialization required. Please run "terraform init".
Plugins are external binaries that Terraform uses to access and manipulate
resources. The configuration provided requires plugins which can't be located,
don't satisfy the version constraints, or are otherwise incompatible.
Terraform automatically discovers provider requirements from your
configuration, including providers used in child modules. To see the
requirements and constraints, run "terraform providers".
Failed to instantiate provisioner "remote_exec" to obtain schema: unknown
provisioner "remote_exec"
But I have already done terraform init and when I am running terraform validate I am getting same above error
It's "remote-exec" ...

I can't SSH onto EC2 instance created using Terraform

I hope everyone that sees this is doing well.
I'm still learning the ropes with Terraform and AWS.
I've created a VPC with 4 subnets in it. 1 subnet is public and the other 3 are private. I currently have 1 EC2 instance in my public subnet (a bastion box/server). I have also created a security group for this instance and have created a NACL rule that allows me to connect via ssh to this instance from my IP only. For some reason when I try to ssh onto this instance my terminal hangs and I see the following message:
OpenSSH_8.2p1 Ubuntu-4ubuntu0.1, OpenSSL 1.1.1f 31 Mar 2020
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to 'instance_public_ip [instance_public_ip] port 22
and then it tells me the connection timed out.
I changed the rule to allow an ssh connection from all IPs (i.e. 0.0.0.0/0) but still get the same problem. The terraform code for the infrastructure is as follows:
# Elastic IP for bastion server
resource "aws_eip" "bastion_eip" {
instance = aws_instance.Bastion.id
vpc = true
}
# EIP association for bastion server
resource "aws_eip_association" "eip_assoc" {
instance_id = aws_instance.Bastion.id
allocation_id = aws_eip.bastion_eip.id
}
# Create internet gateway
resource "aws_internet_gateway" "main-gateway" {
vpc_id = aws_vpc.main-vpc.id
tags = {
Name = "main"
}
}
# Create route table for public subnet
resource "aws_route_table" "public-route-table" {
vpc_id = aws_vpc.main-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main-gateway.id
}
tags = {
Name = "public-route-table"
}
}
# Create subnet 4
resource "aws_subnet" "subnet-4" {
vpc_id = aws_vpc.main-vpc.id
cidr_block = "10.0.4.0/24"
availability_zone = "eu-west-2a"
tags = {
Name = "subnet-public"
}
}
# Associate subnet 4 with public route table
resource "aws_route_table_association" "subnet-4" {
subnet_id = aws_subnet.subnet-4.id
route_table_id = aws_route_table.public-route-table.id
}
# Create bastion server security group (subnet 4)
resource "aws_security_group" "bastion-sg" {
name = "bastion-sg"
description = "Allow web traffic from specific IPs"
vpc_id = aws_vpc.main-vpc.id
# SSH Traffic
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] #allow web traffic.
}
egress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "allow_access_bastion_server"
}
}
# Create NACL for public subnet with Prod server & bastion server
resource "aws_network_acl" "public_nacl" {
vpc_id = aws_vpc.main-vpc.id
subnet_ids = [aws_subnet.subnet-4.id]
# Allow inbound http traffic from internet
ingress {
protocol = "tcp"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 80
to_port = 80
}
# Allow outbound http traffic to internet
egress {
protocol = "tcp"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 80
to_port = 80
}
# Allow inbound SSH traffic from specific IP
ingress {
protocol = "tcp"
rule_no = 103
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 22
to_port = 22
}
# Allow outbound SSH traffic from specific IP
egress {
protocol = "tcp"
rule_no = 103
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 22
to_port = 22
}
tags = {
Name = "public NACL"
}
}
# Create bastion box
resource "aws_instance" "Bastion" {
ami = var.ami-id
instance_type = var.instance-type
key_name = "aws_key_name"
vpc_security_group_ids = ["security_group_id"]
subnet_id = "subnet_id"
tags = {
Name = "Bastion Server"
}
}
I've been looking at this a while now and can't really see where I've gone wrong. Is the issue with my security group or my IGW or route table? If there's any other information you feel is needed let me know :) and thanks for any help in advance
I think the problem is on the security group.
# SSH Traffic
ingress {
description = "SSH"
from_port = 0 # SSH client port is not a fixed port
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] #allow web traffic. 46.64.73.251/32
}
egress {
from_port = 22
to_port = 0 # SSH client port is not a fixed port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

Why can't Terraform SSH into my EC2 instance?

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.

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