Automate AWS Infrastructure with Terraform
Provision AWS resources with Terraform, validate with Python, and orchestrate with Bash.
Introduction
30 Second Summary
Every time you use a website, there are servers, databases, and storage systems running behind the scenes. DevOps engineers don't set these up by clicking buttons one at a time. They write code that builds infrastructure automatically.
In this project, you will build an automation pipeline that provisions real cloud infrastructure, validates it programmatically, and orchestrates the entire workflow from a single command.
What You'll Build
A DevOps automation pipeline that uses Terraform to provision AWS cloud infrastructure, Python to validate the deployment, and Bash to orchestrate the entire workflow.
By the end of this project, you'll have:
- Terraform configuration files (main.tf, variables.tf, outputs.tf) that define an S3 bucket and DynamoDB table in your own AWS account.
- A Python validation script (validate.py) that uses the AWS SDK to programmatically verify your resources deployed correctly.
- A Bash orchestration script (deploy.sh) that runs the full pipeline with one command: initialize, plan, deploy, validate, and report.
- Secret Mission: Upload a test file to your S3 bucket using Python and verify the upload in your Bash pipeline.
Are there any prerequisites?
No prior experience with Terraform, AWS, or Bash scripting is needed. This project walks you through installing and configuring every tool from scratch. You just need a Windows computer with VS Code and Git Bash already installed.
Before We Start
Before We Start...
Install Python, Terraform, and AWS CLI
Before you can automate cloud infrastructure, you need a professional DevOps toolchain on your machine. Three tools form the foundation of this project: Python for scripting, Terraform for infrastructure provisioning, and the AWS CLI for cloud access.
In this step, you will install all three tools and verify they work from your terminal. Each tool plays a distinct role in the automation pipeline you will build.
In this step, get ready to:
- Install Python 3.14.5 on your machine.
- Install Terraform 1.15.5 and add it to your system PATH.
- Install AWS CLI v2 and verify all three tools from PowerShell.
Install Python 3.14
Python is the scripting language you will use to write a validation script that checks whether your cloud resources deployed correctly.
- Open PowerShell by pressing the Windows key, typing PowerShell, and pressing Enter.
- Check if Python is already installed by running this command:
python --version
✔️ I see Python 3.14 or higher
Python is ready to go. Move on to the next substep below.
ⓧ I see an older version
You have an older Python version installed. Install Python 3.14.5 alongside it by following the steps below.
- Open https://www.python.org/downloads/release/python-3145/ in your browser.
- Scroll down to the Files section and click the Windows installer (64-bit) link to download it.
- Run the downloaded installer.
- On the first screen, check the box labelled Add python.exe to PATH before clicking Install Now.
- Wait for the installation to complete, then click Close.
- Close PowerShell and reopen it (press the Windows key, type PowerShell, press Enter).
- Run the version check again:
python --version
You should now see Python 3.14.5 in the output.
ⓧ Command not found
Python is not installed yet. Follow these steps to install it.
- Open https://www.python.org/downloads/release/python-3145/ in your browser.
- Scroll down to the Files section and click the Windows installer (64-bit) link to download it.
- Run the downloaded installer.
- On the first screen, check the box labelled Add python.exe to PATH before clicking Install Now.
- Wait for the installation to complete, then click Close.
- Close PowerShell and reopen it (press the Windows key, type PowerShell, press Enter).
- Run the version check again:
python --version
You should now see Python 3.14.5 in the output.
Still not seeing Python 3.14?
If python --version still shows an older version after installing, the old Python may be earlier in your PATH. Close all PowerShell windows and open a fresh one to pick up the PATH change.
If the issue persists, re-run the installer, select Modify, and make sure the PATH option is enabled.
help me fix my Python version showing as older than 3.14 even after installing
Install Terraform
Terraform is the Infrastructure as Code tool that will provision your AWS resources from configuration files.
- Check if Terraform is already installed by running this command in PowerShell:
terraform --version
✔️ I see Terraform v1.15 or higher
Terraform is ready to go. Move on to the next substep below.
ⓧ I see an older version
You have an older Terraform version. Replace it with 1.15.5 by following the steps below.
- Open https://developer.hashicorp.com/terraform/install in your browser.
- Download the AMD64 binary for Windows.
- Open the downloaded zip file and extract terraform.exe to C:\terraform (create this folder if it does not exist).
- If your old Terraform was at a different location, you can remove the old entry from PATH. Make sure C:\terraform is in your system PATH (follow the PATH instructions in the next tab if needed).
- Close and reopen PowerShell, then verify:
terraform --version
You should see Terraform v1.15.5 in the output.
ⓧ Command not found
Terraform is not installed yet. Follow these steps to install it.
- Open https://developer.hashicorp.com/terraform/install in your browser.
- Download the AMD64 binary for Windows.
- Create a folder called C:\terraform on your computer.
- Open the downloaded zip file and extract terraform.exe into C:\terraform.
- Now add C:\terraform to your system PATH so Windows can find it. Press the Windows key and type Edit the system environment variables, then press Enter.
- In the System Properties window, click Environment Variables.
- Under System variables, find and select Path, then click Edit.
- Click New and type C:\terraform.
- Click OK on all three windows to save.
- Close and reopen PowerShell so it picks up the new PATH.
- Verify the installation by running:
terraform --version
You should see Terraform v1.15.5 in the output.
Why add to system PATH?
The PATH environment variable tells Windows where to look for executable files. Without C:\terraform in your PATH, you would have to type the full file path every time you run a Terraform command.
Adding it to PATH lets you type terraform from any directory and Windows will find it automatically.
Terraform still not recognized?
Make sure you reopened PowerShell after editing the PATH. Existing terminal windows do not pick up PATH changes.
Also confirm that terraform.exe is directly inside C:\terraform (not in a subfolder like C:\terraform\terraform_1.15.5).
help me fix Terraform not being found in my PATH on Windows
Install AWS CLI v2
The AWS CLI is the command-line tool that lets you interact with your AWS account from the terminal. Terraform and your Python script both rely on the credentials that the AWS CLI manages.
- Check if the AWS CLI is already installed by running this command in PowerShell:
aws --version
✔️ I see aws-cli/2.x.x
The AWS CLI v2 is ready to go. Move on to the next step.
ⓧ I see aws-cli/1.x.x
You have version 1 of the AWS CLI. Version 2 is a separate install. Follow the instructions below to install it.
- Download the AWS CLI v2 installer from https://awscli.amazonaws.com/AWSCLIV2.msi.
- Run the downloaded AWSCLIV2.msi file and follow the installer prompts (accept the defaults).
- Close and reopen PowerShell after the installation finishes.
- Verify by running:
aws --version
You should see aws-cli/2 at the start of the output.
ⓧ Command not found
The AWS CLI is not installed yet. Follow these steps to install it.
- Download the AWS CLI v2 installer from https://awscli.amazonaws.com/AWSCLIV2.msi.
- Run the downloaded AWSCLIV2.msi file and follow the installer prompts (accept the defaults).
- Close and reopen PowerShell after the installation finishes.
- Verify by running:
aws --version
You should see aws-cli/2 at the start of the output.
AWS CLI still not recognized?
The MSI installer adds the AWS CLI to your PATH automatically. Make sure you closed and reopened PowerShell after the install completed.
If it still does not work, restart your computer to force Windows to reload the PATH.
help me troubleshoot why aws --version is not working after installing the AWS CLI MSI
Your DevOps toolchain is installed and verified. Next up, you will set up your AWS account and connect the CLI to it so Terraform can provision real cloud resources.
Set Up Your AWS Account and Configure Credentials
Your DevOps toolchain is installed and ready. Now you need to connect it to the cloud.
In this step, you will create an AWS account, set up a dedicated user for Terraform, and configure the AWS CLI to authenticate with your account. This gives Terraform permission to create real cloud resources on your behalf.
In this step, get ready to:
- Create a free AWS account.
- Create an IAM user with access keys for Terraform.
- Configure the AWS CLI with your credentials.
Create a free AWS account
AWS offers a free plan for new accounts that includes $100 in credits and access to Always Free services. The resources you create in this project fall within those free limits.
- Open your browser and go to aws.amazon.com/free.
- Click Create a Free Account.
- Follow the sign-up wizard to create your account. You will need an email address, a payment method (for identity verification only), and a phone number.
- When asked to choose a support plan, select the Free plan.
What does the AWS Free plan include?
New AWS accounts receive $100 in credits at sign-up. The free plan lasts up to 6 months or until credits are depleted.
DynamoDB (which you will use later) is part of the Always Free tier. It offers 25 GB of storage plus 25 provisioned read and write capacity units at no cost, regardless of your credit balance.
Create an IAM user with access keys
Instead of using your root account directly, you will create a dedicated IAM user for Terraform. IAM (Identity and Access Management) lets you create users with specific permissions for programmatic access.
- Sign in to the AWS Management Console at console.aws.amazon.com.
- In the top search bar, type IAM and select the IAM service from the results.
- In the left navigation pane, click Users.
- Click Create user.
- For User name, enter terraform-user.
- Click Next.
Now you will attach permissions to this user.
- On the Set permissions page, select Attach policies directly.
- In the search box under Permissions policies, type AdministratorAccess.
- Select the checkbox next to AdministratorAccess.
- Click Next, then click Create user.
Your user is created. Now you need to generate access keys for CLI use.
- Click the user name terraform-user to open the user details page.
- Select the Security credentials tab.
- Scroll down to Access keys and click Create access key.
- Select Command Line Interface (CLI) as the use case.
- Check the confirmation checkbox at the bottom, then click Next.
- Click Create access key.
Save your credentials now
You will see an Access Key ID and a Secret Access Key on this page. This is your only opportunity to view the Secret Access Key. Copy both values and save them somewhere safe (a password manager or a temporary text file).
Do not share these credentials with anyone or commit them to version control. You will use them in the next substep.
What is IAM?
IAM (Identity and Access Management) is the AWS service that controls who can do what in your account. Instead of using your root credentials everywhere, you create IAM users with only the permissions they need.
The AdministratorAccess policy gives terraform-user full permissions. This is fine for learning but in production you would scope permissions down to only what Terraform needs.
Configure the AWS CLI
The AWS CLI stores your credentials locally so that Terraform and other tools can authenticate with AWS automatically.
- Configure the AWS CLI with your new credentials by running this command in PowerShell:
aws configure
What does this command do?
This command launches an interactive prompt that asks for four values. It saves them to credential and config files on your machine so that all AWS tools can find them automatically.
The command prompts you for four values. Enter them one at a time as each prompt appears:
- For AWS Access Key ID: paste the Access Key ID you saved earlier.
- For AWS Secret Access Key: paste the Secret Access Key you saved earlier.
- For Default region name: type us-east-1.
- For Default output format: type json.
Now verify that your credentials are working correctly.
- Confirm your identity by running this command:
aws sts get-caller-identity
You should see JSON output containing your Account ID, UserId, and an ARN that includes terraform-user. It looks similar to this:
{
"UserId": "AIDASAMPLEUSERID",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/terraform-user"
}
Seeing an error instead of JSON output?
- If you see a "could not connect" error, check your internet connection and try again.
- If you see "InvalidClientTokenId" or "SignatureDoesNotMatch", your Access Key ID or Secret Access Key was entered incorrectly. Run aws configure again and carefully re-paste both values.
- If PowerShell does not recognize aws, close and reopen PowerShell so it picks up the PATH changes from the AWS CLI installation.
Still stuck? Help me debug my aws sts get-caller-identity error.
Your AWS account is live and your CLI is authenticated. Next up, you will write the Terraform configuration that defines your cloud infrastructure.
Write Your Terraform Configuration
Your AWS account is configured and your credentials are working. Now it's time to define the infrastructure you want to build.
Terraform uses configuration files to describe your desired cloud resources. You write what you want, and Terraform figures out how to create it. In this step, you will write three configuration files that define an S3 bucket and a DynamoDB table.
In this step, get ready to:
- Create your project folder and define input variables.
- Write the main Terraform configuration with your AWS resources.
- Write an outputs file that exports resource details for other scripts.
Create your project folder and variables file
Every Infrastructure as Code project lives in its own folder. You will create a dedicated folder on your Desktop, then define the input variables that make your configuration reusable.
- In VS Code, open the integrated terminal by pressing Ctrl+` (the backtick key next to the 1 key).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir infra-automation
cd infra-automation
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the infra-automation folder, and click Open.
Why open the folder in VS Code?
Opening a folder as your workspace means VS Code shows all your project files in the Explorer sidebar. Any terminal you open from here will start in the infra-automation directory automatically.
- If VS Code asks whether you trust the authors of this folder, click Yes, I trust the authors.
- In the Explorer sidebar on the left, hover over the folder name and click the New File icon (a page with a plus sign).
- Type variables.tf and press Enter to create the file.
- Paste the following code into variables.tf:
variable "aws_region" {
description = "AWS region to deploy resources"
type = string
default = "us-east-1"
}
variable "project_name" {
description = "Name prefix for all resources"
type = string
default = "devops-automation"
}
What does this code do?
- variable "aws_region" defines which AWS region your resources will be created in. The default is us-east-1 so you don't have to specify it every time you run Terraform.
- variable "project_name" sets a name prefix that all your resources will share. This keeps your resource names consistent and identifiable.
- Save variables.tf by pressing Ctrl+S.
- In the Explorer sidebar, you should see variables.tf listed under your infra-automation folder.
Don't see variables.tf in the sidebar?
- Make sure you opened the infra-automation folder with File > Open Folder. If you opened a parent folder, the file might be nested one level deeper than expected.
- Check that you typed the filename as variables.tf (not .txt). The .tf extension tells VS Code this is a Terraform file.
Why isn't my variables.tf file showing up in VS Code?
Write main.tf
The main.tf file is the core of your Terraform project. It tells Terraform which cloud provider to use and defines the actual resources to create.
- In the Explorer sidebar, click the New File icon again.
- Type main.tf and press Enter.
- Paste the following code into main.tf:
terraform {
required_version = "~> 1.15"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_s3_bucket" "project_bucket" {
bucket_prefix = "${var.project_name}-"
tags = {
Name = "${var.project_name}-bucket"
Environment = "learning"
ManagedBy = "terraform"
}
}
resource "aws_dynamodb_table" "project_table" {
name = "${var.project_name}-table"
billing_mode = "PROVISIONED"
read_capacity = 1
write_capacity = 1
hash_key = "ID"
attribute {
name = "ID"
type = "S"
}
tags = {
Name = "${var.project_name}-table"
Environment = "learning"
ManagedBy = "terraform"
}
}
What does this code do?
- The terraform block pins your project to Terraform 1.15.x and declares the AWS provider (version ~> 6.0). This ensures everyone on your team uses compatible versions.
- The provider "aws" block configures the region using your aws_region variable. Terraform automatically picks up your AWS CLI credentials from the terraform-user you configured earlier.
- The aws_s3_bucket resource uses bucket_prefix instead of a fixed name. S3 bucket names must be globally unique, so Terraform appends a random suffix to your prefix automatically.
- The aws_dynamodb_table resource creates a table with provisioned capacity (1 read unit, 1 write unit). The hash_key named ID of type S (String) is the table's partition key.
Why use bucket_prefix instead of bucket?
S3 bucket names are globally unique across all AWS accounts worldwide. If you hardcoded a name like devops-automation-bucket, someone else may already own it. Using bucket_prefix lets Terraform generate a unique suffix for you, avoiding naming collisions entirely.
- Save main.tf by pressing Ctrl+S.
- In the Explorer sidebar, you should now see both main.tf and variables.tf listed in your project.
Seeing red squiggly lines in main.tf?
- If VS Code highlights syntax errors, make sure you copied the entire code block including the closing braces. Every opening { needs a matching }.
- Check that the attribute block is inside the aws_dynamodb_table resource, not outside it.
- String values must be wrapped in double quotes. Variable references like var.aws_region do not use quotes.
Help me fix the syntax errors in my main.tf Terraform file.
Write outputs.tf
Outputs let Terraform export values after it creates your resources. Your Python validation script will use these outputs to know which bucket and table to check.
- In the Explorer sidebar, click the New File icon.
- Type outputs.tf and press Enter.
- Paste the following code into outputs.tf:
output "s3_bucket_name" {
description = "Name of the created S3 bucket"
value = aws_s3_bucket.project_bucket.id
}
output "dynamodb_table_name" {
description = "Name of the created DynamoDB table"
value = aws_dynamodb_table.project_table.name
}
output "aws_region" {
description = "AWS region where resources are deployed"
value = var.aws_region
}
What does this code do?
- output "s3_bucket_name" exports the actual bucket name that Terraform generates (including the random suffix from bucket_prefix).
- output "dynamodb_table_name" exports the table name so your scripts can find it programmatically.
- output "aws_region" exports the region, which your validation script needs to connect to the right AWS endpoint.
These three values are everything your Python script needs to verify the infrastructure.
- Save outputs.tf by pressing Ctrl+S.
- Your Explorer sidebar should now show all three files: main.tf, outputs.tf, and variables.tf.
Missing one of the three files?
- Confirm you created each file with the .tf extension, not .txt or .tf.txt. Windows sometimes appends a hidden extension.
- Make sure all three files are at the top level of infra-automation, not inside a subfolder.
Help me check that my Terraform project files are set up correctly.
✔️ Awesome, I've got everything!
Great. Double check you have saved all three files before moving on.
ⓧ I'd like to double check the full code
Here is the full content of each file for comparison.
variable "aws_region" {
description = "AWS region to deploy resources"
type = string
default = "us-east-1"
}
variable "project_name" {
description = "Name prefix for all resources"
type = string
default = "devops-automation"
}
terraform {
required_version = "~> 1.15"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_s3_bucket" "project_bucket" {
bucket_prefix = "${var.project_name}-"
tags = {
Name = "${var.project_name}-bucket"
Environment = "learning"
ManagedBy = "terraform"
}
}
resource "aws_dynamodb_table" "project_table" {
name = "${var.project_name}-table"
billing_mode = "PROVISIONED"
read_capacity = 1
write_capacity = 1
hash_key = "ID"
attribute {
name = "ID"
type = "S"
}
tags = {
Name = "${var.project_name}-table"
Environment = "learning"
ManagedBy = "terraform"
}
}
output "s3_bucket_name" {
description = "Name of the created S3 bucket"
value = aws_s3_bucket.project_bucket.id
}
output "dynamodb_table_name" {
description = "Name of the created DynamoDB table"
value = aws_dynamodb_table.project_table.name
}
output "aws_region" {
description = "AWS region where resources are deployed"
value = var.aws_region
}
Your Terraform configuration is complete. Next up, you will run Terraform to deploy these resources to your real AWS account and see them come to life.
Deploy Your Infrastructure with Terraform
Your Terraform configuration files are complete. You have a blueprint that describes an S3 bucket and a DynamoDB table, but nothing exists in AWS yet.
Terraform's core workflow has three commands that take you from configuration to live resources. You'll initialize the project to download the AWS provider plugin, preview exactly what Terraform plans to create, and then deploy real infrastructure to your AWS account.
In this step, get ready to:
- Initialize your Terraform project and download the AWS provider plugin.
- Preview the deployment plan to see exactly what will be created.
- Apply the configuration to provision real AWS resources.
Initialize Terraform
Before Terraform can create anything, it needs to download the provider plugin that knows how to talk to AWS. The terraform init command handles this setup.
- In VS Code, click Terminal in the top menu bar, then click New Terminal.
- Confirm the terminal opens inside your infra-automation folder (you should see the folder name in your terminal prompt).
- Initialize your Terraform project by running this command:
terraform init
What does this command do?
terraform init reads your main.tf file, sees that you require the hashicorp/aws provider, and downloads it into a hidden .terraform directory.
This is always the first command you run after writing a new Terraform configuration. Without it, Terraform does not know how to communicate with AWS.
- You should see output ending with the message:
Terraform has been successfully initialized!
Don't see the success message?
- Make sure your terminal is inside the infra-automation folder. If not, run cd infra-automation to navigate there and try again.
- Check that your main.tf file is saved. Terraform reads from disk, so unsaved changes won't be picked up.
- If you see a network error, check your internet connection. Terraform needs to download the provider plugin from the registry.
help me fix my terraform init error
Preview the deployment plan
Before creating anything, Terraform lets you preview exactly what it will do. This is a safety net that prevents unexpected changes to your cloud account.
- Preview what Terraform will create by running:
terraform plan
What does this command do?
terraform plan compares your configuration files to the current state of your AWS account. Since nothing exists yet, it shows everything it needs to create.
Look for the summary line at the bottom: Plan: 2 to add, 0 to change, 0 to destroy. This confirms Terraform will create your S3 bucket and DynamoDB table.
- Review the plan output. You should see two resources marked with + create and a summary line showing 2 resources to add.
Seeing an error instead of a plan?
- If you see a credentials error, your AWS CLI may not be configured. Run aws sts get-caller-identity to check. If that fails, re-run aws configure with your access keys.
- If you see a syntax error, double-check your main.tf and variables.tf files are saved and match the code from the previous step.
help me fix my terraform plan error
Apply the configuration
The plan looks good. Now it's time to make it real. Terraform will provision your S3 bucket and DynamoDB table in AWS.
Heads up: Terraform will ask for confirmation
When you run terraform apply, Terraform shows the plan one more time and then asks you to confirm. You need to type yes and press Enter to proceed.
- Deploy your infrastructure by running:
terraform apply
- When you see the prompt asking Do you want to perform these actions?, type yes and press Enter.
What happens during apply?
Terraform sends API calls to AWS to create each resource. You'll see lines like aws_s3_bucket.project_bucket: Creating... followed by Creation complete for each resource.
At the end, Terraform prints the outputs you defined in outputs.tf: your bucket name, table name, and region. These outputs are important for the next step.
- Confirm you see the success message: Apply complete! Resources: 2 added, 0 changed, 0 destroyed..
- Note the three outputs printed at the bottom (s3_bucket_name, dynamodb_table_name, aws_region). You'll use these in the next step.
Apply failed or timed out?
- If you see an access denied error, your IAM user may not have the AdministratorAccess policy attached. Check the user's permissions in the AWS Console under IAM > Users.
- If the bucket creation failed with a naming conflict, bucket_prefix should handle uniqueness automatically. Try running terraform apply again.
- If you accidentally typed something other than yes at the prompt, Terraform cancels the operation. Run terraform apply again and type yes this time.
help me fix my terraform apply error
Your infrastructure is live in AWS. Next up, you'll write a Python script that programmatically verifies these resources exist and are correctly configured.
Write and Run a Python Validation Script
Your S3 bucket and DynamoDB table are live in AWS. But how do you know they actually deployed correctly?
In this step, you'll write a Python script that programmatically verifies your infrastructure. Instead of clicking around the AWS Console, your script will use the AWS SDK (boto3) to check each resource and print a clear PASS/FAIL report.
In this step, get ready to:
- Set up a Python virtual environment and install boto3.
- Write a validation script that checks your S3 bucket and DynamoDB table.
- Run the script and confirm both resources pass validation.
Set up a Python virtual environment and install boto3
A virtual environment is an isolated Python workspace. It keeps your project's packages separate from your system-wide Python installation.
- Create a virtual environment inside your infra-automation folder by running this command in PowerShell:
python -m venv venv
- Activate the virtual environment by running:
venv\Scripts\activate
Your terminal prompt should now show (venv) at the beginning, confirming the environment is active.
Seeing an error about running scripts being disabled?
PowerShell blocks script execution by default on Windows. Fix this for your current session by running:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
This change only applies to your current PowerShell session. After running it, retry the activation command above.
Help me fix my PowerShell execution policy error.
- Install the AWS SDK for Python by running:
pip install boto3
You should see output ending with Successfully installed boto3-... and its dependencies.
What is boto3?
boto3 is the official AWS SDK for Python. It lets your Python code interact with AWS services like S3 and DynamoDB programmatically.
Your validation script will use it to check whether your deployed resources exist and are configured correctly.
pip install failed?
Make sure you see (venv) in your prompt before running pip install. If you don't, the virtual environment isn't active. Re-run the activation command.
Help me troubleshoot my pip install issue.
Write the validation script
Your validation script will accept three command-line arguments (bucket name, table name, region) and use boto3 to verify each resource exists.
- In VS Code, create a new file called validate.py inside your infra-automation folder.
- Add the imports and S3 validation function by pasting this code:
import sys
import boto3
def validate_s3_bucket(bucket_name, region):
"""Check if the S3 bucket exists."""
s3 = boto3.client("s3", region_name=region)
try:
s3.head_bucket(Bucket=bucket_name)
print(f" [PASS] S3 bucket '{bucket_name}' exists")
return True
except Exception as e:
print(f" [FAIL] S3 bucket '{bucket_name}' not found: {e}")
return False
What does this code do?
The validate_s3_bucket function creates an S3 client and calls head_bucket. This is a lightweight API call that checks whether a bucket exists without downloading any data.
If the call succeeds, the bucket exists and the function returns True. If it throws an exception, the bucket was not found.
You won't see a change yet. The next chunk adds the DynamoDB validation function.
- Add the DynamoDB validation function below the S3 function:
def validate_dynamodb_table(table_name, region):
"""Check if the DynamoDB table exists and is active."""
dynamodb = boto3.client("dynamodb", region_name=region)
try:
response = dynamodb.describe_table(TableName=table_name)
status = response["Table"]["TableStatus"]
if status == "ACTIVE":
print(f" [PASS] DynamoDB table '{table_name}' is ACTIVE")
return True
else:
print(f" [WARN] DynamoDB table '{table_name}' status is {status}")
return False
except Exception as e:
print(f" [FAIL] DynamoDB table '{table_name}' not found: {e}")
return False
What does this code do?
The validate_dynamodb_table function calls describe_table to get metadata about the table. It then checks that the table's status is ACTIVE, confirming it is fully provisioned and ready to use.
You won't see a change yet. The next chunk adds the main function that ties everything together.
- Add the main function below the DynamoDB function:
def main():
if len(sys.argv) != 4:
print("Usage: python validate.py <bucket_name> <table_name> <region>")
sys.exit(1)
bucket_name = sys.argv[1]
table_name = sys.argv[2]
region = sys.argv[3]
print("")
print("=== Infrastructure Validation Report ===")
print("")
results = []
results.append(validate_s3_bucket(bucket_name, region))
results.append(validate_dynamodb_table(table_name, region))
print("")
print("========================================")
if all(results):
print("Result: ALL CHECKS PASSED")
sys.exit(0)
else:
print("Result: SOME CHECKS FAILED")
sys.exit(1)
if __name__ == "__main__":
main()
What does this code do?
The main function reads three command-line arguments (bucket name, table name, region) and runs both validation checks. It collects the results into a list and uses all(results) to determine if every check passed.
The script exits with code 0 on success and code 1 on failure. This is important because your Bash orchestration script will use exit codes to detect problems.
- Save validate.py.
Seeing a syntax error?
Make sure your f-strings use straight double quotes inside the print() calls, not curly/smart quotes. Check that every try: block has a matching except at the same indentation level.
Help me fix a syntax error in my validation script.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before continuing.
ⓧ I'd like to double check the full code
import sys
import boto3
def validate_s3_bucket(bucket_name, region):
"""Check if the S3 bucket exists."""
s3 = boto3.client("s3", region_name=region)
try:
s3.head_bucket(Bucket=bucket_name)
print(f" [PASS] S3 bucket '{bucket_name}' exists")
return True
except Exception as e:
print(f" [FAIL] S3 bucket '{bucket_name}' not found: {e}")
return False
def validate_dynamodb_table(table_name, region):
"""Check if the DynamoDB table exists and is active."""
dynamodb = boto3.client("dynamodb", region_name=region)
try:
response = dynamodb.describe_table(TableName=table_name)
status = response["Table"]["TableStatus"]
if status == "ACTIVE":
print(f" [PASS] DynamoDB table '{table_name}' is ACTIVE")
return True
else:
print(f" [WARN] DynamoDB table '{table_name}' status is {status}")
return False
except Exception as e:
print(f" [FAIL] DynamoDB table '{table_name}' not found: {e}")
return False
def main():
if len(sys.argv) != 4:
print("Usage: python validate.py <bucket_name> <table_name> <region>")
sys.exit(1)
bucket_name = sys.argv[1]
table_name = sys.argv[2]
region = sys.argv[3]
print("")
print("=== Infrastructure Validation Report ===")
print("")
results = []
results.append(validate_s3_bucket(bucket_name, region))
results.append(validate_dynamodb_table(table_name, region))
print("")
print("========================================")
if all(results):
print("Result: ALL CHECKS PASSED")
sys.exit(0)
else:
print("Result: SOME CHECKS FAILED")
sys.exit(1)
if __name__ == "__main__":
main()
Run the validation
Your script needs three arguments: the bucket name, table name, and region. You already have these stored as Terraform outputs from the previous step.
- Get your S3 bucket name by running:
terraform output -raw s3_bucket_name
You should see something like devops-automation-20260529123456789000000001 (your suffix will be different).
- Get your DynamoDB table name by running:
terraform output -raw dynamodb_table_name
You should see devops-automation-table.
- Run the validation script by passing all three outputs as arguments:
python validate.py $(terraform output -raw s3_bucket_name) $(terraform output -raw dynamodb_table_name) $(terraform output -raw aws_region)
What does this command do?
The $(...) syntax in PowerShell evaluates the command inside and substitutes its output. This lets you pass your Terraform outputs directly as arguments without copying and pasting them manually.
You should see both checks pass with output like this:
=== Infrastructure Validation Report ===
[PASS] S3 bucket 'devops-automation-...' exists
[PASS] DynamoDB table 'devops-automation-table' is ACTIVE
========================================
Result: ALL CHECKS PASSED
Seeing FAIL results?
If the S3 check fails, make sure your Terraform resources are still deployed. Run terraform apply again if you destroyed them.
If both checks fail with a credentials error, your virtual environment may not have access to your AWS credentials. Run aws sts get-caller-identity to verify your credentials are still configured.
Help me debug my validation script failures.
Your infrastructure now has automated validation. Next up, you'll wrap everything into a single Bash script that deploys and validates with one command.
Automate Everything with a Bash Script
Your Python validation script confirms your infrastructure deployed correctly. But right now, deploying and validating requires running multiple commands in sequence, remembering the right arguments each time.
In this step, you'll write a Bash script that orchestrates the entire workflow into a single command. One command will initialize Terraform, plan the deployment, apply it, and validate the results automatically.
In this step, get ready to:
- Write a Bash orchestration script that runs the full deploy-and-validate pipeline.
- Make the script executable and run it in Git Bash.
- Confirm all 4 stages complete with a passing validation report.
Write deploy.sh
Orchestration scripts tie individual tools together into a repeatable workflow. Your script will run Terraform commands in sequence, capture their outputs, and feed those outputs into your Python validation script.
- In VS Code, create a new file called deploy.sh inside your infra-automation folder.
- Paste the following script into deploy.sh:
#!/bin/bash
set -e
echo "=============================="
echo " DevOps Automation Pipeline"
echo "=============================="
echo ""
# Stage 1: Initialize Terraform
echo "[1/4] Initializing Terraform..."
terraform init -input=false
echo ""
# Stage 2: Plan the deployment
echo "[2/4] Planning infrastructure..."
terraform plan -out=tfplan
echo ""
# Stage 3: Apply the plan
echo "[3/4] Deploying infrastructure..."
terraform apply -auto-approve tfplan
echo ""
# Stage 4: Validate the deployment
echo "[4/4] Validating infrastructure..."
BUCKET_NAME=$(terraform output -raw s3_bucket_name)
TABLE_NAME=$(terraform output -raw dynamodb_table_name)
REGION=$(terraform output -raw aws_region)
python validate.py "$BUCKET_NAME" "$TABLE_NAME" "$REGION"
echo ""
echo "=============================="
echo " Pipeline complete!"
echo "=============================="
What does this script do?
- set -e stops the entire script immediately if any command fails. This prevents the pipeline from continuing after an error.
- Stage 1 runs terraform init -input=false to download provider plugins without prompting for user input.
- Stage 2 saves the deployment plan to a file called tfplan so Stage 3 can apply that exact plan.
- Stage 3 uses -auto-approve to skip the manual "yes" confirmation, making the script fully automated.
- Stage 4 captures Terraform outputs using terraform output -raw and passes them as arguments to your Python validation script.
- Save deploy.sh in VS Code.
Why Bash instead of PowerShell?
Bash scripts are the standard for DevOps automation because they run on Linux servers, macOS, and Windows (via Git Bash). Most CI/CD pipelines, Docker containers, and cloud instances use Bash.
Writing your orchestration in Bash means the same script works everywhere your infrastructure runs.
✔️ Awesome, I've got everything!
Great. Double check you've saved deploy.sh before moving on.
ⓧ I'd like to double check the full code
#!/bin/bash
set -e
echo "=============================="
echo " DevOps Automation Pipeline"
echo "=============================="
echo ""
# Stage 1: Initialize Terraform
echo "[1/4] Initializing Terraform..."
terraform init -input=false
echo ""
# Stage 2: Plan the deployment
echo "[2/4] Planning infrastructure..."
terraform plan -out=tfplan
echo ""
# Stage 3: Apply the plan
echo "[3/4] Deploying infrastructure..."
terraform apply -auto-approve tfplan
echo ""
# Stage 4: Validate the deployment
echo "[4/4] Validating infrastructure..."
BUCKET_NAME=$(terraform output -raw s3_bucket_name)
TABLE_NAME=$(terraform output -raw dynamodb_table_name)
REGION=$(terraform output -raw aws_region)
python validate.py "$BUCKET_NAME" "$TABLE_NAME" "$REGION"
echo ""
echo "=============================="
echo " Pipeline complete!"
echo "=============================="
Run the full pipeline
Your script is saved, but you need to run it in a Bash environment. Git Bash provides a real Bash shell on Windows.
- Press the Windows key to open your search bar.
- Type Git Bash and press Enter to open it.
- Navigate to your project folder by running this command:
cd ~/Desktop/infra-automation
- Activate your Python virtual environment so boto3 is available by running:
source venv/Scripts/activate
Why activate the venv again?
You created the virtual environment earlier in PowerShell. Git Bash is a separate shell session, so it does not inherit that activation.
Running source venv/Scripts/activate ensures the python command inside your script uses the venv's interpreter, which has boto3 installed.
- Make the script executable by running:
chmod +x deploy.sh
- Run the full pipeline by executing:
./deploy.sh
Watch the terminal as each stage executes in sequence. Since your infrastructure already exists from the previous step, Terraform will detect that no changes are needed and apply an empty plan.
Seeing "command not found" errors?
- If you see terraform: command not found, Terraform may not be on your Git Bash PATH. Close Git Bash, reopen it, and try again. If it persists, run export PATH=$PATH:/c/terraform (adjust the path to where you extracted terraform.exe).
- If you see python: command not found, try replacing python with python.exe in your deploy.sh script. Some Git Bash installations require the .exe extension.
- If boto3 is not found, make sure you activated the virtual environment with source venv/Scripts/activate before running the script.
Still stuck? Help me troubleshoot my deploy.sh pipeline errors in Git Bash
Verify the pipeline output
A successful run prints output for all 4 stages followed by a validation report.
- Confirm you see the banner DevOps Automation Pipeline at the top of the output.
- Check that all 4 stages printed their progress messages ([1/4] through [4/4]).
- Verify the validation report shows [PASS] for both the S3 bucket and DynamoDB table.
- Confirm the final line reads Result: ALL CHECKS PASSED followed by Pipeline complete!.
What should I see?
Your terminal output should end with something like:
[4/4] Validating infrastructure...
=== Infrastructure Validation Report ===
[PASS] S3 bucket 'devops-automation-XXXXX' exists
[PASS] DynamoDB table 'devops-automation-table' is ACTIVE
========================================
Result: ALL CHECKS PASSED
==============================
Pipeline complete!
==============================
The bucket name will have a unique suffix that Terraform generated. As long as both checks show [PASS] and the result says ALL CHECKS PASSED, your pipeline is working.
You have just automated what would otherwise take multiple manual steps into a single command. This is the foundation of every DevOps pipeline: repeatable, automated, and verifiable infrastructure delivery.
Secret mission
Upload and Verify a Test File in Your Pipeline
Your pipeline deploys and validates infrastructure. But can it also interact with that infrastructure? Extend your automation to upload a file to S3 and confirm the upload succeeded, all from a single command.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses AWS Free Tier resources, so there are no ongoing costs while your free plan is active.
Resources you used:
- 1 S3 bucket (devops-automation- prefix) in us-east-1.
- 1 DynamoDB table (devops-automation-table) in us-east-1.
- 1 IAM user (terraform-user) with AdministratorAccess policy attached.
- AWS CLI credentials stored locally in %USERPROFILE%\.aws\.
- Local project folder infra-automation on your Desktop.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with Terraform and AWS.
- Your DynamoDB table uses Always Free capacity (1 RCU/WCU out of the 25/25 free limit). It will not consume your credits.
- Your S3 bucket contains one small file. Storage costs are negligible and covered by your free plan credits.
- Python, Terraform, and AWS CLI remain installed on your machine for future projects.
Pause - I'll come back to this later
Free up local resources while keeping your AWS infrastructure intact. You can pick up where you left off later.
- Deactivate your Python virtual environment by running deactivate in PowerShell (if it is still active).
- Your S3 bucket and DynamoDB table remain deployed at no cost on the free tier.
- When you return, navigate to your infra-automation folder and reactivate the venv with venv\Scripts\activate.
Delete - I don't want to use this again
Remove all project resources and start fresh. This is good practice and ensures zero future charges.
- Open PowerShell and navigate to your infra-automation folder.
- Get your bucket name by running:
terraform output -raw s3_bucket_name
- Empty the S3 bucket before destroying it. Replace the bucket name with the value from the previous command:
aws s3 rm s3://[[BUCKET_NAME="your-bucket-name-from-output"]] --recursive
Why empty the bucket first?
Terraform cannot delete an S3 bucket that still contains objects. Emptying it first avoids a BucketNotEmpty error during destroy.
- Destroy both AWS resources by running:
terraform destroy
- Type yes when prompted and press Enter. Terraform removes both the S3 bucket and DynamoDB table.
- Verify the resources are gone by running:
aws s3 ls
aws dynamodb list-tables --region us-east-1
- Your bucket should no longer appear in the S3 list. The DynamoDB tables list should not contain devops-automation-table.
- Delete the IAM user. In the AWS Console, navigate to IAM, then Users.
- Select terraform-user and click Delete. Confirm the deletion when prompted.
- Remove your locally stored AWS credentials by running:
Remove-Item -Recurse -Force $env:USERPROFILE\.aws
- Delete the local project folder. In Git Bash, run:
rm -rf ~/Desktop/infra-automation
Keep the tools installed
Python, Terraform, and AWS CLI are useful for future projects. You do not need to uninstall them unless you want to reclaim disk space.
That's a Wrap!
That's a Wrap!
Nice work! You just built a real DevOps automation pipeline from scratch.
You've learned how to:
- Written Terraform configuration files that provision real AWS infrastructure (an S3 bucket and a DynamoDB table) using Infrastructure as Code.
- Built a Python validation script that programmatically verifies cloud resources exist and are correctly configured using boto3.
- Created a Bash orchestration script that automates the entire deploy-validate pipeline with a single command.
- Extended the pipeline with file upload and verification to confirm your S3 bucket works end-to-end.
Ready to quiz yourself?