Deploy a Web App with CodeDeploy
Project 5 of the 6 Day DevOps Challenge - Learn how to set up and use AWS CodeDeploy to deploy your web app to an EC2 instance!
Introduction
⚡️ 30 second Summary
Welcome to Day FIVE of the 6 Day DevOps Challenge!
Today, we're working with AWS CodeDeploy to automate the deployment of your web app.
You've used:
- ✅ VS Code and GitHub and to write and store your web app's code.
- ✅ CodeArtifact to secure web app's packages.
- ✅ CodeBuild to compile and package your app into a handy WAR file for deployment.
- ⏭️ Now, AWS CodeDeploy is here to deploy that file on web servers, so users can see your web app!
Get ready to:
- ☁️ Launch a deployment environment using AWS CloudFormation.
- ⚙️ Write deployment scripts to automate deployment commands.
- 🚀 Deploy your web app with CodeDeploy and see it live!
- 💎 Implement a disaster recovery technique - roll back a deployment!
Why am I learning about AWS CodeDeploy?
When you're developing software, you need a reliable way to release new versions of your app to the world.
AWS CodeDeploy is a continuous deployment service, which means it automates how you get new software versions onto your servers. Instead of manually moving files and restarting services yourself, CodeDeploy automatically runs a deployment using settings and commands that you define.
As part of a CI/CD pipeline, CodeDeploy makes software releases faster, more consistent, and way less stressful.
Wait, what's the 6 Day DevOps Challenge?
Ooooo, glad you asked 😸
In the 6 Day DevOps Challenge, you'll build from scratch a CI/CD pipeline that automates the build and deployment of a web app. This is a 100% hands-on challenge, so get ready to write documentation and add your work to your portfolio along the way (we'll show you how).
Want a complete demo of how to do this project, from start to finish? Check out our 🎬 walkthrough with Natasha 🎬
If you're up for a bit of a challenge, quiz yourself on the key concepts up ahead in this project.
This project is part of a series:
Before we start Step #1...
🌐 What is deployment?
Deployment is a process that takes your code from development to ➡️ a live environment where users can access it.
Deployment usually comes with many manual steps, like copying files to servers or installing depdencies. This can be time-consuming, error-prone, and difficult to reproduce consistently.
🤔 Then... What is AWS CodeDeploy?
AWS CodeDeploy is a continuous deployment service. This means CodeDeploy...
- Automates deployments: Eliminates error-prone manual steps - no more manually copying files and running commands to deploy your application.
- Enables consistent rollouts: Your application deploys the same way every time.
- Minimizes downtime: Can deploy in ways that keep your application available.
- Handles failures: Can automatically roll back if something goes wrong.
Have you completed Days #1-4 of the 6 Day DevOps Challenge?
Note
- DAY 1️⃣: Set Up a Web App in the Cloud
- DAY 2️⃣: Connect a GitHub Repo with AWS
- DAY 3️⃣: Secure Packages with CodeArtifact
- DAY 4️⃣: Continuous Integration with CodeBuild
Yes, and I've deleted my resources
Perfect! Welcome back to the challenge, and it's awesome to have you here again.
We're going to assume...
- You are doing this project with an IAM User.
- You have VS Code and the SSH extension installed.
- You have a GitHub repository storing the code from Days 1-4.
See you in 💻 Step 1!
Yes, and I kept all my resources
If you've completed all the previous projects and kept your resources, you can skip the setup steps.
Jump straight to 🚀 Step #4!! See you thereeeeee.
Nooo, I haven't done those projects!
Haven't tried those projects yet? That's okay! We still have the steps ready for you.
Get ready for some extra setup time in Steps #1-3 of this project :)
Set Up Your Development Instance
Let's get started by launching an EC2 instance! This will be our virtual server in the cloud where we'll write the code for our web app.
In this step, you're going to:
- Launch an EC2 instance using the AWS Management Console.
- Understand the benefits of using EC2 for this project.
- Troubleshoot common issues during instance launch.
- Let's dive right in! We've split up the setup for your development environment into four key milestones.
Note
If you've done projects #1-4 of the 6 Day DevOps Challenge, here's your mini challenge for this step... how many of these milestones can you check off without using the step by step guidance below?
- Launch an EC2 instance.
- SSH connect to your EC2 instance via VS Code.
- Install Maven, Java and Git in your EC2 instance.
- Clone your web app code on GitHub.
I haven't done Projects 1-4 yet
Then let's do it all step by step together! Ignore the other tabs, and stay right here.
Set up an IAM Admin User
- Do you have an IAM user?
No
Oooo it's the start of a new era!
If you don't have an IAM user yet - here are the steps to create one (this takes less than 10 mins).
What is an IAM user? Why are we setting one up?
In AWS, a user is a person or a computer that can do things on the AWS cloud.
When you create an AWS account for the first time, the login you get is called the root user of the AWS account. AWS actually recommends to not use your root user for everyday tasks to protect it from security breaches.
You should create IAM users instead. If a root user is a master key to your AWS account, think of IAM users as key copies. IAM users have separate usernames and passwords to your root user, and you can set them to have limited access to your account's resources.
- Head to your AWS Account as the root user.
- Open the AWS IAM console.
- From the left hand navigation panel, choose Users.
- Choose Create user.
- For the User name, name it:
[[YOURNAME="enter your name"]]-IAM-Admin
- Make sure to select the checkbox next to Provide user access to the AWS Management Console - optional.
Note
This does not apply to all accounts, but if you're prompted with a pop up panel that says Are you providing access to a person?, choose I want to create an IAM user.
- For the console password, choose Custom password.
- Type in a password that you will be able to remember/access in the future.
Top tip
You will use this password for all future projects, so make sure to choose a secure one!
- Deselect the checkbox for Users must create a new password at next sign-in - Recommended.
- Choose Next.
- In the permissions set up page, choose Attach policies directly.
- From the list of Permissions policies, select AdministratorAccess.
- Choose Next.
- Choose Create user.
- Voilà - you've just created your new user! Stay on this page.
- Choose Download .csv file.
- Copy the Console sign-in URL.
- Now you're ready to start using your IAM user. 🏁
- Log out of your root user's AWS Account.
- Paste and go to your copied console sign-in URL.
- Open your downloaded .csv file containing your user's access instructions.
- Log in using your IAM user's username and password in the .csv file.
- Once you're logged in, you're ready to use your IAM user for this project! Make sure to keep the login details safe - you'll need them for the entire 6 Day DevOps Challenge!
Yes
- Log in to the AWS Management Console with your IAM Admin User.
Note
PLEASE make sure you log in to your IAM Admin User instead of the root user - it's truly best practice for account security.
Launch an EC2 Instance
Before we get into the juicy work of building your web app, we need to set up a home for your web app's files.
Since we want your web app to be entirely created and run on the cloud, we'll use a virtual server (EC2 instance) to house our development work.
- Head to Amazon EC2.
- Switch your Region to the one closest to you.
Tip: We recommend using the following regions
Did you know that not all regions have the same number of AWS services available?
There are only 13 AWS Regions that provide ALL the services we'll use in the 6 Day DevOps Challenge. We'd recommend using one of these regions from the very start of the challenge, so all your resources are in the same place. Even if you don't live in these regions, you can still use them:
- us-east-1 (N.Virginia)
- us-east-2 (Ohio)
- us-west-2 (Oregon)
- eu-west-1 (Ireland)
- eu-west-2 (London)
- eu-central-1 (Frankfurt)
- eu-north-1 (Stockholm)
- eu-south-1 (Milan)
- eu-west-3 (Paris)
- ap-southeast-1 (Singapore)
- ap-southeast-2 (Sydney)
- ap-northeast-1 (Tokyo)
- ap-south-1 (Mumbai)
🙋♀️ Extra for Experts: How do you know only these regions have all the services we need?
By checking out AWS' guidance on services by region! Once you select a region, you can sift through the list and identify whether it has all the services you need.
- In your EC2 console, select Instances from the left hand navigation panel.
- Choose Launch instances.
- Set up your EC2 instance:
- In Name, enter the value: nextwork-devops-enter your name.
- Choose Amazon Linux 2023 AMI under Amazon Machine Image(AMI).
- Leave t2.micro under Instance type.
- Select Create a new key pair and use nextwork-keypair as your key pair's name.
- Store nextwork-keypair.pem in a new folder called DevOps in your local computer's Desktop.
- Back to our EC2 instance setup, head to the Network settings section.
- For Allow SSH traffic from, select the dropdown and choose My IP. This makes sure only you can access your EC2 instance. You can double check your IP by clicking here.
- Choose Launch instance.
Didn't see a success message?
Share any errors/questions with the NextWork community!
Install VS Code
Do you have VS Code installed on your computer?
No
Let's goooooo! We'll set up VS Code in just a few minutes, and learn why we use it along the way.
- Head to the Visual Studio Code website.
What is VS Code?
Visual Studio Code (VS Code) is one of the most popular tools for creating and managing coding projects. You'll often hear people call VS Code an IDE (Integrated Development Environment), which means software that help you write and edit code. It's similar to how Microsoft Word or Google Docs help you write documents!
VS Code also comes with extra tools that we'll use to connect to virtual servers like EC2 instances.
- Install VS Code by following the installation instructions for your OS e.g. Linux, Mac, Windows.
How can I decide which setting/chip option I should pick?
If you're unsure of which chip/settings option to pick for your device:
- Mac: Select the Apple icon from the top left hand corner of your computer's menu bar. Select About this Mac, and note whether your Chip says Apple or Intel.
- Windows: Click the Start button and search for System Information. Note whether your System Type says x64-based or ARM-based PC.
- Linux: Open a terminal and run uname -m. Note whether the output says x86_64 or aarch64/arm64.
- Once downloaded, you might need to unzip a zip file to access VS Code.
- Open VS Code in your local computer (you'll find it in your Downloads folder).
- If a popup asks you to confirm opening VS Code, select Open.
- Welcome to VS Code!
Yes
- Awesome! You're already set up to use VS Code.
- Open VS Code in your local computer.
- Select Terminal from the top menu bar.
- Select New Terminal from the dropdown.
What is a terminal?
A terminal is where you send instructions to your computer using text instead of clicks. For example, instead of right-clicking on your desktop to create a new folder, you can type a simple text command in your terminal instead. It's like sending text messages to your computer's operating system to tell it what to do.
Every computer has a terminal. On Windows, it's often called Command Prompt or PowerShell, while macOS and Linux systems use Terminal.
- Navigate your terminal to the DevOps folder:
- cd ~/Desktop/DevOps (Mac/Linux)
- cd C:\Users\YourUserName\Desktop\DevOps (Windows)
- Once you’re in the DevOps folder, you might want to check if your .pem file is there. Use ls (Mac/Linux) or dir (Windows).
Change the permissions of your .pem file:
In the terminal, run the following command to allow access to your .pem file.
Mac/Linux
chmod 400 nextwork-keypair.pem
What is chmod?
This command stands for "change mode", and it changes the permissions of your .pem file. Using 400 makes it readable only by you (the owner) and restricts access for everyone else.
We're changing the permissions of your .pem file so that you have access to it when you connect to your EC2 instance later. Blocking out everyone else keeps your .pem file i.e. your secret key secure.
Windows
icacls "nextwork-keypair.pem" /reset
icacls "nextwork-keypair.pem" /grant:r "[[USERNAME="enter your username"]]:R"
icacls "nextwork-keypair.pem" /inheritance:r
What is icacls?
Icacls (which stands for Integrity Control Access Control Lists) is a tool for Windows that lets you decide who can open or change the files on your system. In these icacls commands, you're using:
- /reset to remove default permission settings on the file
- /grant:r "USERNAME:R" to give the current user (that's you!) read access to your secret key
- /inheritance:r to make sure changes in the permissions of other files and the DevOps folder won't change the permission settings for this file.
- Make sure to replace "USERNAME" with your Windows username. If you don't know your username, run whoami in your terminal to find out.
Connect to your EC2 Instance
Let's use the terminal in VS Code to set up a 🔌 connection 🔌 to your EC2 instance. Once we're connected, we can work inside your EC2 instance to set up that web app.
- Head back to your AWS Management Console.
- Click on Instances from the left hand navigation panel.
- Click on the checkbox next to your EC2 instance to view its details.
- Under the Details tab, look for Public IPv4 DNS. You'll need this in the next instruction!
- Head back to VS Code and open your terminal again.
- Use the following command to connect to your EC2 instance:
ssh -i [[PEMPATH="PATH TO YOUR .PEM FILE"]] ec2-user@[[EC2="YOUR PUBLIC IPV4 DNS"]]
I'm getting an error!
Ah, classic! Many students have run into an error at this step, and we'll get you unstuck. Make sure there are no spaces in your folder names (e.g. the DevOps folder cannot be titled Dev Ops).
Still stuck? Share any other errors/questions with the NextWork community!
- Your terminal will ask if you want to continue connecting to this EC2 instance. Enter yes to continue connecting.
- Congrats! You've connected your EC2 instance via SSH.
Install Apache Maven and Amazon Corretto 8
Connection DONE. This means your terminal has now entered into your EC2 instance and can use it like a computer that's right in front of you!
Now let's install two tools that are going to help us build Java web apps. Introducing Apache Maven and Amazon Corretto 8 🥁
- Install Apache Maven using the commands below:
wget https://archive.apache.org/dist/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.tar.gz
sudo tar -xzf apache-maven-3.5.2-bin.tar.gz -C /opt
echo "export PATH=/opt/apache-maven-3.5.2/bin:$PATH" >> ~/.bashrc
source ~/.bashrc
What is Apache Maven?
Apache Maven is a tool that helps developers build and organize Java software projects. It's also a package manager, which means it automatically download any external pieces of code your project depends on to work.
We're also using Maven today because it's really useful for kick-starting web projects! It uses something called archetypes, which are like templates, to lay out the foundations for different types of projects e.g. web apps.
We'll use Maven later on to help us set up all the necessary web files to create a web app structure, so we can jump straight into the fun part of developing the web app sooner.
- Now we're going to install Java 8, or more specifically, Amazon Correto 8.
sudo dnf install -y java-1.8.0-amazon-corretto-devel
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-amazon-corretto.x86_64
export PATH=/usr/lib/jvm/java-1.8.0-amazon-corretto.x86_64/jre/bin/:$PATH
What is Java? What is Amazon Correto 8?
Java is a popular programming language used to build different types of applications, from mobile apps to large enterprise systems.
Maven, which we just downloaded, is a tool that NEEDS Java to operate. So if we don't install Java, we won't be able to use Maven to generate/build our web app today
Amazon Corretto 8 is a version of Java that we're using for this project. It's free, reliable and provided by Amazon.
💡 Woah! What's all this text popping up in the terminal?
The text you see after these commands is the terminal keeping you updated about it's progress with installing Java. It shows the specific packages it's going to install, downloading status, and even verifying that everything was installed.
- To verify that Maven is installed correctly, run mvn -v next. Make sure the output mentions a Maven version 3.5.x.
- To verify that you've installed Java 8 correctly, run java -version.
Important
If the Java version command above doesn't return openjdk version 1.8 (=> Java 8), run the following command that allows you to choose the correct Java version: sudo alternatives --config java
🙋♀️ Seeing error messages while installing Maven/Java?
Share any errors/questions with the NextWork community!
Create the Application
We've assembled both Maven and Java into our EC2 instance. Now let's cut straight to generating the web app!
- Use mvn to generate a Java web app. To do this, use these commands:
mvn archetype:generate \
-DgroupId=com.nextwork.app \
-DartifactId=nextwork-web-project \
-DarchetypeArtifactId=maven-archetype-webapp \
-DinteractiveMode=false
Break down these commands for me... What is mvn?
When you run mvn commands, you're asking Maven to perform tasks (like creating a new project or building an existing one).
The mvn archetype:generate command specifically tells Maven to create a new project from a template (which Maven calls an archetype). This command sets up a basic structure for your project, so you don't have to start from scratch.
Extra for Experts: Some of the details you've specified in this command are...
- -DartifactId=nextwork-web-project names your project
- -DarchetypeArtifactId=maven-archetype-webapp specifies that you're creating a web application.
- -DinteractiveMode=false runs the command without pausing for user input, so Maven will go ahead and install everything without waiting for your confirmation.
- Watch out for a BUILD SUCCESS message in your terminal once your application is all set up.
Connect VS Code with your EC2 Instance
In this step, you'll connect VS Code to your EC2 instance so you can see and edit the web app you've just created.
- If you don't have Remote - SSH installed in VS Code already, select the Extensions icon at the side of your VS Code window. Search for Remote - SSH and click Install for the extension.
Why are we installing Remote - SSH?
The Remote - SSH extension in VS Code lets you connect directly via SSH to another computer securely over the internet. This lets you use VS Code to work on files or run programs on that server as if you were doing it on your own computer, which will come in handy when we edit the web app in your EC2 instance!
- Click on the double arrow icon at the bottom left corner of your VS Code window. This button is a shortcut to use Remote - SSH.
- Select Connect to Host...
- Select + Add New SSH Host...
- Enter the SSH command you used to connect to your EC2 instance:
ssh -i [[PEMPATH="PATH TO YOUR .PEM FILE"]] ec2-user@[[EC2="YOUR PUBLIC IPV4 DNS"]]
I'm getting an error!
Ah, classic! Many students have run into an error at this step, and we'll get you unstuck. Make sure there are no spaces in your folder names (e.g. the DevOps folder cannot be titled Dev Ops).
Still stuck? Share any other errors/questions with the NextWork community!
- Select the configuration file at the top of your window. It should look similar to /Users/username/.ssh/config
- A Host added! popup will confirm that you've set up your SSH Host - yay!
- Select the blue Open Config button on that popup.
- Confirm that all the details in your configuration file look correct:
- Host should match up with your EC2 instance's IPv4 DNS.
- IdentityFile should match up to nextwork-keypair.pem's location in your local computer.
- User should say ec2-user
- Now you’re ready to connect VS Code with your EC2 instance.
- Click on the double arrow button on the bottom left corner and select Connect to Host again.
- You should now see your EC2 instance listed at the top.
- Select the EC2 instance and off we gooooooooooo to a new VS Code window ✈️
- Check the bottom right hand corner of your new VS Code window - it should show your EC2 instance's IPV4 DNS.
Now that VS Code is connected to your EC2 instance, let's open up your web app's files.
- From VS Code's left hand navigation bar, select the Explorer icon.
- Select Open folder.
- Enter /home/ec2-user/nextwork-web-project.
- Press OK.
- VS Code might show you a popup asking if you trust the authors of the files in this folder. If you see this popup, select Yes, I trust the authors.
- Check your VS Code window's file explorer again - a folder called nextwork-web-project is here!
- Try expanding all the subfolders in the file explorer. All folders have a > icon next to their name.
What are all these files and subfolders?
All the files and subfolders you see under nextwork-web-project are parts of a web app! You can start working right away on the content you want to display on your web app, since Maven's taken care of the basic structuring and setup.
Let's get to know some of these web app files/folders:
- The src (source) folder holds all the source code files that define how your web app looks and works.
- src is further divided into webapp, which are the web app's files e.g. HTML, CSS, JavaScript, and JSP files, and resources, which are the configuration files a web app might need e.g. connection settings to a database.
- pom.xml is a Maven Project Object Model file. It stores information and configuration details that Maven will use to build the project. We'll use pom.xml later in this project series!
- From your file explorer, click into index.jsp.
- Let's try modifying index.jsp by changing the placeholder code to the code snippet below. Don't forget to replace {YOUR NAME} from the following code with your name:
<html>
<body>
<h2>Hello [[YOURNAME="enter your name"]]!</h2>
<p>This is my NextWork web application working!</p>
</body>
</html>
- Save the changes you've made to index.jsp by selecting Command/Ctrl + S on your keyboard.
Let's connect our local web app to a remote repository on GitHub. This will let us track changes to our code and collaborate with others!
Install Git
To start using Git, we need to install it on your EC2 instance.
- Open a new terminal in VS Code (if you don't already have one open in the remote session) by selecting Terminal > New Terminal. This terminal is now connected to your EC2 instance.
- Run the following commands in the terminal to update the package list and install Git:
sudo dnf update -y
sudo dnf install git -y
Why install Git on the EC2 instance?
Git is a version control system that we'll use to manage our web app's code. We need to install Git on the EC2 instance so we can initialize a Git repository in our project directory, track changes, and push our code to GitHub in the next steps.
- Off we goooo! Git is installed - you should see a Complete! message like this:
- To check that Git was installed correctly, run the following command in the terminal:
git --version
- This command will show you the installed version of Git if it's installed correctly.
Nice, Git is installed! You're ready to track the changes you make to your web app. We're also going to set up a remote repository on GitHub, so your web app code is also stored in the cloud.
Set up your GitHub repository
- Do you have a GitHub account?
Yes - I'm ready to go!
- Log in to your GitHub account.
No - I need to set up a GitHub account
Oooo exciting let's get you set up 🤘 Signing up is free and takes just 5 minutes!
- Head to GitHub's signup page.
What is Github?
GitHub is a place for engineers to store and share their code and projects online. It's called GitHub because it uses Git to manage your projects' version history.
- Follow the prompts to create your account by entering your email, creating a password, and choosing a username.
- Complete one of their bot verification tasks. Switch the task type to Audio if the Visual task crashes your website.
- Once your account is created, confirm your email address with a verification code sent to your inbox.
- Log into your GitHub account once you've verified your email.
- Welcome to your GitHub account!
- Click on the "+" icon in the top right corner of the page, next to your profile icon.
- From the dropdown menu, select "New repository".
- Nice! We're ready to create a new repository. Fill out the Create a new repository page.
- In the Repository name field, enter nextwork-web-project
- In the Description (optional) field, add a description like: Java web app set up on an EC2 instance. This web app was set up as a part of the NextWork's CI/CD Pipeline series.
- Choose Public for the repository visibility.
- Leave the Initialize this repository with: options unchecked.
- Click the Create repository button.
What is GitHub?
GitHub is like a social network for code! It's where developers store their projects, track changes, and collaborate with others. At its heart, GitHub uses Git (a version control system) to keep track of every change made to your code. This means you can see who changed what, when they changed it, and even roll back to previous versions if something breaks.
GitHub is also where many companies look when hiring developers - your GitHub profile is essentially your coding portfolio. For our project, we're using GitHub to safely store our code and track all the changes we make as we build our CI/CD pipeline. Plus, you can keep this repository to show off this project to potential employers later!
- Go back to VS Code, and open the terminal connected to your EC2 instance.
- Make sure you're in your web app directory - run pwd and make sure it returns /home/ec2-user/nextwork-web-project.
- Initialize a new Git repository in this directory by running:
git init
What does git init do?
To start using Git for your project, you need to create a local repository on your computer.
When you run git init inside a directory e.g. nextwork-web-project, it sets up the directory as a local Git repository which means changes are now tracked for version control.
💡 What's a local repository?
The local repository is where you use Git directly on your own EC2 instance. The edits you make in your local repo is only visible to you and isn't shared with anyone else yet
This is different to the GitHub repository, which is the remote/cloud version of your repo that others can see.
WOAH! I got a bunch of yellow text when I ran this command
This yellow text is just Git giving you a heads-up about naming your main branch master and suggesting that you can choose a different name like 'main' or 'development' if you want.
💡 What is a main branch?
You can think of Git branches as parallel versions or 'alternate universes' of the same project. For example, if you wanted to test a change to your code, you can set up a new branch that lets you diverge from the original/main version of your code (called master) so you can experiment with new features or test bug fixes safely. We won't create new branches in this project and we'll save all new changes directly to master, but it's best practice to make all changes in a separate branch and then merge them into master when they're ready.
Add remote origin
Now let's connect your local project folder with your Github repo!
- Head back to your terminal in VS Code.
- Add the remote repository as the origin with the following command, replacing <repository_url> with your repository's URL:
git remote add origin [[REPOURL="<repository_url>"]]
What does 'remote add origin' mean?
Your local and GitHub repositories aren't automatically linked, so you'll need to connect the two so that updates made in your local repo can also reflect in your GitHub repo.
When you set remote add origin, you're telling Git where your GitHub repository is located. Think of origin as a bookmark for your GitHub project's URL, so you don't have to type it out every time you want to send your changes there.
- To find the URL of your GitHub repository, head back to your Git repository page.
- In the blue section of the page titled Quick setup - if you've done this kind of thing before, copy the HTTPS URL to your repository page. It will look like https://github.com/username/nextwork-web-project.git
- To verify that the remote origin has been set up correctly, run the command:
git remote -v
- This command lists all configured remote repositories!
- You should see origin listed, with both fetch and push URLs pointing to your GitHub repository URL.
Add, commit, and push your code to GitHub
Now, let's add all the files in your project to the Git repository. To do this, there are three commands you need to run...
- First, run this command in your terminal:
git add .
What does this command do?
git add . stages all (marked by the '.') files in nextwork-web-project to be saved in the next version of your project.
💡 What does staging mean?
When you stage changes, you're telling Git to put together all your modified files for a final review before you commit them. This is incredibly handy because you get to see all your edits in one spot, which means its much easier to check if there were are mistakes or unwanted changes before you commit.
- Run this command next in your terminal:
git commit -m "Updated index.jsp with new content"
What does this command do?
git commit -m "Updated index.jsp with new content"saves the staged changes as a snapshot in your project's history. This means your project's version control history has just saved your latest changes in a new version. -m flag lets you leave a message describing what the commit is about, making it easier to review what changed in this version.
- Finally, run this command:
git push -u origin master
What does this command do?
git push -u origin master uploads i.e. 'pushes' your committed changes to origin, which you've bookmarked as your GitHub repo. 'master' tells Git that these updates should be pushed to the master branch of your GitHub repo. By using -u you're also setting an 'upstream' for your local branch, which means you're telling Git to remember to push to master by default. Next time, you can simply run git push without needing to define origin and master.
- When you run git push, you might get asked for your GitHub username and password.
Why is Git asking for my username?
Git needs to double check that you have the right to push any changes to the remote origin your local repo is connected with. To do this, Git is now authenticating your identity by asking for your GitHub credentials.
- Enter your Github username, and press Enter on your keyboard.
- Next, enter your password. You'll notice that as you type this out, nothing shows on your terminal. This is totally expected - your terminal is hiding your input for your privacy. Press Enter on your keyboard when you've typed out your password, even if you don't see it printed out in your terminal.
- Hmmmm, now Git is letting us know that it can't actually accept our password.
What does this mean?
GitHub phased out password authentication to connect with repositories over HTTPS - there are too many security risks and passwords can get intercepted over the internet 🤺 You need to use a personal access token instead, which is a more secure method for logging in and interacting with your repos.
💡 What is a token?
A token in GitHub is a unique string of characters that looks like a random password. For example, a GitHub token might look like ghp_xHJNmL16GHSZSV88hjP5bQ24PRTg2s3Xk9ll. As you can imagine, tokens are great for security because they're unique and would be very hard to guess.
Set up and use a GitHub Personal Access Token (PAT)
- To create a PAT, go back to your GitHub account in your web browser.
- Click on your profile icon in the top right corner.
- Select Settings from the dropdown menu.
- In the left sidebar, scroll down and click on Developer settings.
- Under Developer settings, click on Personal access tokens.
- Click on Tokens (classic).
- Click on Generate new token (classic).
- Now you're on the New personal access token (classic) page!
- In the Note field, enter the reason why you're generating this token, like Generated for EC2 Instance Access. This is a part of NextWork's 6 Day DevOps Challenge.
- For Expiration, you can set it to 7 days for this project, or choose a different duration as per your preference.
What is a token expiration limit?
A token expiration limit means how long your personal access token would work for. After this time period, the token expires and no longer grants access, so you'll need to generate a new token. GitHub does this to make sure any tokens that are left lying around for months or years can't get picked up and used by someone else.
- Under Select scopes, select the checkbox next to repo.
What do all these scopes mean?
We use scopes to decide what kind of permissions your token will grant. Each scope you pick gives the token the ability to do even more things with your GitHub account. In our case, we picked the repo scope, which means the token can even access and control private repositories in your account.
- Scroll down and click the Generate token button at the bottom of the page.
Copy and use your Personal Access Token
- Nice, a new token (a long string of random letters) is generated!
- Make sure to copy the generated token right away - you won't be able to see it again after you leave this page 🍵
- Click the Copy to clipboard icon next to your new token to copy it, and paste it somewhere safe now.
- Go back to your VS Code terminal.
- Re-run the git push -u origin master command - you can use the up ⬆️ key on your keyboard to re-run a previous command.
- When asked for your username, enter your GitHub username again.
- ✋ PAUSE
- Do you remember what the GitHub token was generated for?
- When Git asks for your password, paste in your token instead.
- When you paste, it'll look like nothing is happening. That's because your terminal won't show your token for privacy reasons.
- Press Enter on your keyboard once you've pasted your token (even if you don't see it on screen).
- You'll see output in the terminal telling us that the push was successful, such as "Enumerating objects...", "Writing objects...", and "Branch 'master' set up to track remote branch 'master' from 'origin'".
What do these messages mean?
These messages show the progress of transferring objects (like files and commits).
Once the push is done, you also get messages that tell you that your local branch is now tracking the remote branch after the push. This means you only have to run git push next time, instead of the full git push origin master.
- Well done! Looks like Github recognises your token and pushed your changes to your repository.
Important Secufrity Note
Treat your Personal Access Token like a password. Keep it secure and do not share it with anyone or commit it into your code. If you accidentally share your token, make sure to delete it straight away and generate a new one.
Verify code in GitHub repository
- To confirm that your code has been successfully pushed to GitHub, let's refresh your GitHub repository page in your browser.
- You should now see all your web app files listed in your GitHub repository. SO good!
Congratulations! You've successfully connected your web app to GitHub. Your code is now safely stored in a remote repository, and you can track changes and collaborate more effectively.
To avoid having to enter your username and PAT every time you push to GitHub, you can configure Git to store your credentials.
This is optional but can make your workflow smoother!
YES - let's configure Git
- Run the following command to configure Git to use the store credential helper:
git config --global credential.helper store
- After running this command, try pushing again: git push. You might be asked for your credentials one last time. Once entered, Git will store them for future pushes.
- Refresh your GitHub repository page in your browser again.
- Open the index.jsp file in your repository on GitHub.
- Verify that the changes you made (<h2>Hello {YOUR_NAME}!</h2> and the new paragraph) are now visible in the file on GitHub.
Nope - skip configuration
- No problem, onwards and upwards! You can continue without configuring the credential helper.
- You'll just need to enter your GitHub username and personal access token each time you push changes to GitHub. Make sure to keep the token safe!
Let's GO! Your web app is now fully connected to GitHub, and you're ready for the next steps in setting up your CI/CD pipeline.
Launch EC2 Instance
To kick things off, we'll need to set up our development EC2 instance and connect to it via SSH!
Launch an EC2 Instance
- Log in to the AWS Management Console as your IAM Admin user.
- Switch your Region to the one closest to you.
Tip: We recommend using the following regions
Did you know that not all regions have the same number of AWS services available?
There are only 13 AWS Regions that provide ALL the services we'll use in the 6 Day DevOps Challenge. We'd recommend using one of these regions from the very start of the challenge, so all your resources are in the same place. Even if you don't live in these regions, you can still use them:
- us-east-1 (N.Virginia)
- us-east-2 (Ohio)
- us-west-2 (Oregon)
- eu-west-1 (Ireland)
- eu-west-2 (London)
- eu-central-1 (Frankfurt)
- eu-north-1 (Stockholm)
- eu-south-1 (Milan)
- eu-west-3 (Paris)
- ap-southeast-1 (Singapore)
- ap-southeast-2 (Sydney)
- ap-northeast-1 (Tokyo)
- ap-south-1 (Mumbai)
- Head to the EC2 console.
What is Amazon EC2?
Amazon EC2 (Elastic Compute Cloud) gives you virtual servers in the cloud that you can spin up whenever you need them. Think of it like renting computers in AWS's data centers that you can configure and use without having to worry about the physical hardware.
For this project, we're using EC2 as both the place where we'll deploy our application and the environment where we'll build it initially. It's perfect for this because you can easily SSH into it to make changes and see your application running in real-time.
- Launch an EC2 instance with the following settings:
- Name: nextwork-devops-enter your name
- Application and OS Images (Amazon Machine Image): Amazon Linux 2023
- Instance type: t2.micro
- Key pair: nextwork-keypair
- Network settings: Allow SSH traffic from is set to My IP.
- Finally, click Launch instance at the bottom right of the page.
Getting an "InsufficientInstanceCapacity" error when launching your EC2 instance?
Don't worry - this is a common issue that many NextWork students encounter! It simply means AWS doesn't have enough capacity for your chosen instance type in that specific data center (Availability Zone) right now. Here's how to fix it:
- Try a different Availability Zone: Launch your instance in a different Availability Zone within the same region. Each zone is like a separate data center, and some might have more available capacity than others.
- Choose a different instance type: If you're still having trouble, try selecting a different instance type. Some types are in higher demand than others.
- Success!
EC2 instance LAUNCHED 🚀
Next up, let's connect to the EC2 instance. Jump to the next tab for the next step!
SSH to Instance
Now that your EC2 instance is running, let's connect to it securely use SSH so we can start setting up our environment.
Prepare Your Private Key File
Let's get your private key file ready for secure authentication.
- Locate the private key file (nextwork-keypair.pem) that you downloaded when creating the key pair in the previous step.
- Move this .pem file to a dedicated folder on your local computer for better organization. For example, create a folder named DevOps on your Desktop and move the .pem file into it: ~/Desktop/DevOps/.
Configure SSH in VS Code
- Open VS Code on your local machine.
- Click on the Remote Explorer icon in the Activity Bar on the side (it looks like a double arrow).
- If you don't see the Remote Explorer icon, you might need to install the Remote - SSH extension.
- You can do this by going to the Extensions view (Ctrl+Shift+X or Cmd+Shift+X) and searching for "Remote - SSH".
- In the Remote Explorer, click on the Configure SSH Hosts... icon (it looks like a gear or settings icon).
- Select SSH configuration file. Choose the existing config file path (e.g., ~/.ssh/config) from the dropdown menu.
What is SSH Configuration File?
The SSH configuration file (config) is a text file that allows you to define and save settings for SSH connections. By editing this file, you can store connection details for your EC2 instance, making it easier to connect in the future.
- VS Code will open your SSH config file.
- Edit the SSH config file - delete the existing setup.
- Add the following configuration, replacing <your_instance_public_ipv4_dns> with the Public IPv4 DNS of your EC2 instance.
- Make sure <path_to_pem_file> points to your private key file (the .pem file you downloaded when launching the EC2 instance).
Host <your_instance_public_ipv4_dns>
HostName <your_instance_public_ipv4_dns>
User ec2-user
IdentityFile <path_to_pem_file>
What is SSH (Secure Shell)?
SSH (Secure Shell) is a secure way to connect to and control your remote servers. Think of it like having a super-secure, encrypted phone line directly to your server where you can talk to it using text commands.
When you use SSH, everything you send (commands, passwords, data) is encrypted, so even if someone is eavesdropping on your internet connection, they can't see what you're doing. For developers, SSH is the go-to tool for managing servers remotely, transferring files securely, and running commands on distant machines without having to be physically present.
Need help finding your EC2 instance's IPv4 DNS?
- In the EC2 console, navigate to Instances and select your running instance.
- Under the Details tab, find and note down the Public IPv4 DNS. We'll need this to connect to our instance remotely.
What is Public IPv4 DNS?
The Public IPv4 DNS is basically your EC2 instance's address on the internet. You'll use this address whenever you need to connect to your instance from your local machine.
- Save your updated SSH config file. Save the updated config file in VS Code by pressing Ctrl+S (Windows/Linux) or Cmd+S (Mac).
- Make sure no unsaved changes indicator (a white circle) is present on the file tab. If you see a white circle, it means your changes are not saved yet.
Connect to EC2 via SSH
- In VS Code, click on the Remote Explorer icon again.
- Select Connect to Host.
- Your EC2 instance's Public IPv4 DNS (which you set as HostName in the config file) should now appear as a configured host in the Remote Explorer panel (under SSH TARGETS).
- Select your configured EC2 instance.
- VS Code will open a new window and attempt to connect to your EC2 instance.
- Continue SSH connection. If prompted with a host key verification, click Continue. This is a security measure to ensure you are connecting to the correct server.
- If the connection went well, you'll see a new VS Code window connected to your EC2 instance. The bottom left corner of the window should say that you are connected to SSH: ec2-instance.
Seeing a "Permission denied (publickey)" error when trying to connect?
Oops! SSH connections can sometimes be a bit finicky. This usually happens when your private key file has incorrect permissions or you're using the wrong key file. Let's solve this together:
- Fix your key file permissions: SSH requires secure permissions on your private key
- Open your terminal
- Go to where your .pem file is stored
- MacOS/Linux: Run chmod 400 nextwork-keypair.pem (replace nextwork-keypair.pem with your actual key file name if different). This command sets the permissions so that only the owner (you) can read the file.
- Windows: Run the following command:
icacls "nextwork-keypair.pem" /reset
icacls "nextwork-keypair.pem" /grant:r "[[USERNAME="enter your username"]]:R"
icacls "nextwork-keypair.pem" /inheritance:r
- Make sure you're using the correct key: Double-check that the key file you're using matches the one you selected when creating your EC2 instance
After adjusting the permissions and verifying your key pair, try connecting again. You should now be able to establish an SSH connection to your EC2 instance successfully!
Woop woop! Do you know what's next?
It's time to install your development instance's key tools - Maven, Java and Git! Jump to the next tab for the next step!
Install tools
Now that you're connected to your EC2 instance, let's set up the necessary tools and environment to build our web application. We'll install Maven, Java, and Git!
Install Maven
- In the new VS Code window, open a new terminal (Terminal > New Terminal). The terminal prompt should say that you are now working within your EC2 instance (e.g., ec2-user@your-instance-id ~ $).
- Run the following commands one by one to download and install Maven:
wget https://archive.apache.org/dist/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.tar.gz
sudo tar xzf apache-maven-3.5.2-bin.tar.gz -C /opt
echo 'export PATH=/opt/apache-maven-3.5.2/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
What is Apache Maven?
Apache Maven is like a smart assistant for your Java projects. It helps you build your project, manage all the libraries your code depends on, and generate helpful documentation - all using simple commands.
When you're working on a Java project (like we are), Maven saves you from the headache of manually downloading and configuring all those JAR files your application needs. Instead, you just tell Maven what you need in a simple XML file, and it handles the rest. It's a huge time-saver that lets you focus on writing code rather than managing dependencies.
- Run the command mvn -v to check if Maven is installed correctly and to see its version.
- Confirm Maven version 3.5.2 is displayed in the output.
Install Java
- Run the following commands to install Java 8 Amazon Corretto:
sudo dnf install -y java-1.8.0-amazon-corretto-devel
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-amazon-corretto.x86_64
export PATH=$JAVA_HOME/jre/bin/:$PATH
What is Java?
Java is one of the world's most popular programming languages, known for its "write once, run anywhere" capability. You write your code once, and it can run on any device that has a Java Virtual Machine (JVM) - whether that's a laptop, server, or even a smart TV!
Amazon Corretto is AWS's free, production-ready version of Java. It's basically Java with AWS's stamp of approval, meaning they've tested it extensively and support it themselves. We're using it as the foundation for running our web application because it's reliable and doesn't cost a thing.
- Run the command java -version to check if Java is installed and to see its version.
- Confirm Java version 1.8.0_442 (or similar Java 8 version) is displayed.
Install Git
- Run the following commands to update the package manager and install Git:
sudo dnf update -y
sudo dnf install git -y
What is Git?
Git is like a time machine for your code. It keeps track of all the changes you make, lets you rewind to previous versions if something breaks, and helps teams collaborate without overwriting each other's work.
In our project, we'll use Git to grab our web application code from GitHub (a popular service built on Git) and bring it to our EC2 instance. This means you can easily update your code, track changes, and work with others on the same codebase without stepping on each other's toes.
- Check that you've installed Git by running git -version - you should see a version number!
I don't see a version number
Aha! Check the terminal response - turns out, git -version is not the right command to see your Git version number. Can you tell what is the proper command to find it?
Now that Git is installed, it's ⏰ time ⏰ to set up your web app's code! Jump to the next tab for the next step!
Web App Code
Clone Web App Repository
- In the EC2 terminal, run the following command, replacing <repository_url> with the URL you just copied:
git clone <repository_url>
- Not sure where to find the repository URL?
- Navigate to your GitHub repository for the web application in your web browser.
- On your GitHub repository page, click on the green Code button.
- A dropdown menu will appear. Copy the HTTPS URL provided in the dropdown.
- Paste and run the repository URL in your command.
Getting a "git: command not found" error?
No worries - this just means Git isn't installed on your EC2 instance yet. This is super easy to fix:
- Run this command to install Git: sudo dnf install git -y
- After installation completes, verify it worked by running: git --version
Open Project Folder in VS Code
- In VS Code, click Open Folder in the Explorer panel (top left corner).
- Select the cloned project folder (nextwork-web-project) in your home directory and click OK.
- You should now see the files and folders of your web application project in the VS Code Explorer.
- If VS Code prompts Do you trust the authors of the files in this folder?, click Yes, I trust the authors.
- Close any popup that you might get about installing another extension.
- To verify that your web app code is present, open src/main/webapp/index.jsp by double-clicking it and check its content.
With Maven, Java, and Git installed and your project opened in VS Code, you're all set to move on to the next step!
Set up CodeArtifact Repository
Let's set up AWS CodeArtifact, a fully managed artifact repository service. This will help us securely store and share software packages used in our CI/CD pipeline.
In this step, you're going to:
- Set up a CodeArtifact repository to store your web app's packages.
- Create an IAM policy and role for CodeArtifact access.
- Configure Maven settings to use CodeArtifact.
- Build your web app with Maven and verify the connection with CodeArtifact.
Just like the previous step, we've broken down CodeArtifact set up into four key milestones.
How far can you go in each milestone without looking at the step by step instructions?
- Create your CodeArtifact repository.
- Set up an IAM policy and role for CodeArtifact access.
- Give your development EC2 instance access to CodeArtifact.
- Complete the connection settings between CodeArtifact and Maven.
Create repository
Let's get started with CodeArtifact!
Navigate to CodeArtifact
- Open the CodeArtifact service. You can search for "CodeArtifact" in the search bar and select CodeArtifact under Services.
What is AWS CodeArtifact?
AWS CodeArtifact is like a secure, private repository for all your software packages and dependencies. Instead of having developers download packages from the public internet (which can be risky and unreliable), you store trusted versions in CodeArtifact.
It works seamlessly with tools you're already using like Maven, npm, and pip. In our CI/CD pipeline, CodeArtifact gives us a central, secure place to store our Maven artifacts so our build and deployment processes can always access exactly what they need.
Create CodeArtifact Repository
- In the CodeArtifact console, click Create repository.
- On the Create repository page, configure the following:
- Repository name: nextwork-devops-cicd
- Description: Repository for NextWork CI/CD artifacts.
- Package format: Select Maven.
- Public upstream repositories: Enable Include public upstream repositories and select maven-central-store. This allows CodeArtifact to fetch packages from Maven Central if they are not found in your repository.
- If prompted to set a domain, set the domain name to nextwork. If a domain already exists, you can use the existing domain.
- Click Create repository.
- The CodeArtifact repository nextwork-devops-cicd should be created successfully. You should see it listed in the repositories dashboard.
With your CodeArtifact repository set up and connection details noted, we're ready to configure our EC2 instance to access it.
Jump to the next tab for the next step!
IAM Policy and Role
Create IAM Policy for CodeArtifact Access
First, we'll create an IAM policy that defines the permissions needed to access CodeArtifact.
- Navigate to the IAM console in a new tab.
- In the IAM console, click Policies in the left-hand menu.
- Click Create policy.
- Switch to the JSON tab.
- Replace the existing content in the JSON editor with the following policy document. This policy grants permissions to get authorization tokens, repository endpoints, and read from CodeArtifact repositories:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "sts:GetServiceBearerToken",
"Resource": "*",
"Condition": {
"StringEquals": {
"sts:AWSServiceName": "codeartifact.amazonaws.com"
}
}
}
]
}
- Click Next.
- On the Review policy page, enter the following:
- Policy name: codeartifact-nextwork-consumer-policy
- Description: Policy for EC2 instances to access CodeArtifact
- Click Create policy.
- Nice! The codeartifact-nextwork-consumer-policy should be created and listed in your IAM policies.
Create IAM Role for EC2 Instance
- In the IAM console, click Roles in the left-hand menu.
- Click Create role.
- Select AWS service as the trusted entity type.
- Choose EC2 as the service that will use this role.
- Click Next.
- In the Attach permissions policies page, search for and select the codeartifact-nextwork-consumer-policy you just created.
- Click Next.
- On the Name, review, and create page, enter the following:
- Role name: ec2-instance-nextwork-cicd
- Description: IAM role for EC2 instances to access CodeArtifact
- Click Create role.
- The ec2-instance-nextwork-cicd-role should be created and listed in your IAM roles.
Attach IAM Role to EC2 Instance
Now, let's associate the IAM role with your EC2 instance.
- Navigate back to the EC2 service console and go to the Instances dashboard.
- Select your running instance (nextwork-devops-yourname).
- Click Actions at the top, then select Security, and then Modify IAM role.
- In the Modify IAM role dialog, from the IAM role dropdown, select the ec2-instance-nextwork-cicd-role you just created.
- Click Update IAM role to apply the changes.
Too cool - your EC2 instance's permissions are all set up.
Time to make the connection between CodeArtifact and your web app happen 🤝
Jump to the next tab for the next step!
Connect CodeArtifact and Maven
Get Connection Instructions
We'll need to get the connection instructions to configure Maven to use this repository.
- Click on the newly created repository nextwork-devops-cicd to view its details.
- Click on Connection instructions in the repository details page.
- In the Connection instructions dialog, select Mac and Linux as the operating system and Maven as the package manager.
- 🚨 Double check that you're using Mac and Linux as the operating system. Even if you're doing this project on a Windows computer, Mac and Linux is the right choice. Your EC2 instance is an Amazon Linux 2023 instance!- Copy the command in Step #3 - this command stores an authorization token to your CodeArtifact repository!
Export CodeArtifact Auth Token
- Go back to your VS Code remote window connected to your EC2 instance.
- In the terminal, run the command in Step #3 of your CodeArtifact connection instructions window.
- This should export the CodeArtifact authorization token as an environment variable.
Excellent work! You've successfully configured IAM permissions to allow your EC2 instance to access CodeArtifact. Let's move on to configuring Maven to use CodeArtifact in the next step.
Create settings.xml
Note
Aready have a settings.xml file in your repo? Nice - skip to building the project!
Let's create the settings.xml file in your Maven project.
What is settings.xml?
The settings.xml file in Maven is used to configure Maven's execution environment. It allows you to customize settings like repository locations, server credentials, and proxy configurations. In our case, we are using it to tell Maven to use our CodeArtifact repository for managing dependencies and deploying artifacts.
- In your VS Code Explorer, navigate to the root directory of your cloned project (nextwork-web-project).
- Create a new file named settings.xml in the project root directory.
- Add settings tags in the file:
<settings>
</settings>
- Copy and paste the code from steps #4-6 of CodeArtifact's connection instructions between the <settings> tags.
- Save settings.xml.
Question: What is the purpose of configuring settings.xml for Maven?
The settings.xml file is like Maven's address book and passport for accessing repositories. We're setting it up for a couple of important reasons:
First, it tells Maven where to find your AWS CodeArtifact repository - essentially giving Maven the address of your private package library. Without this, Maven would only look in public repositories like Maven Central.
Second, it provides the authentication details Maven needs to access your private repository. The CODEARTIFACT_AUTH_TOKEN environment variable works like a temporary password that proves Maven has permission to access your CodeArtifact repository.
By configuring this file correctly, you're ensuring that Maven can seamlessly download dependencies from and upload artifacts to your private CodeArtifact repository. This creates a secure, controlled environment for your project's packages, which is essential for reliable builds in your CI/CD pipeline.
Build the Project with Maven
- In the EC2 terminal, navigate to your root project directory (nextwork-web-project) if you are not already there.
- Run the command to build your project using Maven and the settings.xml file you configured:
mvn compile -s settings.xml
Getting "UnauthorizedException" or "AccessDeniedException" during your Maven build?
Many students encounter this error - it means your EC2 instance doesn't have proper permissions to access CodeArtifact. Let's fix it together:
- Check your IAM policy: Make sure the codeartifact-nextwork-consumer-policy has all the permissions shown in the steps above
- Verify the IAM role is attached: Confirm that the ec2-instance-nextwork-cicd role (with the policy attached) is connected to your EC2 instance
- Review your settings.xml file: Double-check that all placeholders like <your_aws_account_id> have been replaced with your actual values
- Verify your auth token: Make sure the CODEARTIFACT_AUTH_TOKEN environment variable was exported correctly - run echo $CODEARTIFACT_AUTH_TOKEN to check
- You should see a BUILD SUCCESS message at the end of the Maven build output in the terminal.
- The first build might take longer (wait 1-3 minutes) as Maven downloads dependencies from CodeArtifact for the first time.
Verify Artifact Upload to CodeArtifact
- Go back to the CodeArtifact console.
- Navigate to Repositories and select your nextwork-devops-cicd repository.
- Check that packages are uploaded to your CodeArtifact repository. Don't forget to hit the refresh button!
Set up CodeBuild Project
Now, let's automate the build process using AWS CodeBuild. CodeBuild is a fully managed build service that will compile our source code, run tests, and produce deployable software packages.
In this step, you're going to:
- Set up an S3 bucket to store your CodeBuild artifacts.
- Create a CodeBuild project.
- Update CodeBuild's IAM role to include CodeArtifact access.
- Run a (successful) CodeBuild build!
You guessed it! Here's the CodeBuild set up split into 5 key milestones - which steps can you tackle without step by step guidance?
- Launch an S3 bucket to store your build artifacts.
- Set up a CodeBuild project.
- Update your CodeBuild IAM Role to access CodeArtifact.
- Create buildspec.yml in your web app repository.
- Put your CodeBuild setup to the test - build your project!
S3
- Make sure you're still in the same region where you set up the CodeBuild build project.
- Let's head to the S3 console. In the AWS Management Console search bar, type S3 and select S3 from the dropdown menu under Services.
- On the Buckets page, click Create bucket.
- In the Create bucket page, under General configuration, for Bucket name, enter nextwork-devops-cicd-enter your name
- Leave all other settings as default.
- Click Create bucket at the bottom of the page.
Jump to the next tab to see the next step!
CodeBuild
Create CodeBuild Project
Let's automate our builds with CodeBuild!
- Open the CodeBuild service.
What is AWS CodeBuild?
AWS CodeBuild is like having an automated builder who lives in the cloud and is ready to compile your code 24/7. Instead of setting up and maintaining your own build servers, CodeBuild handles everything for you.
When you push changes to your GitHub repository, CodeBuild springs into action - compiling your code, running tests, and packaging everything up for deployment. The beauty of CodeBuild is you only pay for the compute time you actually use. Your project might build in just a few minutes, so why pay for a server that sits idle the rest of the day?
- In the CodeBuild console, click Create build project.
- On the Create build project page, configure the following: Project name: nextwork-devops-cicd
- In the Source section:
- Source provider: Select GitHub.
- Repository: Choose your your repository (<your_github_username>/nextwork-web-project).
I don't see any GitHub connections or repositories!
- If you haven't connected to GitHub before, click Manage account credentials.
- Select create a new GitHub connection.
- In the Create GitHub connection popup, enter a connection name: nextwork-devops-cicd
- Click Connect to GitHub.
- Follow the connection steps in the GitHub App, and get redirected back to AWS.
- Select your GitHub account name under App Installation.
- Select Connect.
- Select the refresh button next to the Connection field, then select your new nextwork-devops-cicd connection.
- Select Save.
- Once the connection is established, select your repository from the Repository dropdown (<your_github_username>/nextwork-web-project) in your CodeBuild setup.
- Untick the Webhook checkbox that says "Rebuild every time a code change is pushed to this repository."
Configure Environment
- In the Environment section:
- Environment image: Select Managed image.
- Operating system: Choose Amazon Linux 2.
- Runtime: Choose Standard.
- Image: Choose aws/codebuild/amazonlinux2-x86_64-standard:coretto8.
- Image version: Select Always use the latest image for this runtime version.
- In the Service role section, let's keep the default and let CodeBuild create a New service role.
- In the Buildspec section, select Use a buildspec file. This tells CodeBuild to use a buildspec.yml file in your repository to define the build commands.
- In the Artifacts section:
- Artifacts type: Select Amazon S3.
- Bucket name: Enter your S3 bucket name nextwork-devops-cicd. You'll need to create this bucket if you don't have it yet!
- Name: nextwork-devops-cicd-artifact
- Artifacts packaging: Select zip.
- In the Logs section:
- Enable CloudWatch logs if it's not enabled yet.
- Set the CloudWatch group name to /aws/codebuild/nextwork-devops-cicd
- Click Create build project.
Sweet! Build project set up. After creating the CodeBuild project, an IAM role is automatically created for it.
We'll need to add permissions to this role, so that CodeBuild can access CodeArtifact!
Jump to the next tab for the next step!
IAM
Update CodeBuild IAM Role
- Head to the IAM console.
- Click Roles in the left-hand menu.
- Search for your CodeBuild service role (e.g., codebuild-nextwork-devops-cicd-service-role) and click on the role name.
- Click Add permissions, then Attach policies.
- Search for and select the codeartifact-nextwork-consumer-policy.
- Click Attach policies.
Alrighty! Now that CodeBuild has access to CodeArtifact, we'll set up buildspec.yml next. This will help CodeDeploy know how to deploy your web app.
Jump to the next tab for the next step!
buildspec.yml
Note
Psssst do you already have a complete buildspec.yml file from cloning the code in your GitHub repository? You're on to it - you can skip to the next tab.
Create buildspec.yml
- In your VS Code Explorer, navigate to the root directory of your cloned project (nextwork-web-project).
- Create a new file named buildspec.yml in the project root directory.
- Open the buildspec.yml file and copy and paste the following content into it.
- 🚨 Replace <your_aws_account_id> with your actual AWS account ID
- 🚨 Replace us-east-2 with your region.
version: 0.2
phases:
install:
runtime-versions:
java: corretto8
pre_build:
commands:
- echo Logging in to AWS CodeArtifact...
- CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain nextwork --domain-owner <your_aws_account_id> --region us-east-2 --query authorizationToken --output text`
- export CODEARTIFACT_AUTH_TOKEN
build:
commands:
- echo Build started on `date`
- mvn clean install -s settings.xml
post_build:
commands:
- echo Build completed on `date`
- echo Packaging artifacts...
- mvn package -s settings.xml
artifacts:
files:
- target/*.war
discard-paths: no
What is buildspec.yml?
buildspec.yml is a YAML file that defines a set of build commands and settings for AWS CodeBuild. It tells CodeBuild what to do at each phase of the build process, including installing dependencies, running tests, and packaging artifacts. It's essential for automating your build process in CodeBuild.
- Save buildspec.yml.
CodeBuild setup all DONE 🔥 Let's build our project to check that everything is set up correctly.
Jump to the next tab for the next step!
Push buildspec.yml to GitHub
- In your EC2 instance terminal, navigate to your project directory (cd ~/nextwork-web-project) if you are not already there.
- Run git add . to stage all changes, including the newly created buildspec.yml and any modifications to settings.xml.
- Run git commit -m "Add CodeBuild resources and buildspec.yml" to commit the staged changes with a descriptive message.
- Run git push origin master to push your local commits to your GitHub repository's master branch.
Seeing errors when pushing your code?
Try resetting your Git credentials, and starting over again! Run these commands in your terminal:
git config --global --unset credential.helper
git config --local --unset credential.helper
git remote set-url origin https://[[GITUSERNAME="<github_username>"]]@github.com/[[GITUSERNAME="<github_username>"]]/[[REPONAME="<repository_name>"]].git
- Make sure to replace <YOUR_GITHUB_USERNAME> with your GitHub username and <YOUR_REPOSITORY_NAME> with your repository name.
- Then try git push again and enter your GitHub PAT when prompted for the password. Check the top of your VS Code window if you're not prompted in the terminal.
- Let's confirm that your changes are reflected in your GitHub repository.
- After a successful git push, refresh your GitHub repository page in your web browser.
- You should see the buildspec.yml file and the updated settings.xml file in your repository, confirming that your push was successful!
Build project
Test CodeBuild Project
Let's test your CodeBuild project by starting a build manually.
- Navigate back to the CodeBuild console and select your nextwork-devops-cicd project.
- Click Start build to manually trigger a build.
- The build should start successfully.
- You will be redirected to the build details page where you can monitor the build progress in real-time in the Build logs section.
- Wait for the build to complete and check the build logs for any errors.
- Fingers crossed!
Build success!
- You can check for a build artifact in your S3 bucket, which would also help verify that the build was a success!
- Yeehaw! You've successfully set up a CodeBuild project that automates the build process for your web application.
- In the next steps, we'll set up CodeDeploy to automate the deployment of your application.
Build failed!
Here's your troubleshooting checklist!
- Is buildspec.yml in your code repository? Check your GitHub repo now!
- Is settings.xml at the root of your code repository? It should NOT be in any subfolders.
- Is buildspec.yml at the root of your code repository? It should NOT be in any subfolders.
- Does your CodeBuild service role have access to CodeArtifact?
- Does your CodeBuild service role have access to CodeConnection?
- Does buildspec.yml reference your account ID and region code?
- Are there any errors if you run mvn compile -s settings.xml locally?
Still Running into CodeBuild errors? Let's solve them!
CodeBuild issues are common, but they're usually quick fixes. Scroll down to your build event's logs, and study the error messages:
If you see YAML_FILE_ERROR: YAML file does not exist
This means CodeBuild can't find your buildspec.yml file. Make sure it's:
- In the root directory of your repository
- Committed and pushed to GitHub (run git add buildspec.yml, git commit -m Add buildspec.yml, and git push origin master)
If you see CLIENT_ERROR: Failed to get access token
Your CodeBuild service role needs permission to access CodeConnection! Without permission, it can't access your GitHub repo.
- Head to the IAM console and find your CodeBuild role (like codebuild-nextwork-devops-cicd-service-role)
- Attach the AWSCodeStarConnectionsFullAccess policy to it.
- If that still doesn't work, consider attaching a custom policy that allows access to connection! Note the ARN in your error message, and use that in this policy template:
If you see Domain owner should be your account ID
You need to replace the placeholder account ID:
- Open buildspec.yml in VS Code
- Find the line with domain-owner and replace the ID with your actual AWS account ID (found in the top right of AWS console)
- Save and push your updated code.
Retry CodeBuild Build
Let's retry the CodeBuild build.
- Go back to the CodeBuild console.
- Make sure you're in your nextwork-devops-cicd project page.
- Click Retry build or Start build again to trigger a new build.
- The build should start successfully.
- You can also check for a build artifact in your S3 bucket, which would also help verify that the build was a success!
- If you still run into any issues, share a screenshot of your error message in the NextWork community!
Congratulations! You've successfully set up a CodeBuild project that automates the build process for your web application. In the next steps, we'll set up CodeDeploy to automate the deployment of your application.
Launch EC2 with CloudFormation
Let's start our deployment by setting up the deployment infrastructure. We'll use CloudFormation to launch an EC2 instance and its networking resources.
Didn't we already create an EC2 instance at the start of this project?
Yes we did! When we created our development instance, we've set up an EC2 instance for us to use as a development environment i.e. to create and edit code for our web app.
This new instance is created specifically for running our application in a live, production environment i.e. what users will see. By having a separate EC2 instance for deploying our application, we avoid the risk of testing any code changes/new features in a live production environment that users end up seeing too.
In this step, you're going to:
- Launch an EC2 instance using CloudFormation.
- Configure network settings for the EC2 instance.
- Understand Infrastructure as Code.
- Head to the CloudFormation console.
What is AWS CloudFormation?
Think of CloudFormation is AWS' infrastructure as code tool. Instead of clicking around the AWS console to set up resources (which gets tedious fast!), you write a single template file that describes everything you need - your EC2 instances, security groups, databases, and more. Then, CloudFormation reads this file and builds your entire environment for you, exactly the same way every time.
💡 What is Infrastructure as Code?
Just like software developers write code to build applications, Infrastructure as Code (IaC) is a type of software that lets you write code to create your servers, networks, and other infrastructure. Instead of manually configuring each server (time-consuming and error-prone), you have a script that sets everything up perfectly every time. Your infrastructure becomes predictable, repeatable, and much easier to manage!
Create a CloudFormation Stack
- Select Create stack.
- Select With new resources (standard) from the dropdown menu.
What is a CloudFormation stack?
When you deploy a CloudFormation template, you're creating a stack - think of it as a project folder that holds all your connected resources. The cool thing is that CloudFormation treats this stack as a single unit, so you can create, update, or delete all those resources together with one command.
- Select Template is ready.
- Select Upload a template file.
- Select Choose file.
- Upload the following CloudFormation template: nextworkwebapp.yaml
What is a CloudFormation template?
A CloudFormation template is a text file where you tell CloudFormation the AWS resources you want to create and how they should be configured - like specifying "I want a t2.micro EC2 instance with this security group and permissions to access this S3 bucket." The beauty is that this template is both human-readable AND machine-executable. In our project, we're using YAML because it's a bit friendlier to read than JSON (fewer brackets and quotes to keep track of!).
- Verify the file nextworkwebapp.yaml is uploaded and select Next.
Configure Stack Details
- Select Next.
- Enter NextWorkCodeDeployEC2Stack as the Stack name.
- Next, we'll need to add our IP address to the template.
- Head to https://checkip.amazonaws.com/ and copy your IP address.
- Paste your IP address into the MyIP parameter field and add /32 at the end.
Why do we add /32 to the IP address?
Adding /32 to your IP address is like telling AWS "I only want this exact address to have access, not any others." The /32 is CIDR notation (pronounced "cider" - yes, like the drink!) that specifies exactly how many IP addresses are included in your rule.
With /32, it's just one - your specific IP. If we used /24 instead, we'd be allowing 256 different IP addresses! For security, we're being as specific as possible to minimize who can access our EC2 instance.
- Select Next.
Configure Stack Options
- In Configure stack options, under Stack failure options, select Roll back all stack resources.
- Next, select Delete all newly created resources.
What are Stack failure options?
Stack failure options are your safety net when things don't go as planned. They determine what CloudFormation should do if it runs into an error while creating your resources:
- Roll back all stack resources: This is like having an "undo" button for your entire deployment. If anything fails, CloudFormation will automatically revert everything back to how it was before you started. This prevents you from ending up with a half-built environment that might not work or cost you money unnecessarily.
- Delete all newly created resources: This makes sure CloudFormation cleans up after itself during a rollback. No resources left behind to surprise you on your bill next month!
These options are especially valuable when you're learning - they let you experiment without worrying about leaving a mess behind.
- Scroll down to Capabilities and check the box I acknowledge that AWS CloudFormation might create IAM resources.
Why would CloudFormation create an IAM role?
IAM roles are like special visitor passes that AWS services can "wear" to temporarily access other services. In our case, our deployment EC2 instance will use a role to access files from S3.
Hmmm... why do you think it'll need access to S3 (have you created an S3 bucket anywhere)? Aha - your deployment environment will need to use the build artifacts stored in your S3 bucket!
- Select Next.
Review and Submit
Let's review our stack's details!
- Template: Template is ready
- Stack name: NextWorkCodeDeployEC2Stack
- Parameters: MyIP is filled with your IP and /32
- Rollback on failure: Activated
- Delete all newly created resources during rollback: Activated
- Select Submit.
Monitor Stack Creation
- CloudFormation is now launching the stack in the background! This process will create the EC2 instance and networking resources in the background.
- While we're wating...
- Let's check out the Resources tab.
- You can see a list of the resources being created!
What resources are being created by the CloudFormation template?
The CloudFormation template is creating a:
- VPC (Virtual Private Cloud): A virtual network in the cloud for your AWS resources.
- Subnet: A subdivision of the VPC where you can place resources.
- Route Tables: Define how network traffic is routed within the VPC.
- Internet Gateway: Allows resources in your VPC to connect to the internet.
- Security Group: Acts as a virtual firewall to control inbound and outbound traffic for your EC2 instance.
- EC2 Instance: The virtual server where your web application will be deployed.
💡 Why are we deploying networking resources too?
By defining these networking resources in the template, we're not just launching an EC2 instance, but creating a complete, secure, and configurable infrastructure that can be easily replicated or modified. This is an especially good idea for EC2 instances that are hosting web apps, because they have more complex needs like connecting with multiple databases and controlling both public and private network traffic.
- Next, you can also check the Events tab.
- You can watch your CloudFormation stack's events in real-time in the Events tab - just keep refreshing it!
What is a CloudFormation stack event?
Every time CloudFormation creates, updates, or deletes something, it records an event - "Starting to create EC2 instance," "Security group created successfully," "Oh no, there isn't enough capacity to create a new VPC." These events give you visibility into exactly what's happening behind the scenes, which is super helpful for troubleshooting if something goes wrong.
- Wait for the stack's status to become CREATE_COMPLETE.
Is your stack creation failing?
If you see ROLLBACK_IN_PROGRESS or ROLLBACK_COMPLETE status, it means CloudFormation encountered an error while creating your resources. Don't worry! This is a common issue that many NextWork students encounter.
To fix this:
- Check the detailed error messages in the Events tab
- Make any needed corrections to template parameters or IAM permissions
- Try creating the stack again
Remember, troubleshooting is part of the learning process! You've got this. If you're still stuck, ask the NextWork community!
Great job! You've launched your production environment using CloudFormation. It's looking great 😎
Prepare Deployment Scripts and AppSpec
Now that our EC2 instance is up and running, can we start deploying our application?
Mmm, not yet. To start deploying our application, we need to prepare a set of scripts and configuration files for CodeDeploy. It's like we need to write a set of instructions for CodeDeploy to follow - otherwise, it wouldn't know how to deploy our application!
In this step, you're going to:
- Create deployment scripts for installing dependencies, starting, and stopping the server.
- Create an appspec.yml file to tell CodeDeploy how to handle the deployment process.
- Update buildspec.yml to package deployment files in your build artifact.
Create Scripts Folder
- In your IDE, create a new folder at the root of your project directory.
- Name the folder scripts
What are scripts?
Scripts are like mini-programs that automate tasks. They're essentially text files filled with commands that you'd normally type one by one in the terminal, but packaged together so they run automatically in sequence. In our project, we're using scripts to automate deployment tasks.
Create install_dependencies.sh
- Inside the scripts folder, create a new file.
- Let's name the file install_dependencies.sh
- Add the following content to install_dependencies.sh:
#!/bin/bash
sudo yum install tomcat -y
sudo yum -y install httpd
sudo cat << EOF > /etc/httpd/conf.d/tomcat_manager.conf
<VirtualHost *:80>
ServerAdmin root@localhost
ServerName app.nextwork.com
DefaultType text/html
ProxyRequests off
ProxyPreserveHost On
ProxyPass / http://localhost:8080/nextwork-web-project/
ProxyPassReverse / http://localhost:8080/nextwork-web-project/
</VirtualHost>
EOF
What does install_dependencies.sh do?
The install_dependencies.sh script sets up all the software needed to run our website by installing programs (called Tomcat and Apache) that handle internet traffic and host our application. It then creates special settings that let these programs to work together, making our website accessible to visitors on the internet.
💡 Extra for Experts: Can we break down each line in install_dependencies.sh?
This script sets up everything our application needs to run:
- #!/bin/bash: This is like telling the computer "Hey, run this file using bash" (it's called a shebang line).
- sudo yum install tomcat -y: Installs Tomcat (our Java application server) - the -y means "yes to all prompts" so it won't stop and ask questions.
- sudo yum -y install httpd: Installs Apache HTTP server - this is like the front desk receptionist for our web application.
- sudo cat << EOF > /etc/httpd/conf.d/tomcat_manager.conf: This starts a special block that writes multiple lines to a configuration file at once - like dictating a whole letter instead of typing it line by line.
- The lines between <VirtualHost *:80> and </VirtualHost> create a configuration file for Apache to act as a reverse proxy for Tomcat. This is like telling Apache: "When visitors come to our website, don't talk to them directly - instead, silently pass their requests to Tomcat who's running our actual application."
- ProxyRequests off: Tells Apache not to act as a general proxy for any website (a security measure).
- ProxyPreserveHost On: Makes sure Apache remembers who the original request was for.
- ProxyPass and ProxyPassReverse: These are the magic lines that actually connect Apache to our Tomcat application.
- EOF: Marks the end of our configuration block - everything between the first EOF and this one gets written to the file.
💡 Extra for Experts: What is a reverse proxy?
A reverse proxy is like a receptionist for your web application. When visitors (users) come to your site, they talk to the receptionist (Apache) first, who then forwards their requests to the right person (Tomcat) behind the scenes. The visitors never interact directly with the backend server! This is a common pattern in web architecture that gives you more control over how requests are handled.
- Notice the round circle next to the file name at the top of the IDE.
- The round circle tells us that your file is unsaved!
- Save the install_dependencies.sh file by pressing Ctrl+S. You should see the round circle disappear.
Create start_server.sh
- Still inside the scripts folder, let's create a new file again.
- This time, let's name the file start_server.sh
- Add the following content to start_server.sh:
#!/bin/bash
sudo systemctl start tomcat.service
sudo systemctl enable tomcat.service
sudo systemctl start httpd.service
sudo systemctl enable httpd.service
What does this script do?
This script starts both Tomcat (our Java application server) and Apache (our web server) and makes sure they'll restart automatically if the EC2 instance ever reboots.
💡 What is systemctl?
systemctl is the command-line tool for controlling services on modern Linux systems. Think of it as the master control panel for all the programs running in the background on your server. With systemctl, you can start services ("Hey Apache, time to wake up!"), stop them ("Tomcat, take a break"), check their status ("Is MySQL actually running?"), or set them to start automatically on boot. It's an essential tool for server management that gives you a standardized way to control just about any service on your Linux instance.
💡 What does each line in start_server.sh do?
- #!/bin/bash: This tells the system to use the bash shell to interpret this script.
- sudo systemctl start tomcat10: Fires up Tomcat (our Java application server) - like turning the key in the ignition.
- sudo systemctl enable tomcat10: Sets Tomcat to auto-start whenever the server reboots - like setting your car to automatically start every morning.
- sudo systemctl start httpd: Starts Apache HTTP server - turning on our web server frontend.
- sudo systemctl enable httpd: Sets Apache to auto-start on reboot too.
Together, these commands ensure our application is up and running and will stay that way even if the server restarts.
- Save the start_server.sh file by pressing Ctrl+S.
Create stop_server.sh script
- Inside the scripts folder, create a new file named stop_server.sh
- Add the following content to stop_server.sh:
#!/bin/bash
isExistApp="$(pgrep httpd)"
if [[ -n $isExistApp ]]; then
sudo systemctl stop httpd.service
fi
isExistApp="$(pgrep tomcat)"
if [[ -n $isExistApp ]]; then
sudo systemctl stop tomcat.service
fi
What does stop_server.sh do?
This script safely stops web server services by first checking if they're running. It uses pgrep to check for running processes of Apache (httpd) and Tomcat, and only attempts to stop the services if they are actually active. This prevents errors that could occur from trying to stop services that aren't running.
Specifically, the script:
- Checks if Apache (httpd) is running
- Stops Apache if it is active
- Checks if Tomcat is running
- Stops Tomcat if it is active
💡 Extra for Experts: Why do we check if the server is running before stopping it?
We check if the server is running first because trying to stop something that's not running can cause errors that might interrupt our deployment.
This makes our script more robust and reliable. If we blindly tried to stop services regardless of their state, we might get error messages that could cause our deployment to fail unnecessarily. Good scripts anticipate potential issues instead of assuming everything is in an ideal state - that's the difference between code that works in perfect conditions versus code that works in the real world!
- Save the stop_server.sh file by pressing Ctrl+S.
- Check that you have install_dependencies.sh, start_server.sh, and stop_server.sh inside the scripts folder.
Create appspec.yml
- Create a new file, but this time at the root of your project.
- Make sure this file is NOT inside the scripts folder!
- Let's name the file appspec.yml
- Add the following content to appspec.yml:
version: 0.0
os: linux
files:
- source: /target/nextwork-web-project.war
destination: /usr/share/tomcat/webapps/
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/start_server.sh
timeout: 300
runas: root
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 300
runas: root
What does each section in appspec.yml mean?
The appspec.yml file is essentially the instruction manual for CodeDeploy. Here's what each part does:
- version: 0.0: This is just the format version CodeDeploy expects (yes, starting at 0.0 is a bit odd).
- os: linux: Tells CodeDeploy we're deploying to a Linux system, not Windows.
- files: This section maps what files go where:
- source: /target/nextwork-web-project.war: "Take this WAR file from our artifact..."
- destination: /usr/share/tomcat10/webapps/: "...and put it here on the EC2 instance."
- hooks: These are like event triggers that run at specific points in the deployment:
- BeforeInstall: "Before you install the new version, run this script" - we're stopping the server first.
- AfterInstall: "After the files are copied, run this" - we're installing dependencies.
- ApplicationStart: "Once everything's ready, run this" - we're starting the server.
- Each hook specifies the script location, a timeout (5 minutes max), and that it should run as the root user.
Think of appspec.yml as the choreographer that ensures everyone moves in the right sequence during deployment.
💡 Extra for Experts: CodeDeploy lifecycle events
Each phase in the appspec.yml file is a CodeDeploy lifecycle event. Lifecycle events are like the chapters in your deployment story - predefined points where you can hook in custom scripts to perform specific actions. They're the key to customizing exactly how your application gets deployed.
Think of it like baking a cake: there are distinct phases (mixing ingredients, baking, cooling, frosting) where different actions need to happen. CodeDeploy's lifecycle events are similar - BeforeInstall, Install, AfterInstall, ApplicationStart, etc.
- Save the appspec.yml file by pressing Ctrl+S.
Update buildspec.yml File
- Open buildspec.yml and modify the artifacts section to include appspec.yml and the scripts folder:
artifacts:
files:
- target/nextwork-web-project.war
- appspec.yml
- scripts/**/*
discard-paths: no
Recap: What is buildspec.yml?
buildspec.yml is like the recipe card for AWS CodeBuild. It contains step-by-step instructions that tell CodeBuild exactly how to turn your raw code into a deployable package. In our project, it tells CodeBuild how to compile the Java application, run any tests, and then package up everything needed for deployment.
The artifacts section we just edited is particularly important - it's telling CodeBuild: "After you build the app, make sure to include these additional files in the final package." We're adding our appspec.yml and scripts folder because CodeDeploy will need them to properly deploy the application. Without them, CodeDeploy wouldn't know what to do with our compiled code!
- Save the buildspec.yml file by pressing Ctrl+S.
Commit and Push Changes to GitHub
- Open the terminal in VS Code.
- Stage all changes using git add .
- Commit changes with the message "Adding CodeDeploy files" using git commit -m "Adding CodeDeploy files"
- Push changes to your GitHub repository using git push
- Check the output of git push to confirm that your changes are pushed successfully.
Having trouble with git push?
Try resetting your Git credentials, and starting over again! Run these commands in your terminal:
git config --global --unset credential.helper
git config --local --unset credential.helper
git remote set-url origin https://[[GITUSERNAME="<github_username>"]]@github.com/[[GITUSERNAME="<github_username>"]]/[[REPONAME="<repository_name>"]].git
- Make sure to replace <YOUR_GITHUB_USERNAME> with your GitHub username and <YOUR_REPOSITORY_NAME> with your repository name.
- Then try git push again and enter your GitHub PAT when prompted for the password. Check the top of your VS Code window if you're not prompted in the terminal.
Give it another try, and you'll be back on track in no time! If you're still having trouble, don't hesitate to ask for help in the NextWork Community 👋
- Head to your GitHub repository in the browser. Let's check that the scripts folder and appspec.yml file are in your repository!
One of my files are not in the repo!
One of your files might still be unsaved. Make sure to save all your files (open the file in VS Code and press Ctrl+S), run git add ., and commit and push again.
Awesome! You've prepared all the necessary deployment scripts and configuration files. Let's move on to creating a CodeDeploy application.
Set Up CodeDeploy
Now, let's get to know CodeDeploy and set it up to automate the deployment of our web app!
In this step, you're going to:
- Create a CodeDeploy application, which is an application that you want to deploy.
- Create a CodeDeploy deployment group, which is like a folder of deployment settings for the same application.
- Give CodeDeploy the permission to access CodeArtifact.
Create CodeDeploy Application
- Head to the CodeDeploy console.
- Select Applications in the left hand navigation menu.
- Select Create application.
What is a CodeDeploy application?
A CodeDeploy application is like the main folder for your deployment project. It doesn't do much on its own, but it helps you organize everything related to deploying one application.
In more technical, AWS terms, a CodeDeploy application is a namespace or container that groups deployment configurations, deployment groups, and revisions for a specific application. Having separate CodeDeploy applications helps you manage multiple applications without mixing up their deployment resources.
- Enter nextwork-devops-cicd as the Application name.
- Choose EC2/On-premises as the Compute platform.
Extra for Experts: What are CodeDeploy compute platforms?
CodeDeploy's compute platforms are basically the different types of environments where your application can live:
- EC2/On-premises: This is for traditional server-based applications - like what we're doing in this project. Your app runs on actual servers (either in AWS or in your own data center).
- AWS Lambda: This is for serverless applications where you don't manage any servers. Your code just runs when triggered, and AWS handles all the infrastructure.
- Amazon ECS: This is for containerized applications running in Docker containers managed by Amazon's Elastic Container Service.
Choosing the right platform depends on how your application is built. Each has its own advantages for different types of applications!
- That's it! Select Create application.
- Wait for the success message that the application nextwork-devops-cicd is created.
Create Deployment Group
- Select Create deployment group.
What is a CodeDeploy deployment group?
A deployment group is a collection of EC2 instances that are grouped to deploy something together.
It's also where you plan out exactly where and how your application gets deployed on this group of instances. In other words, it's where you tell CodeDeploy "let's deploy this app to these specific servers, using this deployment pattern, with these load balancing settings, and handle failures this way."
The power of deployment groups is that you can have multiple groups within the same application - maybe one for your test environment, another for staging, and a third for production. Each can have different settings and target different sets of servers, but they all deploy the same core application. This adds to the (many) reasons why CI/CD tools are so powerful - in this case, CodeDeploy saves you the time it'd take to manually deploy the same app to each instance in each environment.
- Enter nextwork-devops-cicd-deploymentgroup as the Deployment group name.
- Under Service role, check if you have a service role available...
- Maybe not!
- We'll have to create a new service role.
Why does CodeDeploy need IAM roles?
CodeDeploy needs IAM roles to get permissions to access and manage AWS resources on your behalf. These permissions let CodeDeploy do things like:
- Accessing EC2 instances to deploy applications.
- Reading application artifacts from S3 buckets.
- Updating Auto Scaling groups.
- Write CloudWatch logs about what it's doing
Create an IAM Role for CodeDeploy
- Head to the IAM console.
- In the IAM console, select Roles from the left hand navigation bar.
- Select Create role.
- Choose AWS service as the trusted entity type.
- Choose CodeDeploy as the service and select CodeDeploy as the use case.
- Select Next.
- You'll notice the AWSCodeDeployRole default policy is suggested already - nice! That's all we need.
- Click on the plus button to take a look at the permissions this grants. There are many actions that we're allowing in this policy!
- Phew, this saves us the time from defining all these permission policies ourselves.
- Select Next.
- Expand the AWSCodeDeployRole policy to review its permissions.
- Review the EC2 permissions within the policy.
What permissions does AWSCodeDeployRole policy include?
The AWSCodeDeployRole policy lets CodeDeploy work with your EC2 instances. It includes permissions for:
- Auto Scaling, so CodeDeploy can handle deployments when you're automatically scaling instances up or down
- EC2, so CodeDeploy can interact with your instances - tag them, query them, and deploy to them
- Elastic Load Balancing, so CodeDeploy can temporarily remove instances from load balancers during deployment
- CloudWatch, so CodeDeploy can send logs and metrics so you can monitor what's happening
- S3, so CodeDeploy can access the build artifacts stored in S3 buckets
Not that these permissions are carefully scoped to only what CodeDeploy actually needs to do its job, instead of everything in your AWS account. This is a best practice in security called the "principle of least privilege."
- Select Next.
- Enter NextWorkCodeDeployRole as the Role name.
- Add a description to help you remember why you created this role.
Allows CodeDeploy to call AWS services such as Auto Scaling on your behalf.
Created as a part of NextWork's Cl/CD Pipeline series.
- Review the Permissions policies and make sure AWSCodeDeployRole is attached.
- Select Create role.
- Let's double check that the NextWorkCodeDeployRole is created.
Select Service Role in CodeDeploy
- Head back to the CodeDeploy deployment group configuration tab.
- Looks like we'll need to refresh the page to use the new service role!
- Refresh the page, and re-enter nextwork-devops-cicd-deploymentgroup as Deployment group name.
- Select the newly created NextWorkCodeDeployRole as the Service role.
- Choose In-place as the Deployment type.
Extra for Experts: What is the difference between In-place and Blue/Green deployments?
- In-place deployment: Updates the application on existing instances. During the deployment, the application on the instances is stopped, the new version is installed, and then the application is restarted. This can cause a brief downtime during deployment.
- Blue/green deployment: Creates a new, separate environment (the "green" environment) to deploy the new application version. Once the new version is verified in the green environment, traffic is switched from the old environment (the "blue" environment) to the new one. This minimizes downtime and allows for quick rollbacks.
For most production applications, blue/green is preferred because of the zero downtime and easy rollback, but for our learning project, in-place is simpler and more cost-effective.
- Under Environment configuration, select Amazon EC2 instances.
- In Tag group 1, enter role as the Key.
- Enter webserver as the Value.
Why are we using tags?
We are using tags here so that CodeDeploy can identify the target EC2 instances for deployment. In our CloudFormation template, we tagged our EC2 instance with role: webserver. CodeDeploy will use this tag to find and deploy to the correct instance.
Tags help us be super efficient for three reasons:
- Flexibility: If you add new instances with the same tag, CodeDeploy automatically includes them in future deployments.
- Self-documentation: Tags like role: webserver clearly explain what the instance does, making our AWS environment easier to understand.
- Integration: The CloudFormation template we used earlier already tagged our EC2 instance with role: webserver, so everything works together seamlessly.
Check the line below your tag settings - you might notice that 1 unique matched instance is found.
Can't find any matching instances?
If CodeDeploy isn't finding your EC2 instance, don't worry - this is easy to fix! The issue is usually related to the tags we're using to identify the instance.
When you see "0 instances found" instead of "1 unique matched instance," here's what to check:
- Verify that you entered the exact tag key role and value webserver
- Double-check for any typos or extra spaces
- Check your CloudFormation outputs to confirm the EC2 instance was tagged correctly
- Click Click here for details to view the matched instance.
- You'll see an EC2 instance called NextWorkCodeDeployEC2Stack::WebServer - that's the EC2 instance we launched from our CloudFormation template. This confirms to us that the web app will be deployed onto that instance instance.
Configure Agent and Deployment Settings
- Now let's head back to your CodeDeploy Deployment group set up.
- Under Agent configuration with AWS Systems Manager, select Now and schedule updates and Basic scheduler with 14 Days.
What is CodeDeploy Agent?
The CodeDeploy Agent is a software that lets your EC2 instance communicate with CodeDeploy.
Whenever you initiate a deployment, it's the CodeDeploy Agent that receives the deployment instructions (i.e. bash scripts) from CodeDeploy and carries them out on the EC2 instance.
Setting it to update every 14 days simply makes sure your agent software is always up to date to the latest version that AWS released.
- In Deployment settings, keep the default CodeDeployDefault.AllAtOnce
What are deployment settings?
Deployment settings help you control how quickly you'd like to roll out your application.
CodeDeployDefault.AllAtOnce deploys the application to all instances in the deployment group at the same time. It's the fastest option, but also the riskiest - if something goes wrong, all your instances could be affected at once.
💡 Extra for Experts: How do you know what deployment setting to choose?
For production environments, you might choose more conservative options like OneAtATime (update one instance, make sure it works, then move to the next) or HalfAtATime (update 50% of instances, verify, then do the rest). These slower approaches reduce risk by limiting the blast radius of any potential issues.
For production systems with hundreds of instances, these settings become crucial. Imagine updating your banking app on all servers simultaneously vs. trying it on 10% first to make sure customers can still access their accounts! But since we only have one instance in our project, the cautious approach doesn't offer much benefit.
We're using AllAtOnce in this project because we only have one instance and we're learning - speed is more important than caution here!
- Deselect Enable load balancing.
What is load balancing?
Load balancing directs visitors to whichever server is least busy at the moment. Instead of everyone lining up at one server (which could get overwhelmed), traffic is distributed across multiple servers. When a visitor tries to access your website, they actually connect to the load balancer first. The load balancer then decides which server should handle this particular request - typically choosing the least busy one.
We're not using load balancing in this project because we only have one server - there's nothing to balance between! But in a production environment, you'd almost always use it to improve the availability and performance of your application.
- Select Create deployment group
Phewwww! A deployment group is the longest, most detailed part of CodeDeploy's setup. Nice work making it to the end 👏
Create and Verify Deployment
It's time to put everything together and deploy our web application to the EC2 instance!
In this step, you're going to:
- Create a CodeDeploy deployment.
- Monitor the deployment process.
- Access and verify the deployed web application.
Create Deployment
- In the deployment group details page, select Create deployment.
What is a CodeDeploy deployment?
A CodeDeploy deployment represents a single update to your application, with its own unique ID and history. When you create a deployment, you're telling CodeDeploy:
- Which version of the application to deploy (the revision)
- Where to deploy it (the deployment group)
- How to deploy it (the deployment settings)
CodeDeploy then orchestrates the entire process - stopping services, copying files, running scripts, starting services - and keeps track of whether it succeeds or fails. You can monitor it happening in real-time and see a detailed log of each step.
- Sweet! Some of this deployment configuration is already pre-filled for us.
- Under Revision type, make sure My application is stored in Amazon S3 is selected. That's because our deployment artifact is inside an S3 bucket!
- Head back to your S3 bucket called nextwork-devops-cicd.
- Click into the nextwork-devops-cicd-artifact build artifact.
- Copy the file's S3 URI.
- Paste the S3 URI into the Revision location field in CodeDeploy.
- Select .zip as the Revision file type.
What is a revision location? Why did we use our WAR/zip file?
The revision location is the place where CodeDeploy looks to find your application's build artifacts. We're using the S3 bucket that's storing our WAR file, so CodeDeploy knows where to find the latest version of our web app it's deploying to the deployment EC2 instances!
- Next, we'll leave Additional deployment behavior settings as default.
Extra for Experts: What are Additional deployment behaviors settings and Deployment group overrides?
These settings give you extra flexibility when you need to handle special cases without changing your standard deployment settings:
- Additional deployment behaviors settings let you configure options like how to handle file permissions during deployment or whether to immediately allow traffic to new instances.
- Deployment group overrides let you to override deployment group settings for a specific deployment - maybe you normally deploy one instance at a time (safe but slow), but for a critical security fix, you want to override that and deploy to all instances at once (faster but riskier).
- Select Create deployment.
- Off we go! CodeDeploy kicks off a deployment of your web app.
- Scroll down to Deployment lifecycle events and monitor the events by clicking View events.
- See the lifecycle events progressing, such as BeforeInstall, ApplicationStart, etc. These are the events you defined in appspec.yml!
- Whoops! After a few minutes of waiting, you might notice that you hit an error...
- Why do you think your deployment failed?
- Don't forget, CodeDeploy is deploying your web app by grabbing the latest build artifact from your S3 bucket.
- When was the last time you ran a build on CodeBuild? 🤔
- Aha - our last time running a build was before we added appspec and the deployment scripts!
- Because of this, your deployment instance isn't getting any of the scripts you've written - causing the error you're seeing now.
- Head back to your CodeBuild build project, and rebuild your project.
- Once your second build is a success, return to CodeDeploy and retry the deployment.
Check Your Deployed App!
- Wait until the deployment status says Success.
Is your deployment still failing?
If your deployment isn't succeeding, you're hitting a common challenge in the DevOps journey! Let's troubleshoot together:
- Check the CodeDeploy deployment details and lifecycle events for specific error messages
- Check your EC2 security group rules to ensure port 80 is open for your IP address
- If you end up making any changes to your web app code, make sure to push your changes and rebuild the web app before redeploying!
If you're still running into any issues, share a screenshot of your error message with the NextWork community.
- Select the Instance ID in the Deployment lifecycle events panel. This takes you to the deployment EC2 instance you launched with CloudFormation.
- Get the Public IPv4 DNS of your EC2 instance from the EC2 console.
- Open the Public IPv4 DNS in a web browser.
- It might take a minute or two for the application to become fully accessible after deployment.
I don't see the application!
Check whether your URL is using https - if it is, change it to http in the browser. This is because the EC2 instance's security group lets in connections on port 80 (HTTP) but not port 443 (HTTPS).
If you still don't see the application, it might be because your IP address has changed. You can check your IP address by visiting http://checkip.amazonaws.com/. Double check the IP address with another site, like https://whatismyipaddress.com/. If your IP address changed, head to your EC2 instance's security group and update its settings to allow your new IP address.
- WOOOHOOO! Welcome to your application!
Congratulations! You've successfully automated a web app's deployment to EC2 using AWS CodeDeploy!
Awesome work 👏
Secret mission
Want to experience the full power of CodeDeploy?
Here's your chance to go beyond just deployment automation!
Your secret mission, should you choose to accept it, is to conduct a deployment recovery - a critical skill in scenarios where a failed deployment takes down your application.
In this secret mission, you're going to:
- Intentionally create a broken deployment (don't worry, it's safe!)
- Watch it fail in real-time
- Perform an emergency recovery to restore service
- Showcase advanced disaster recovery skills in your documentation!
Disaster Recovery with CodeDeploy
Delete your resources
Delete your resources
Now that we've built and tested our automated deployment pipeline, it's time to clean up the resources we created. This is important to avoid getting charged for unused resources.
Resources to delete:
- Delete the CodeDeploy application.
- Delete the CodeBuild project.
- Delete the CodeArtifact domain and repository.
- Delete the CloudFormation stack.
- Delete the CodeConnection connection
- Delete the development EC2 instance and key pair
- Delete the IAM roles and policies.
- Delete the S3 bucket created by CloudFormation.
- Delete the S3 bucket containing the build artifact.
- Delete the GitHub personal access token.
Code Services
CodeDeploy
- Head to the CodeDeploy console.
- Select Applications from the left hand menu.
- Select the application named nextwork-devops-cicd
- Select Delete application.
- Confirm the deletion when prompted.
- Verify that the CodeDeploy application is deleted.
CodeBuild
- Head to the CodeBuild console.
- Select Build projects from the left hand menu.
- Select the nextwork-devops-cicd project.
- Click Actions and select Delete build project.
- Type delete in the confirmation field.
- Select Delete.
CodeArtifact
- Head to the CodeArtifact console.
- Select Repositories from the left hand menu.
- Select nextwork-devops-cicd.
- Select Delete repository.
- Confirm the deletion by typing delete and selecting Delete repository.
- After deleting the repository, select Domains from the left hand menu.
- Select nextwork.
- Select Delete domain.
- Confirm the deletion by typing delete and selecting Delete domain.
Why delete CodeArtifact resources?
CodeArtifact charges are based on storage and API requests. Even if you're not actively using your repository, you'll continue to pay for storing any packages that were cached during your project work.
CodeConnection
- Expand the Settings arrow at the bottom of the left hand navigation panel.
- Select Connections.
- Select your connection.
- Select Delete
- Confirm the deletion by typing delete and clicking Delete.
Don't forget to delete your other resources!
CloudFormation
- Head to the CloudFormation console.
- Select Stacks from the left hand menu.
- Select the stack named NextWorkCodeDeployEC2Stack
- Select Delete.
- Confirm the deletion when prompted.
- Wait for the stack status to become DELETE_COMPLETE
Why delete the CloudFormation Stack?
Deleting the CloudFormation stack is like hitting a master "clean up" button that removes everything we created, all at once. This is incredibly convenient because CloudFormation remembers every single resource it created - the EC2 instance, security groups, VPC, subnets, route tables - and deletes them all in the correct order.
Need these resources again later? Just run the CloudFormation template again and you'll have an identical environment in minutes.
EC2
EC2 instance
- Head to the EC2 console.
- Select Instances from the left hand menu.
- Select your instance (nextwork-devops-yourname).
- Select Instance state > Terminate instance.
- Select Terminate to confirm.
- Wait for the instance to show "Terminated" status before proceeding with other cleanup tasks.
Why terminate your EC2 instance?
EC2 instances continue to incur charges as long as they're in the "running" state. By terminating the instance (not just stopping it), you ensure you won't be billed for compute resources you're no longer using.
Key pair
Note: If you plan to continue this DevOps Challenge (totally recommend!), you can keep your key pair and use the same credentials for the rest of this series. AWS does not charge for key pairs. Store your private key in a safe place.
- Still in your EC2 console, select Key Pair from the left hand navigation panel.
- Select the checkbox next to your key pair.
- Select the Actions dropdown, and then Delete.
- Type Delete in the text field, and select Delete.
- Don't forget to delete your private key pair in your local computer - delete the entire DevOps foler!
IAM
- Head to the IAM console.
- Select Roles from the left hand menu.
- Search for EC2-instance-nextwork-cicd.
- Select the role.
- Select Delete.
- Confirm the deletion by typing Delete and selecting Delete.
- Delete codebuild-nextwork-web-build-service-role next.
- Delete NextWorkCodeDeployRole next.
- Next, select Policies from the left hand menu.
- Search for codeartifact-nextwork-consumer-policy
- Select the policy.
- Select Delete policy.
- Confirm the deletion by typing Delete and selecting Delete.
Why clean up IAM resources?
While IAM roles and policies don't incur direct costs, maintaining unused permissions increases your security risk surface area and complicates access management. Following the principle of least privilege means removing access rights when they're no longer needed.
S3
Delete S3 Bucket (CloudFormation)
Huh, what do you mean by a CloudFormation S3 bucket?
When you upload your template, CloudFormation stores it in S3 so it always has a clean copy to reference during the deployment process. This is super handy because S3 is incredibly reliable (99.999999999% durability!) and highly available. Even if a whole AWS data center had issues, your template would still be safe and accessible.
- Head to the S3 console.
- Select Buckets from the left hand menu.
- Find the bucket starting with nextworkcode-deployec2stack-
- Select the bucket.
- Select Empty.
- Type permanently delete in the confirmation field.
- Select Empty to confirm emptying the bucket.
- Once the bucket is empty, select it again and select Delete.
- Type the bucket name in the confirmation field.
- Confirm the deletion.
Delete S3 Bucket (Artifact)
- Head to the S3 console.
- Select Buckets from the left hand menu.
- Find the bucket named nextwork-devops-cicd
- Select the bucket.
- Select Empty.
- Type permanently delete in the confirmation field.
- Select Empty to confirm emptying the bucket.
- Once the bucket is empty, select it again and select Delete.
- Type the bucket name in the confirmation field.
- Confirm the deletion.
Why optionally delete the S3 Bucket containing the artifact?
If you created a dedicated S3 bucket for storing your application artifact (nextwork-devops-cicd), deleting it is optional. S3 storage costs are generally low, but deleting unused buckets helps keep your AWS account tidy and avoids any potential storage charges. If you plan to do more CodeDeploy projects, you might want to keep this bucket for future artifacts.
GitHub PAT
- Head to your GitHub account.
- Select your profile icon in the top right corner of the screen.
- Select Settings from the dropdown menu.
- In the left sidebar, select Developer settings.
- Select Personal access tokens, then Tokens (classic).
- Find the PAT you created for this project.
- Select Delete next to the token.
- Confirm the deletion.
Local Files
- Go to your Desktop or wherever you stored your project files.
- Delete the DevOps folder containing the nextwork-keypair.pem file.
- You can also delete any local copies of the web app code.
Why delete key pair files?
The .pem file is essentially a private key that provides access to your EC2 instance. Even though the instance is terminated, it's a security best practice to delete private keys when you no longer need them to prevent any unauthorized access if you ever reuse the key name.
Get your documentation!
Get your documentation!
Nice work! ⚙️ You've just automated web application deployment to EC2 using AWS CodeDeploy.
You've learned how to:
- 👏 Launch a deployment architecture using CloudFormation.
- ✍️ Prepare deployment scripts and an appspec.yml file for CodeDeploy.
- ⚙️ Configure a CodeDeploy application and deployment group.
- 🚀 Deploy your web application using CodeDeploy (woah!).
- 💎 Implement disaster recovery and roll back a deployment!
Ready to quiz yourself? You got this! 💪
p.s. Does it say "Still tasks to complete!" at the bottom of the screen?
This means you still have screenshots left to upload, or questions left to answer!
- Press Ctrl+F (Windows) or Command+F (Mac) on your keyboard.
- Search for the text Return to later.
- Jump straight to your incomplete tasks!
- 🙋♀️ Still stuck? Ask the community!