INDEX
1. What is Terraform?
2. Prerequisites
3. What are we going to do?
4. Steps
4.1. Create CLI User
Access Key
4.2. Software Installation
Install AWS CLI
Install Terraform
4.3. Main Code
Provider
Creating the instance
Connecting from outside
RDP Connection
RDP: Security Groups
RDP: Key pair
Modified Instance
Output
Output: Public IP
Output: Credentials
5. Extras
5.1. Dynamic AMI
5.2. AWS Credentials on File
1. What is Terraform?
Terraform is an Open Source tool that allows you to generate an infrastructure through a declarative language. This is what is called Infrastructure as Code (IaC).
That is, it allows us to abstract from the environment in which this structure is going to be implemented, both for cloud environments (such as AWS, Azure, Google Cloud), and local environments (such as Virtualbox, Mikrotik RouterOS or Unifi).
2. Prerequisites
For this lab we will need:
- An AWS account that has been previously created
- A PC or Virtual Machine with Admin Permissions
3. What are we going to do?
Tasks that we are going to do in this lab are:
- Create a Programmatic User on AWS
- Installing Terraform in a Linux environment
- Know the use of different Terraform modules
- Initialize and Apply Settings in an AWS Account
4. Steps
4.1. Create CLI User
In order to make Terrraform connect to our AWS account, it will be needed to create a user with CLI permissions. In this lab we will create a user with Administrator permissions, which we will only be used for programmatic use.
We will connect to our AWS account, and we will access the IAM -> Users section
In this section we will click on Create user
We will fill the fields with a username (in our case we will use the user "usercli", and will leave “console access” option disabled, as this user will only be used for programmatic access.
In Permission groups, we'll need to set enough permissions to create resources. In our case we will create a new group with Administrator permissions.
To do this we will click on "Create group".
We will name this group (in our case we will use the name "admin"), and in permission policies we will associate the "AdministratorAccess" policy. Then click on "Create user group".
And in the Permissions window, select our new "admin" group
Finally we click on "Create User".
Access Keys
We have already created our new user. But it will be needed to generate an access code in order to be able to access this user.
To do this we select in the menu of IAM -> Users our user "usercli", and go to "Security credentials" tab
Now we will look for the "Access keys" section and click on "Create access key".
In the next menu, we will select the option "Command Line Interface (CLI)". This option will allow the user to access AWS account programmatically.
And we mark the security option
Finally, we will click on the "Create access key" button.
In the next section, we will see the key pairs that we will need later: "access key" and "secret key".
4.2. Software Installation
Install AWS CLI
Before you can use AWS on your computer, you will need to install the AWS CLI. In our case, as it’s an Ubuntu distribution, the following commands will be run:
apt-get install zip curl -y
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
Install Terraform
We will need to update our system and also install the following packages for its installation (gnupg, curl)
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
Once installed, we proceed to install the GPG key
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
Then we add the repository of Hashicorp and proceed to download the package information
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
Finalmente instalamos terraform
sudo apt install terraform
4.3. Main Code
Provider
The first thing we're going to need is to inform Terraform how to connect to AWS.
In our case, as we have created a user to connect by CLI, it will be the one we use. Specifically, we will need the "access_key" and "secret_key" of that user.
In addition to that data, it will also be needed to set the region in which we are going to deploy the resources.
provider "aws" {
region = "eu-west-1"
access_key = "USER_ACCESS_KEY"
secret_key = "USER_SECRET_KEY"
}
Creating the instance
Regarding the Windows instance, we will add the main lines; we will add more lines later.
resource "aws_instance" "windows-server" {
ami = “ami-04f19c2d332c17a0c”
instance_type = “t2.micro”
associate_public_ip_address = true
}
ami: Indicates the distribution image that we want to add to the instance. In our example, the ID belongs to a Windows Server 2012 R2.
instance_type: Indicates the hardware on which that instance will work. In this example we have chosen the instance "t2.micro" since it is "free tier" (that is, free up to 750 hours of use for a year if you have a new account)
associate_public_ip_address: This will allow us to associate a public IP to the instance, to connect from outside.
Connecting from outside
With the previous configuration we will create a Windows 2012 R2 instance, but we will not be able to connect to. That’s why we will add some more options.
RDP Connection
Apart from an IP address, we will need a protocol to connect to that instance. In this lab we will choose to connect by RDP (Remote Desktop Protocol).
RDP: Security Groups
Remember that Security Groups are permissions that we grant to the instances to control the incoming and outgoing traffic of the same.
ingress: In our case, we need to enable inbound access to port 3389, which is the usual port of the RDP protocol. We will also accept any IP, so in our cidr block we use any range "0.0.0.0/0"
egress: As outgoing access we will accept any protocol, port and IP.
# Security Groups
####################################################
# Define the security group for the Windows server
resource "aws_security_group" "sg-windows" {
name = "windows-sg"
description = "Allow incoming connections"
ingress {
from_port = 3389
to_port = 3389
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow incoming RDP connections"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
RDP: Key pair
En el caso de instancias Windows, es necesario una pareja de claves (key pair). Esta clave permitirá desencriptar la contraseña de administrador. Una vez desencriptada, podremos utilizarla para poder conectarnos a la instancia.
tls_private_key: Utilizaremos este recurso para generar una clave privada. Definiremos el uso del algoritmo RSA para esta clave
aws_key_pair: En esta sección crearemos el par de claves para controlar el acceso a la instancia. Utilizaremos la clave privada creada previamente para crearla.
# Private Key for Credentials
####################################################
resource "tls_private_key" "instance_key" {
algorithm = "RSA"
}
resource "aws_key_pair" "instance_key_pair" {
key_name = "windows-instance-key"
public_key = tls_private_key.instance_key.public_key_openssh
}
Once the instance is created, we can decrypt the Administrator password. To do this, we will decrypt it under code, and store it in an AWS SSM Parameter Store.
¿What is SSM Parameter Store?
It is a capability of AWS System Manager, which provides safe, hierarchical storage for managing configuration data and secrets.
You can store data such as passwords, database strings, Amazon Machine Image IDs (AMIs), and license codes as parameter values. You can also store values as plain text or as encrypted data.
The main command that allows to decrypt the password is:
rsadecrypt(aws_instance.windows-server.password_data, nonsensitive(tls_private_key.instance_key.private_key_pem))
tls_private_key.instance_key.private_key_pem: This is the private key previously created in PEM format.
nonsensitive(SENSITIVE_TEXT) function: The previous private cell is a sensitive element, so it will not allow us to use it. This Terraform feature let us return a copy of that key by removing the "sensitive" mark.
aws_instance.windows-server.password_data: Once the instance is created , this argument points to the encrypted password of the instance.
rsadecrypt(CIPHERTEXT, PRIVATEKEY) function: Decrypts a ciphertext in RSA and returns it in plain text.
# Store Password
####################################################
resource "aws_ssm_parameter" "windows_ec2" {
depends_on = [aws_instance.windows-server]
name = "/Instances/windows/windows-password"
type = "SecureString"
value = rsadecrypt(aws_instance.windows-server.password_data, nonsensitive(tls_private_key.instance_key.private_key_pem))
}
Terraform Dependencies
Most of the time, Terraform infers dependencies between resources based on the configuration given, so that resources are created and destroyed in the correct order. This is automatically done, and it's named "implicit dependencies".
Occasionally, however, Terraform cannot infer dependencies between different parts of your infrastructure, and you will need to create a manual explicit dependency with the depends_on argument
Modified instance
We will finally modify our instance creation resource to use these extra resources that we have added.
# Create EC2 Instance
resource "aws_instance" "windows-server" {
# Instance info
ami = “ami-04f19c2d332c17a0c”
instance_type = "t2.micro"
# Public IP
associate_public_ip_address = true
# Instance Credentials
key_name = aws_key_pair.instance_key_pair.key_name
get_password_data = true
# Security Group
vpc_security_group_ids = [aws_security_group.sg-windows.id]
}
Output
Once we have all the resources created, there is certain data that we will require to connect to that instance. For this we will use in this example two different ways to display this information
Output: Public IP
We will need the public IP to be able to connect. That is why we will show it to us using the "output" module. This information will be displayed on the screen after you finish creating the instance.
# Windows Public IP
output "windows_public_ip" {
value = aws_instance.windows-server.public_ip
}
Output: Credentials
We had stored the credentials in a Parameter Store, so we are going to export that information in a local file, in the same directory where we launch the execution.
filename: Name of the destination file
content: What you are going to export; in our case, the credentials
# Export Credentials to File
resource "local_file" "RDP_key" {
filename = "windows_key.txt"
content = aws_ssm_parameter.windows_ec2.value
}
5. Extras
5.1. Dynamic AMI
In AWS, you need to know the ID of the image you want to use (or AMI) in order to create an EC2 instance.
This ID changes depending on the version of the distribution, region and even language. That is why it would be necessary to previously know the exact identification of the image to apply it to our code.
But Terraform has a module that will help us in this task. The "Data" module allows you to connect to your AWS account and perform a query with the parameters that we specify.
In our case, we will indicate that we want the Spanish distribution of Windows 2012 R2, and we will specify that we want the latest version.
data "aws_ami" "windows-2012-r2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["Windows_Server-2012-R2_RTM-Spanish-64Bit-Base-*"]
}
}
And we will replace in the instance creation module the value of the AMI with the new value obtained from this new module "aws_ami"
# Create EC2 Instance
resource "aws_instance" "windows-server" {
# Instance info
ami = data.aws_ami.windows-2012-r2.id
instance_type = "t2.micro"
# Public IP
associate_public_ip_address = true
# Instance Credentials
key_name = aws_key_pair.instance_key_pair.key_name
get_password_data = true
# Security Group
vpc_security_group_ids = [aws_security_group.sg-windows.id]
}
5.2. AWS Credentials on File
We had originally defined the provider module with the "access key" and "secret key" keys of the user "usercli" that we created in AWS.
So credentials can be seen in code, so in case of export it, we would generate a security problem.
The AWS CLI has a directory in which we can store credentials. By default it is usually located in "~/.aws/config" in Linux, and "C:\Users\USERNAME\. aws\config" on Windows.
That’s why we will edit the file (or create the file if it is not found), and assign the profile "default".
# nano /home/USER/.aws/config/credentials
[default]
aws_access_key_id = ACCESS_KEY
aws_secret_access_key = SECRET_ACCESS_KEY
And we will modify the Terraform provider module so that we will point to the location of those credentials
provider "aws" {
region = "eu-west-1"
shared_credentials_files = ["/home/USER/.aws/credentials"]
profile = "default"
}
6. I want the code
You can find the whole code in the following repository:
Code
¿Te ha gustado?
Si te ha gustado y quieres aportar tu granito de arena para que esta comunidad crezca: