Webserver configuration on AWS EC2 using Ansible
Shubham Bhalala
Lead Data @DC DSLBD | MS Data Science @Columbia University | Data Science, MLOps Engineer
Moving towards industry 4.0 the drastic change we will see or we have to get ready to face will be automation on an industrial scale. So the basic needs of IT Infrastructure we see are resources and the hosting platform we use to make our idea public. One such is webserver which we commonly use. Today we will see how to configure apache webserver on AWS EC2 instance using ansible.
Ansible is not meant for provisioning operating systems, it's meant to configure systems. Tho ansible has a capability to provision it, this concept we have seen in terraform also. Terraform is not meant for configuring services, it's meant for managing cloud services. Tho it can configure services.
Moving towards building the code, let's understand the use case for it. For instance, let's say we have around 10 different servers running in different parts of the world. We need to deploy our web server and website on these servers so that the people in the near locality can get low latency in accessing the website. So, we need an intelligent script that goes to all the servers and on top of AWS it will provision EC2 and inside it, it will configure apache webserver and give us the unique URL which will be a public DNS name through which we can access our website. The main thing here will be we won't be doing anything manual even while writing code and won't go to AWS portal even once for doing anything.
We will create our workspace first, I have created this particular file system for this project to manage the things.
Here we see two files vars.yml and webserver.yml, vars.yml contains our AWS secret, and access key in encrypted form using ansible-vault and webserver.yml is the one which contains our main code. So, let's see how to write the Infrastructure as a Code.
vars.yml
To save passwords in clear text is not a good practice, hence we will save it in an encrypted format. To do so we will create a vault.
Using vim we can create a normal text file and save our password in a particular format as it's a YAML file.
Here awsaccess and awssecret are variables and their corresponding value. Here, the values which I have provided are not correct, obviously?? for security reasons. So, it will somewhat look like this.
Now we will encrypt it and save it in the vault.
Here, ansible-vault is the command, encrypt will tell vault to encrypt the file, --vault-id is to tell some user will be specified to use it shubham@prompt, shubham is the user, note: it's not compulsory to physically have shubham as a user it's just a name and prompt here means, it will prompt the user to insert password. Finally, we have to give the file name, for our case it's vars.yml.
Now if you check the content of the vars.yml file by cat or vim command, you will see encrypted data.
webserver.yml
The main code file where we tell the managed nodes and what all configuration we need to do on them. In our case, we are configuring our own system, hence we will give host as localhost. This means we are going to configure our own system or say controller node, if we have multiple nodes to be configured we can use the inventory file. To know more about these files and how to setup ansible visit my previous article and read about it here.
- name: Webserver on aws instance hosts: localhost vars_files:
- vars.yml
This is the start of the code where we mention the managed nodes as hosts. Then we have vars_files: this module helps us to import variables from other files.
tasks: - name: getting vpc subnet info ec2_vpc_subnet_info: aws_secret_key: "{{ awssecret }}" aws_access_key: "{{ awsaccess }}" register: vpc - debug:
var: vpc.subnets[0].subnet_id
Here we are using ec2_vpc_subnet_info: module because later we have to launch ec2 instance with public IP. To assign public IP we need a specific vpc subnet or else there we will face error and have to manually give subnet id. Using register we have saved the output and we retrieve and print it using the debug module. We have randomly picked the initial subnet of the three by default using vpc.subnets[0].subnet_id. We have used the aws_secret_key and aws_access_key using our vault.
- name: creating security group for webserver ec2_group: name: "webosansible" description: sg with ssh and http region: ap-south-1 aws_secret_key: "{{ awssecret }}" aws_access_key: "{{ awsaccess }}" rules: - proto: tcp from_port: 80 to_port: 80 cidr_ip: 0.0.0.0/0 - proto: tcp from_port: 22 to_port: 22 cidr_ip: 0.0.0.0/0
register: sgweb
Then we created a security group for our instance and we saved the output in sgweb variable as we will need the security group name in while creating an ec2 instance.
- name: creating key ec2_key: name: ansiblewebos region: ap-south-1 aws_secret_key: "{{ awssecret }}" aws_access_key: "{{ awsaccess }}" register: ec2key - name: saving key copy: content: "{{ ec2key.key.private_key }}" dest: "/ansible_code/aws-ec2/ansiblewebos"
mode: "0600"
Here, we are creating a key that helps us logging inside the instance in the future. Someone who is familiar with the AWS knows that we even have to save the key or else we won't be able to log in the OS using it. So, first, we will generate a key using ec2_key module then we will save it using copy module.
We have all the minimum requirements to launch an instance and configure it. Note: We haven't used anything manually here. Even if you want to create your own VPC use the following code. We don't need this because we have used ec2_vpc_subnet_info module to get the subnet info otherwise you need to create vpc for your own and save the output in a variable and use it while creating instance which will eventually help you get public IP.
- name: creating vpc ec2_vpc: aws_secret_key: "{{ awssecret }}" aws_access_key: "{{ awsaccess }}" state: present cidr_block: 172.22.0.0/16 region: ap-south-1 subnets: - cidr: 172.22.1.0/24 az: ap-south-2a - cidr: 172.22.2.0/24 az: ap-south-2b - cidr: 172.22.3.0/24 az: ap-south-2c internet_gateway: True route_tables: - subnets: - 172.22.2.0/24 - 172.22.3.0/24 routes: - dest: 0.0.0.0/24 gw: igw - subnets: - 172.22.1.0/24 routes: - dest: 0.0.0.0/24 gw: igw
register: vpc
Note: We don't need this if you are using my approach of using ec2_vpc_subnet_info or else you need to create vpc.
Now, let's see how to provision and configure at the same time.
- name: provisioning and configuring ec2 instance ec2: assign_public_ip: yes aws_access_key: "{{ awsaccess }}" aws_secret_key: "{{ awssecret }}" count: 1 group_id: "{{ sgweb.group_id }}" image: ami-0ebc1ac48dfd14136 instance_tags: webos: httpd instance_type: t2.micro vpc_subnet_id: "{{ vpc.subnets[0].subnet_id }}" wait: yes key_name: "{{ ec2key.key.name }}" region: ap-south-1 state: present user_data: | #!/bin/bash sudo yum install httpd php -y sudo systemctl start httpd sudo systemctl enable httpd
echo "Website created by Shubham Bhalala" >> /var/www/html/index.html
Here you see we have used vpc_subnet_id, key_name, group_id using the register values from the above code. The main thing here which we used to configure webserver is user_data using this we set the metadata of the instance.
Finally, we have to retrieve the public DNS name of the instance using which we will access the website.
- name: gathering instance info ec2_instance_info: aws_access_key: "{{ awsaccess }}" aws_secret_key: "{{ awssecret }}" filters: instance-state-name: running register: ec2instance - name: url to access website debug: var: ec2instance.instances[0].network_interfaces[0].association.public_dns_name
We have used ec2_instance_info for getting all the information about the ec2 instance which is in running state. Then using the info we collect we got the public_dns_name using json logic ec2instance.instances[0].network_interfaces[0].association.public_dns_name
It's the time to launch it and see what output we get and whether we are getting everything right or not.
we have added --vault-id shubham@prompt while running ansible-playbook because we are using a variable file which is locked by vault id and it will prompt to add the password which we have initially set while creating the vault. Finally, we can see it successfully executed and gave the URL through which we can access the website.
We can also check from the AWS portal that it's launched and running successfully.
Inside the code we have written a code for saving the key to our system, that's also been done and you can see it.
Thank you for reading this article! Please give a start to the GitHub repository if you like it.?
??GitHub: Link