Creation of Secure Web Server Using Ansible


Deploying a secure and scalable web server shouldn't be a mystery or a manual process.
Whether you're a DevOps enthusiast, a student building cloud skills, or an engineer tired of repetitive tasks, automation is your best friend.
In this walkthrough, we'll use Ansible to automate the creation of an AWS EC2 instance and set up a secure web server running Nginx - all without clicking through the AWS console or manually configuring a thing. If you’ve ever thought “there must be a better way”, this guide is for you.
🟢 Why This Is Needed:
Manually launching EC2 instances and configuring them can quickly become error-prone and inefficient, especially when you're managing multiple environments or need to redeploy often. With Ansible, you can turn that entire workflow → provisioning infrastructure, securing users, installing services, and even setting up firewall rules into repeatable code. This not only saves time but ensures your environments are consistent, secure, and ready for scaling.
Let’s start with our Automated Web Server
Creation of EC2 Instance
Installing Dependencies
sudo apt install python3-boto3
ansible-galaxy collection install amazon.aws
Set up Vault
openssl rand -base64 2048 > vault.pass
ansible-vault create group_vars/all/pass.yml --vault-password-file vault.pass
EC2 Creation Playbook
-
name: Create EC2 instance with termination protection turned on
hosts: localhost
connection: local # Tells ansible it runs on same device
tasks:
- name: Launch EC2 instance
amazon.aws.ec2_instance:
name: "my-ec2-instance"
key_name: "general-key-pair" # key present in aws
# vpc_subnet_id: "subnet-5ca1ab1e"
instance_type: "t2.micro"
security_group: default
region: "ap-south-1"
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}" # From vault as defined
image_id: "ami-0e35ddab05955cf57"
network:
assign_public_ip: true
Run the playbook
ansible-playbook ec2_insatnce.yml --vault-password-file vault.pass
Create Folder Structure
ansible-galaxy init secure-web-server-project
Create Inventory and Main Playbook
[all]
ubuntu ansible_host=52.66.244.170 ansible_user=ubuntu
[all:vars]
ansible_ssh_key=/home/dakshsawhneyy/dakshsawhneyy/general-key-pair.pem
-
name: Secure Web Server Setup
hosts: all
become: true
roles:
- secure-web-server
Run tasks inside tasks/main.yml
Create User
- name: Create deploy user
ansible.builtin.user:
name: daksh
shell: /bin/bash # specify to use which shell
groups: sudo # add it to root user permission group
create_home: true #create home directory
state: present
We have created user because ec2 has default ubuntu so to login using that user, we need key which we going to generate inside files/
Now creating SSH key to SSH into deploy user
Inside /files
ssh-keygen -t rsa -b 2048 -f deploy_key
Create .ssh directory
- name: Create .ssh directory
ansible.builtin.file:
path: /home/daksh/.ssh
state: directory
owner: daksh
group: daksh
mode: '0700' #Make it super secure
Adding SSH_Key to user
- name: Copy public key to the server
ansible.builtin.authorized_key:
user: daksh
state: present
key: "{{ lookup('file', 'files/daksh_key.pub') }}"
This is very useful as it reduces manual effort to copy ssh into home directory of user to ssh into that by automating it and copying it by himself, just need public key file and user name
Now we can easily ssh into the instance
ssh -i secure-web-server/files/daksh_key daksh@52.66.244.170
Now Updating system and Installing NginX
- name: Update system
ansible.builtin.apt:
update_cache: yes
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Start Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
Copy index.html to /var/www/html
- name: copy index.html to server
ansible.builtin.copy:
src: files/index.html
dest: /var/www/html/index.html
Allowing ports using UFW
- name: Allow HTTP port 80
ansible.builtin.ufw:
rule: allow
port: 80
proto: tcp
- name: Allow HTTP port 443
ansible.builtin.ufw:
rule: allow
port: 443
proto: tcp
- name: enable ufw firewall
ansible.builtin.ufw:
state: enabled
Add a CronJob for scheduling updates
- name: Schedule daily update with cron
ansible.builtin.cron:
name: "Daily update"
minute: 0
hour: 2
job: "apt-get update -y && apt-get -y upgrade"
user: root
Delete or Terminate EC2 - Playbook
-
name: Create EC2 instance with termination protection turned on
hosts: localhost
connection: local # Tells ansible it runs on same device
tasks:
- name: Launch EC2 instance
amazon.aws.ec2_instance:
name: "my-ec2-instance"
key_name: "general-key-pair" # key present in aws
# vpc_subnet_id: "subnet-5ca1ab1e"
instance_type: "t2.micro"
security_group: default
region: "ap-south-1"
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}" # From vault as defined
image_id: "ami-0e35ddab05955cf57"
network:
assign_public_ip: true
Subscribe to my newsletter
Read articles from Daksh Sawhney directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Daksh Sawhney
Daksh Sawhney
Aspiring DevOps & DevSecOps Engineer | Automating Infra with Terraform & Ansible | Kubernetes Enthusiast | Building Scalable Pipelines 🤷♂️