Alright, let’s see how my
last home server video did... Woah. Okay. I guess you guys really liked it then! No but seriously, the response
to that video has been amazing, And I cannot thank you all
enough for all of your support. And if I got one thing clear from your reactions, It’s that a lot of you guys wanted to see a
deep dive into my infrastructure as code set up. Because of that, I decided to make a series
of videos with a working title IaC Deep Dive, In which we are going to create
an Infrastracture as Code project To set up a home server from scratch. In this video, which is a part one, We’re going to go through basics of
Ansible and write our first little playbook To automate the initial
configuration for a Linux machine. This video is geared towards complete beginners, people who don’t know anything about Ansible, but have some knowledge of Linux,
command line and shell scripting. If you’re already familiar with Ansible, Feel free to skip this part And make sure to subscribe, so
that you don’t miss part two. So, without further ado, let’s get started! *1. What is Ansible?* Ansible is an orchestration and
automation tool for Linux, Windows And BSD-based operating systems. It is primarily used to automate deploying,
provisioning and setting up multiple machines, But we can also use it to
automate one home server, Since it makes your configuration easily
reproducible, idempotent and resilient. If you want to know more about why Ansible
and Infrastructure as Code are so cool, Check out my previous video here,
especially the part about IaC. *2. Requirements* You will pretty much only need
one thing for this project, And that is a server that
we’re gonna be configuring. I’ll use a virtual machine for that And you can do the same if you
don’t have a spare computer. I’m using Fedora Linux as my
target OS today instead of Ubuntu, Just to show you that Ansible
works for pretty much any distro. You should also have access to your server via SSH And have a non-root user set up during
the installation with sudo privileges. Ubuntu server enables sudo
access for your user by default, And on Fedora Server this can be
configured during the installation. And that’s pretty much it. First things first, we need to install
Ansible, and that is pretty simple. I’m on Mac, so I’ll use the Homebrew package
manager and type `brew install ansible`. If you’re on Linux, you can use
your distro’s package manager, so for example, on Debian-based distros
it’s `sudo apt install ansible`. Once that’s done, let’s create
a folder called "homeserver". That’s gonna be our project folder. *3. Project structure and inventory* Now let’s move on to the structure of our project. Ansible has a pretty rigid structure when
it comes to projects (or “playbooks”), and some terms like “role”, “inventory”
and “playbook” can definitely be confusing. So let’s see what they’re all about. First thing we need to do in an Ansible project Is tell Ansible what machines
we’re gonna be working on. For that, we need to create
a so called “inventory”. Let’s create a file called “hosts” and edit it. Here, we need to create a
group, I’m gonna call it “home”, And in the group, we’re gonna create our host. The way a host is declared is
basically the name of the host, In my case i’ll call it “homeserver”, Then its IP address or
hostname, I’m gonna put mine... Then the username that Ansible
will use to connect to our server, In my case it's "notthebee"... Then `ansible_connection=ssh`... And finally, the private key file. This is going to let us save the effort
of manually typing out the SSH password Every time we do something with our server If you don’t have an SSH key set up
yet, let me show you how to do it. Let’s open a terminal tab and type
`ssh-keygen -o -a 100 -t ed25519` (this is the hip new SSH cypher, by the way) then “-f”, the path to the key,
`-C`, and your email address. Then let’s press Enter, make the
password empty by pressing Enter again. Now you can type `ssh-copy-id -i path-to-the-key`, then your username, at, the address of the server. And that’s it! Now let’s test the key to make
sure everything works... good! One more thing we need to do
is create an `ansible.cfg` file And point it towards our inventory file. Let’s create a file, create
a group called “[defaults]” and write INVENTORY = hosts. I’m also gonna create a group
called “[ssh_connections]” And write “pipelining = true”. This is going to speed up the
Ansible commands considerably. Now that we’ve put our host into the inventory, We need to test the connection,
so that we know that Ansible is able to communicate with our host. For that, let’s go into the
directory with our inventory And type this command: `ansible
all -m ping`, and press “Enter”. We want to see “SUCCESS” here,
and that’s exactly what we see. *4. Tasks* Tasks are the bread and butter of Ansible. This is what your playbook is actually
going to do on your target hosts. So now we’re going to create our first task. Let’s create a folder called `tasks` and
create a file in there called `essential.yml`. Ansible uses a markup language called YAML, which relies heavily on
indentation, kind of like Python. If you’ve ever written a docker-compoe
file, you’ll feel right at home here. Our essential task should basically
be able to do the following things: Update the packages on our target system Install some required software,
like vim, tmux, htop and so on Give our login user paswordless sudo privileges And finally, harden the SSH configuration
and enable public-key only authentication Those are the basics of setting up a fresh server. You’ll probably have to do it regardless
of what you want to use the machine for, So I think it’s a good first
example. Let’s get started So the individual tasks in
Ansible have the following format `name: `, then what the task does.
Let’s call this one “Update packages” Then the name of the module.
Ansible has a module called `dnf` that we can use for managing and
installing packages on Fedora Then we indent the next line and
type `name: "*", as in "all packages" Next, we specify a parameter caled `state`. This is a pretty common one in
Ansible, and we want it to be `latest`. As in, “update all packages to the latest state”. For apt, the syntax would be a bit different. You’ll have to use `upgrade: yes`
and `update_cache: yes instead`. And that’s pretty much it! We just wrote
our first task. Let’s write another one. We’re gonna call this one
“Install essential packages”, and this task we’re gonna use
a module called `package`. It’s distro-agnostic and works for
pretty much all package managers. We’re gonna call this one
“Install essential packages”, then write `name:`, and here you can put all of
the packages that you want to have on your system. For example vim, htop, neofetch,
tmux and let’s say, speedtest-cli. Finally, let’s also type `state: latest`. *5. Variables* Now you might have noticed that we have
quite a lot of hardcoded values in our code. Username, list of packages to be installed All of those values are things that we might
need in many other places of our Ansible project, So hardcoding them everywhere
is probably a bad idea. Instead, we’ll use variables. There are two places you can put
your variables in an Ansible project. `group_vars` and `host_vars`. In `group_vars` you can store variables
that are limited to groups of hosts, which are these things in square
brackets that can group multiple hosts. And in `host_vars` you can store
variables for individual hosts. For example, if you use different
SSH keys for your hosts, You can put each of your keys
in the host variable file. For now though, we’re going to
create a folder called "group_vars", and here we’re going to
create a folder called "all". In this folder, we’re gonna
create a file called "vars.yml" The variables that we’re
going to store in that file Will apply to all hosts and all groups, But we can also override them for individual hosts By putting some different values
in the host variable file. Here, we’re going to specify our
username – mine is notthebee. And the list of packages that I want to
install, I’m gonna put that here as well. In the future, you might need to declare variables That shouldn’t be stored in plain text, Like passwords, API keys or access tokens. So we’re going to create another variable
file that will store encrypted variables. Let’s navigate to the `all`
folder that we just created, and type `ansible-vault create secret.yml`. It’s gonna ask you for a password twice, And then open a file where you
can put your secret variables. I’m gonna put my super secret password
here, and then hit `:x` to save the file. Now if we cat that file, you can see
that it’s just a bunch of gibberish. If you want to actually edit the file later, You’ll have to use the
command `ansible-vaule edit` And Enter the password that you
specified earlier. And that’s it Now that we have our variables, we need to actually use them in our task file. Let’s go back to it and replace all of
our hardcoded values with variables. In Ansible, variables have to be surrounded
by double curly braces and quotes. *6. More tasks* Now as you might remember, We have two more things to do on our target system Harden the SSH configuration
and enable passwordless sudo. In order to change values in
the sshd configuration file, We’ll use a module called `lineinfile`. This module replaces lines in
files according to the parameters. Let’s type `name: Disable SSH
password auth`, lineinfile: dest /etc/ssh/sshd_config Then we’re specify our regular expression, `^#PasswordAuthentication yes, and line (that’s what we want to replace
it with): `PasswordAuthentication no`. Last thing we need to do
is restart the SSH daemon. But we only want to restart
it if there were actually changes made in the SSH configuration file. So let’s write `register:
sshd_config` here at the end, That’s gonna create a variable called sshd_config. Next, we type `name: Restart
SSHD on config change`, `service`, `name` is `sshd`, `state: restarted`. And then we also write `when: sshd_config.changed` So that is only going to
execute this particular task If Ansible made changes to the config file. But before we restart SSHD, we also need
to enable passwordless sudo for our user. For that, we’re going to use `lineinfile` again, But this time, the destination
file is `/etc/sudoers`. Next, we also specify `state: present`, and the regular expression
is going to be `^%wheel`. And we need to replace it with this long line. Finally, we also need to validate the sudoers
file to make sure we didn’t mess it up. The reason why you’re supposed to use `visudo`
on Linux instead of editing the file directly, Is because an error in it can
prevent you from using sudo at all. And that is not good. So let’s write `validate: /usr/sbin/visudo -cf %s` This will validate our sudoers
file after the edit has been done, And revert the file in case
it doesn’t pass the check. *7. Playbook* So now that we’ve created our task
file, we also need to create a playbook. A playbook is basically a list of all the
tasks and roles that Ansible has to execute. For now we only have one task, But we will be writing more stuff
in the next parts of this series, So we need a playbook file. Let’s create a file called "run.yml"
in the root of the project. Here, we’re gonna put three dashes to
signify the beginning of a YML file. then another dash, `hosts: all`, `become: yes`. “Become” is basically “sudo”, this means that
Ansible will run the tasks as a sudo user. Now we write `tasks:`, indentation,
`- import_tasks: tasks/essential.yml`. And that’s pretty much it! Now
we’re ready to execute our playbook. Let’s exit the editor and type
`ansible-playbook run.yml -K —ask-vault-pass`. `-K` will ask us for the sudo password, and `--ask-vault-pass` will
ask us for the vault password. Thankfully, you can avoid
entering all of those passwords By enabling passwordless sudo, which we just did, And storing the vault
password in your OS keychain, But that’s a topic for another video. Let’s launch our playbook and see what happens! As you can see, all tasks
have been executed succesfully And the tasks that actually enacted
changes in the system were marked yellow. Let’s see what happens if we
execute the playbook again! This time around we can omit `-K`,
since we just enabled passwordless sudo, And as you can see, there were no changes made
to the system, since there’s nothing to change. So there you go, guys! In this video we wrote our first
Ansible playbook to automate the initial configuration for
your typical Linux machine! In the next part, we’re gonna go more in depth, and I’ll show you how to do
more complex things with roles, Use Ansible-Galaxy for
importing other users’ roles, How to store your Vault password in your
system keychain and other cool stuff! I hope you liked this video,
and if you want to see part two, make sure to subscribe so you don’t miss it. As usual at the end of this video
I’d like to thank my Patrons.