Motivation
From time to time I really need linux server with fresh public IP address without any firewall rules in any direction.
Once I need to upload/download something via SFTP/SCP.
Then I need to scan something and want be sure, that my ISP doesn’t block some communication for good reasons…
It should be fresh, raw, unfiltered, isolated, temporary virtual machine in a wild internet.
And as usually I want it quick and without any mental overhead:)
Security disclaimer
This setup is intentionally insecure by design. Never reuse these patterns for production or long-running servers. Use freely, if you know what you are doing;) Do not forget always to destroy VM after use
SecureCRT integration
I’ve already wrote about using SecureCRT as my personal automation controller in my previous post, so without repeating myself:
- I have a buttons “Start SCP” and “Destroy SCP” in SecureCRT
- Each button has calls its “controller” python script
- “controller” script then call my executive python script (inside its venv and with its libraries)
Controller script is very simple. It just open new terminal and call my executive script:
import subprocess
import os
cmd = "/Users/stefanbezo/Documents/Dev/SCRT_2025/.venv/bin/python /Users/stefanbezo/Documents/Dev/SCRT_2025/SCP/init_scp_tf.py"
osascript_command = f'''
tell application "Terminal"
activate
do script "{cmd}"
end tell
'''
subprocess.Popen(["osascript", "-e", osascript_command])
Main executive python script
Executive script is much more interesting. But first structure of folder where scripts are placed.
📁 SCP
├── 📄 .gitignore
├── 📄 init_scp_controller.py
├── 📄 destroy_scp_controller.py
├── 📄 init_scp_tf.py
├── 📄 destroy_scp_tf.py
├── 📄 last_instance_ip.txt
├── 📄 main.tf
└── 📄 variables.tf
📄 .env
📄 .key-pair-2025.pem
Where .env holds sensitive information for AWS and will be loaded during script runtime.
RSA key is already placed in AWS and file .key-pair-2025.pem will be used for programatic connection to new VM after creation.
The executive script init_scp_controller.py does following:
- Load enironment variables
- call Terraform (like manually “terraform apply”)
- gets new public IP from AWS
- connects to new VM
- Configure new user and SFTP/SCP service on new VM
- Prints all important info
Here is full working executive python script:
import subprocess
from dotenv import load_dotenv
import os
import time
import paramiko
load_dotenv("/Users/stefanbezo/Documents/Dev/SCRT_2025/.env")
# Copy current environment
env = os.environ.copy()
new_password = os.getenv("new_password")
# Path to your Terraform working directory
tf_dir = "/Users/stefanbezo/Documents/Dev/SCRT_2025/SCP"
# Initialize Terraform - just first time
# subprocess.run(["terraform", "init"], cwd=tf_dir, check=True, env=env)
# Apply
subprocess.run(["terraform", "apply", "-auto-approve"], cwd=tf_dir, check=True, env=env)
# Configure SCP
# AWS CLI command
cmd = [
"aws", "ec2", "describe-instances",
"--query", "Reservations[].Instances[] | sort_by(@, &LaunchTime)[-1].PublicIpAddress",
"--output", "text"
]
result = subprocess.run(cmd, env=env, capture_output=True, text=True, check=True)
host = result.stdout.strip()
time.sleep(20)
user = "ubuntu"
new_user = "transfer"
key = "/Users/stefanbezo/Documents/Dev/SCRT_2025/.key-pair-2025.pem"
commands = [
f"echo 'PasswordAuthentication yes' | sudo tee /etc/ssh/sshd_config.d/*.conf",
f"echo 'HostKeyAlgorithms +ssh-rsa' | sudo tee -a /etc/ssh/sshd_config.d/*.conf",
f"echo 'PubkeyAcceptedAlgorithms +ssh-rsa' | sudo tee -a /etc/ssh/sshd_config.d/*.conf",
f"sudo service ssh restart",
f"sudo useradd -m -s /bin/bash {new_user}",
f'echo "{new_user}:{new_password}" | sudo chpasswd',
f"sudo usermod -aG sudo {new_user}"
]
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host, username=user, key_filename=key)
for cmd in commands:
stdin, stdout, stderr = client.exec_command(cmd)
print(stdout.read().decode(), stderr.read().decode())
client.close()
print(f"***********************************************************************")
print(f"You can access new server by: \nssh -i /Users/stefanbezo/Documents/Dev/SCRT_2025/.key-pair-2025.pem {user}@{host}\n")
print("Upload: ")
print(f"scp /Users/stefanbezo/Downloads/some_file {new_user}@{host}:/home/{new_user}/\n")
print("Download: ")
print(f"scp {new_user}@{host}:/home/{new_user}/some_file ./\n")
print(f"current password for user '{new_user}' is '{new_password}'")
Terraform
And now Terraform part. It was eventually first part of whole workflow and I used it for manual terraform apply and terraform destroy actions.
Terraform variables defines only access key to AWS for service account and load them from running evironment variables (loaded by python script before). Nothing special:
variable "aws_access_key_id" {
description = "AWS Access Key ID"
type = string
}
variable "aws_access_key_access_key" {
description = "AWS Access Key Secret"
type = string
}
.env file should contains access key in following form:
TF_VAR_aws_access_key_id=your-access-key-id
TF_VAR_aws_access_key_access_key=your-access-key
And finally terraform.main without any necessary complications.
Just defining:
- Provider, region, credentials
- necessary networking (new isolated VPC, Subnet, CIDR, Gateway, routing)
- some quite open policy (This is why I usually need it)
- Instructions for new VM (instance type, request public IP, use RSA keypair…)
provider "aws" {
region = "eu-central-1"
access_key = var.aws_access_key_id
secret_key = var.aws_access_key_access_key
}
resource "aws_vpc" "Sandbox" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "Sandbox-VPC"
}
}
resource "aws_subnet" "mgmt_subnet" {
vpc_id = aws_vpc.Sandbox.id
cidr_block = "10.10.1.0/24"
availability_zone = "eu-central-1a"
tags = {
Name = "mgmt_subnet-eu-central-1a"
}
}
resource "aws_security_group" "Sandbox-Security-Group" {
name = "Sandbox-Security-Group"
description = "Security group for Sandbox VM"
vpc_id = aws_vpc.Sandbox.id
tags = {
Name = "Sandbox-Security-Group"
}
ingress {
from_port = 0
to_port = 0
protocol = "All"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "All"
cidr_blocks = ["0.0.0.0/0"]
}
}
# Create an Internet Gateway
resource "aws_internet_gateway" "Sandbox-Internet-Gateway" {
vpc_id = aws_vpc.Sandbox.id
tags = {
Name = "Sandbox-Internet-Gateway"
}
}
# Create a Route Table for the VPC
resource "aws_route_table" "Sandbox-Route-Table" {
vpc_id = aws_vpc.Sandbox.id
tags = {
Name = "Sandbox-Route-Table"
}
}
# Add a Route to the Internet Gateway in the Route Table
resource "aws_route" "Sandbox-Route" {
route_table_id = aws_route_table.Sandbox-Route-Table.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.Sandbox-Internet-Gateway.id
}
# Associate the Management Route Table with the Subnet
resource "aws_route_table_association" "Sandbox-Route-Table-Association" {
subnet_id = aws_subnet.mgmt_subnet.id
route_table_id = aws_route_table.Sandbox-Route-Table.id
}
resource "aws_instance" "Sandbox" {
ami = "ami-03250b0e01c28d196"
instance_type = "t2.micro"
subnet_id = aws_subnet.mgmt_subnet.id
security_groups = [aws_security_group.Sandbox-Security-Group.id]
availability_zone = "eu-central-1a"
associate_public_ip_address = true
key_name = "key-pair-2025"
tags = {
Name = "Sandbox"
}
lifecycle {
ignore_changes = [
security_groups,
]
}
metadata_options {
http_tokens = "required" # Enforce IMDSv2
http_put_response_hop_limit = 1 # Limit metadata access to the instance itself
http_endpoint = "enabled"
}
}
And in less than 30 seconds I get exactly following shell:

Cleanup
Do not forget to create also “terraform destroy” part and use it ! ;)