Secure Azure Infrastructure with Checkov
Scan and fix Azure Terraform misconfigurations using Checkov static analysis.
Introduction
30 Second Summary
Imagine pushing infrastructure code to production only to discover it left a storage account wide open to the internet. Catching security flaws after deployment is expensive and stressful. What if you could find every vulnerability before a single resource goes live?
In this project, you will write Azure infrastructure code in Terraform, scan it with Checkov to discover security vulnerabilities, fix each misconfiguration, and then write your own custom security policy from scratch.
What You'll Build
You run a single scan command against your Terraform files and instantly see a colour-coded security report. Red lines highlight every misconfiguration. You fix them one by one, re-scan, and watch the failures disappear until you achieve a clean bill of health.
By the end of this project, you'll have:
- A scan-fail-fix loop you can run in seconds. One command reveals every security issue hiding in your infrastructure code, complete with check IDs and file locations.
- A fully secured Azure configuration where you have fixed real-world vulnerabilities: unrestricted network access, missing HTTPS enforcement, and SSH open to the entire internet.
- Your own custom security policy written in YAML that enforces an organization-specific tagging rule Checkov does not ship with.
- Secret Mission: Write a second custom policy that enforces a minimum TLS version across all storage accounts.
Are there any prerequisites?
You need basic command line familiarity, plus Python and Homebrew already installed on your Mac. No Azure account is required. Checkov performs static analysis on your Terraform files locally, so nothing gets deployed to the cloud.
Before We Start
Before We Start...
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Terraform and Checkov
Scanning infrastructure code for security issues requires two tools working together. Terraform defines what your cloud infrastructure looks like as code. Checkov reads that code and flags any security misconfigurations before anything gets deployed.
In this step, you'll install both tools and create the project folder where your Terraform files will live.
In this step, get ready to:
- Install Terraform via Homebrew.
- Install Checkov via pip.
- Create your project directory and open it in your editor.
Install Terraform
Terraform is an Infrastructure as Code tool that lets you define cloud resources in configuration files. You'll use it to write Azure infrastructure that Checkov can scan.
- Install Terraform using Homebrew by running these commands:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
What do these commands do?
- brew tap hashicorp/tap adds HashiCorp's official package repository to Homebrew so it knows where to find Terraform.
- brew install hashicorp/tap/terraform downloads and installs Terraform from that repository.
- Confirm Terraform installed successfully by running:
terraform -version
✔️ I see version 1.2 or higher
You should see output starting with Terraform v1.15 (or similar). Terraform is ready to go.
ⓧ I see an older version
Your Terraform install is outdated. Upgrade to the latest version by running:
brew upgrade hashicorp/tap/terraform
- Run terraform -version again to confirm you now see v1.2 or higher.
ⓧ Command not found
The install may not have completed successfully. Try these fixes:
- Close and reopen your terminal, then run terraform -version again.
- If it still fails, re-run both install commands above (brew tap hashicorp/tap followed by brew install hashicorp/tap/terraform).
- Confirm Homebrew itself is working by running brew --version.
If you're still stuck, help me troubleshoot my Terraform installation on macOS using Homebrew.
Install Checkov
Checkov is a static analysis tool that scans Terraform files for security misconfigurations. It runs entirely on your machine without needing any cloud credentials.
- Install Checkov using pip by running:
pip3 install checkov
What does this command do?
The pip3 command pulls Checkov from PyPI (Python's package registry) along with all its dependencies. This may take a minute as it downloads several packages.
- Confirm Checkov installed successfully by running:
checkov --version
✔️ I see a version number starting with 3
You should see something like 3.3.8 printed to the terminal. Checkov is ready to scan.
ⓧ I see version 2.x
You have an older version of Checkov installed. Upgrade by running:
pip3 install --upgrade checkov
- Run checkov --version again to confirm it now shows 3.x.
ⓧ Command not found
Your Python scripts directory may not be on your system PATH. Try these fixes:
- Close and reopen your terminal, then run checkov --version again.
- If that does not work, try running it as a Python module instead:
python3 -m checkov --version
If the module command works but checkov alone does not, you can use python3 -m checkov as a substitute throughout this project.
Still stuck? Help me fix my Checkov installation.
Create the project directory
Now that both tools are installed, you need a folder to hold your Terraform configuration files.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir azure-secure-infra
cd azure-secure-infra
- Confirm you're in the new directory by running:
pwd
You should see a path ending in /Desktop/azure-secure-infra in the output.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Select the azure-secure-infra folder on your Desktop, then click Open.
Can't find the folder?
Make sure you ran cd ~/Desktop before the mkdir command. The folder should be at ~/Desktop/azure-secure-infra.
If VS Code asks whether you trust the authors of this folder, click Yes, I trust the authors.
Need more help? Help me find my project folder.
Your tools are installed and your project folder is ready. Next up, you'll write some intentionally insecure Terraform code to give Checkov something to catch.
Write Insecure Azure Infrastructure
Your tools are installed and ready to go. Now you need infrastructure code for Checkov to scan.
In this step, you will write deliberately insecure Terraform code defining Azure resources. This gives Checkov real vulnerabilities to find in the next step.
In this step, get ready to:
- Create a provider configuration and resource group in main.tf.
- Create an insecure storage account in storage.tf.
- Create an insecure network security group in nsg.tf.
Create the provider configuration
Every Terraform project needs a provider block that tells Terraform which cloud platform to target and which plugin version to download.
- Create a new file called main.tf in your azure-secure-infra folder, then paste the following code:
# Declare the required provider and Terraform version
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.81"
}
}
required_version = ">= 1.2"
}
# Configure the Azure provider
provider "azurerm" {
features {}
}
# Define a resource group to contain all resources
resource "azurerm_resource_group" "example" {
name = "secure-infra-rg"
location = "East US"
}
What does this code do?
The terraform block pins the azurerm provider to version ~> 4.81 so Terraform knows which plugin to download.
The provider "azurerm" block configures Azure-specific settings. The features {} block is required by the azurerm provider even if empty.
The azurerm_resource_group creates a logical container in Azure for all the resources you will define next.
- Save main.tf.
Before you run the next command, what do you think terraform init will do with the provider block you just wrote?
- Initialize the project by running this command in your terminal:
terraform init
Terraform downloads the azurerm provider plugin and prints Terraform has been successfully initialized! at the end of the output.
Seeing a provider error?
- Make sure you saved main.tf before running the command.
- Confirm the source value is exactly hashicorp/azurerm with no typos.
- Check that your terminal is inside the azure-secure-infra folder where main.tf lives.
help me fix a terraform init error
Why don't I need Azure credentials?
Checkov performs static analysis on your Terraform files. It reads the code and checks it against security policies without ever connecting to Azure.
You will never run terraform plan or terraform apply in this project. No Azure account or credentials are needed.
✔️ Awesome, I've got everything!
Great. Make sure you have saved main.tf and that terraform init completed successfully.
ⓧ I'd like to double check the full code
# Declare the required provider and Terraform version
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.81"
}
}
required_version = ">= 1.2"
}
# Configure the Azure provider
provider "azurerm" {
features {}
}
# Define a resource group to contain all resources
resource "azurerm_resource_group" "example" {
name = "secure-infra-rg"
location = "East US"
}
Create the insecure storage account
A storage account in Azure holds blobs, files, queues, and tables. The configuration below has several security misconfigurations that Checkov will flag in the next step.
- Create a new file called storage.tf in the same folder, then paste this code:
# Intentionally insecure storage account for Checkov to scan
resource "azurerm_storage_account" "example" {
name = "secureinfrastorage"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
public_network_access_enabled = true
https_traffic_only_enabled = false
allow_nested_items_to_be_public = true
# Permissive network rules - allows all traffic
network_rules {
default_action = "Allow"
}
}
What makes this insecure?
- public_network_access_enabled = true means anyone on the internet can attempt to reach this storage account.
- https_traffic_only_enabled = false allows unencrypted HTTP traffic, exposing data in transit.
- allow_nested_items_to_be_public = true lets individual blobs be made publicly accessible.
- default_action = "Allow" in the network rules permits all network traffic by default instead of denying it.
- Save storage.tf.
✔️ Awesome, I've got everything!
Great. Make sure you have saved storage.tf.
ⓧ I'd like to double check the full code
# Intentionally insecure storage account for Checkov to scan
resource "azurerm_storage_account" "example" {
name = "secureinfrastorage"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
public_network_access_enabled = true
https_traffic_only_enabled = false
allow_nested_items_to_be_public = true
# Permissive network rules - allows all traffic
network_rules {
default_action = "Allow"
}
}
Create the insecure network security group
A network security group (NSG) controls which traffic is allowed in and out of Azure resources. This one has a rule that opens SSH to the entire internet.
- Create a new file called nsg.tf in the same folder, then paste this code:
# Intentionally insecure NSG - allows SSH from anywhere
resource "azurerm_network_security_group" "example" {
name = "secure-infra-nsg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
# SSH rule with no source restriction
security_rule {
name = "SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
What makes this insecure?
The source_address_prefix = "*" setting means SSH traffic is allowed from any IP address on the internet. In production, SSH should be restricted to a known network range so only trusted machines can connect.
- Save nsg.tf.
- Confirm all three files exist by running:
ls *.tf
You should see main.tf, nsg.tf, and storage.tf listed in the output.
Missing a file?
- Check that you created each file inside the azure-secure-infra folder, not in a subfolder or parent directory.
- Make sure you saved each file after pasting the code.
help me find my missing Terraform file
✔️ Awesome, I've got everything!
Great. Make sure all three files are saved.
ⓧ I'd like to double check the full code
# Intentionally insecure NSG - allows SSH from anywhere
resource "azurerm_network_security_group" "example" {
name = "secure-infra-nsg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
# SSH rule with no source restriction
security_rule {
name = "SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
You now have three Terraform files packed with real security vulnerabilities. Next up, you will unleash Checkov on this code and see exactly how many issues it catches.
Run Your First Security Scan
You have three Terraform files defining Azure infrastructure. They look like perfectly valid code. But how secure are they?
In this step, you run Checkov against your project directory to find out. The scan report makes invisible security gaps visible by checking your configuration against over 1,000 built-in security policies.
In this step, get ready to:
- Run your first Checkov security scan against the project directory.
- Identify specific security vulnerabilities flagged in the report.
- Establish a security baseline with passed and failed check counts.
Run the security scan
Checkov performs static analysis on your Terraform files. It reads the configuration, compares each resource against its built-in policy library, and reports which checks passed and which failed. No cloud credentials or deployment required.
Before you run the scan, take a guess: how many security issues do you think are hiding in three small Terraform files?
- Run the Checkov scan against your project directory by running this command:
checkov -d .
What does this command do?
The -d . flag tells Checkov to scan the current directory. Checkov automatically detects your Terraform files, parses them, and runs every applicable Azure security check against each resource.
The output scrolls through each check result. For every failed check, you see a structured report entry containing the check ID, a description of the security rule, the resource that failed, and the exact file and line numbers where the problem lives.
You should see multiple FAILED checks in the output, highlighted in red. The scan found security issues you would never catch by just reading the code.
Not seeing any output?
Make sure your terminal is inside the azure-secure-infra directory where your .tf files live. Run ls to confirm you see main.tf, storage.tf, and nsg.tf listed.
If you see a "command not found" error, Checkov may not be on your PATH. Try running pip3 show checkov to verify the installation.
help me troubleshoot why Checkov is not running in my terminal
Identify the key failures
Scroll through the report and look for the lines marked FAILED. Each failure follows the same structure: a check ID, a human-readable description, the resource it applies to, and the file location.
Here are the critical failures to look for in your output:
- CKV_AZURE_35 — the storage account's default network access rule is not set to Deny. Anyone on the internet could potentially reach it.
- CKV_AZURE_59 — the storage account allows public access. Its data could be exposed to unauthenticated users.
- CKV_AZURE_10 — SSH access is not restricted from the internet. The NSG rule allows port 22 connections from any source address.
Why do these checks matter?
Each of these represents a real attack surface. An unrestricted storage account is a data breach waiting to happen. An NSG rule allowing SSH from * means any IP address on the internet can attempt to connect to port 22.
In a production environment, these misconfigurations might sit unnoticed for months until someone exploits them. Catching them in code, before deployment, is the core idea behind shift-left security.
Note your security baseline
Scroll to the very bottom of the Checkov output. You will see a summary line showing the total counts in this format:
- Look for the line that reads: Passed checks: X, Failed checks: Y, Skipped checks: 0.
- Note down your failed check count. This is your security baseline.
You should see 5 or more failed checks across your storage account and NSG resources. This number is your starting point. In the next step, you will fix each misconfiguration and watch the failure count drop to zero.
What is a security baseline?
A baseline is the measured state before you start making changes. By recording the exact number of failures now, you can prove the impact of each fix in the next step. Every time you re-run the scan, you compare against this baseline to confirm progress.
Your scan revealed real security vulnerabilities hiding in plain sight. Next up, you will fix each misconfiguration one by one and re-run the scan to prove the failures disappear.
Fix the Misconfigurations
Your first Checkov scan revealed multiple security failures hiding in what looked like normal Terraform code. Each failed check points to a real vulnerability that an attacker could exploit in production.
Now you fix each misconfiguration one by one, then re-run the scan to watch the failure count drop. This scan-fail-fix loop is the core workflow practitioners use to harden infrastructure code before it ever reaches a cloud environment.
In this step, get ready to:
- Fix security misconfigurations in the storage account.
- Restrict SSH access in the network security group.
- Re-run Checkov and confirm the failures are resolved.
Harden the storage account
The storage account in storage.tf currently allows public network access, permits unencrypted HTTP traffic, exposes nested blob items publicly, and uses an Allow default network rule. Each of these is a separate Checkov failure you need to flip.
- Replace the entire contents of storage.tf with the secured version below:
resource "azurerm_storage_account" "example" {
name = "secureinfrastorage"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
public_network_access_enabled = false
https_traffic_only_enabled = true
allow_nested_items_to_be_public = false
network_rules {
default_action = "Deny"
}
}
What changed and why?
- public_network_access_enabled = false blocks all public internet traffic from reaching the storage account.
- https_traffic_only_enabled = true forces encrypted HTTPS connections, preventing credentials from being sent in cleartext.
- allow_nested_items_to_be_public = false prevents individual blobs inside the account from being made publicly accessible.
- default_action = "Deny" in the network_rules block rejects any traffic not explicitly whitelisted.
- Save storage.tf.
✔️ Awesome, I've got everything!
Great. Double check you have saved the file before moving on.
ⓧ I'd like to double check the full code
resource "azurerm_storage_account" "example" {
name = "secureinfrastorage"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
public_network_access_enabled = false
https_traffic_only_enabled = true
allow_nested_items_to_be_public = false
network_rules {
default_action = "Deny"
}
}
Restrict SSH access in the network security group
The NSG rule currently sets source_address_prefix to "*", which means SSH port 22 is open to every IP address on the internet. You need to restrict it to a known internal network range.
- In nsg.tf, find this line inside the security_rule block:
source_address_prefix = "*"
- Replace "*" with "10.0.0.0/24" so the line now reads:
source_address_prefix = "10.0.0.0/24"
Why 10.0.0.0/24?
This CIDR range represents a private subnet. Only machines within that range can SSH into your resources, instead of every device on the public internet. In a real environment, you would use your organization's VPN or bastion subnet range.
- Save nsg.tf.
✔️ Awesome, I've got everything!
Great. Double check you have saved the file before moving on.
ⓧ I'd like to double check the full code
resource "azurerm_network_security_group" "example" {
name = "secure-infra-nsg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
security_rule {
name = "SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "10.0.0.0/24"
destination_address_prefix = "*"
}
}
Re-run Checkov and confirm the fixes
Both files are saved with the hardened configuration. Before you run the scan, think about which specific check IDs you expect to flip from FAILED to PASSED.
- Re-run the security scan by running this command:
checkov -d .
Look at the output and compare it to the results you saw in Step 3. The checks CKV_AZURE_35, CKV_AZURE_59, and CKV_AZURE_10 now show PASSED. Your total failure count is significantly lower than before.
What do these passing checks prove?
- CKV_AZURE_35 confirms the default network access rule is set to Deny.
- CKV_AZURE_59 confirms the storage account disallows public access.
- CKV_AZURE_10 confirms SSH access is no longer open to the entire internet.
Still seeing those checks as FAILED?
- Make sure you saved both storage.tf and nsg.tf before re-running the scan.
- Check that default_action is set to "Deny" (capital D) in your network_rules block. Checkov is case-sensitive.
- Verify source_address_prefix is "10.0.0.0/24" and not still "*". A stray character or missing quotes will cause a failure.
Need more help? Why are my Checkov checks still failing after I updated the Terraform files?
Your infrastructure code just went from vulnerable to hardened in a single iteration. Next up, you will write your own custom security policy to enforce a rule that Checkov does not ship with out of the box.
Write a Custom Security Policy
Your infrastructure now passes all of Checkov's built-in security checks. That covers common misconfigurations, but every organization has rules that no generic scanner ships with.
In this step, you will write your own custom security policy using Checkov's YAML custom check format. The policy will require every storage account to carry an environment tag. You will run the scan, see the check fail, then fix the code to make it pass.
In this step, get ready to:
- Create a custom YAML policy that enforces an organization-specific tagging rule.
- Run Checkov with external checks and observe your policy in the scan output.
- Fix the failing check and confirm a clean scan.
Create the custom policy file
Checkov loads custom checks from a directory you point it to with the --external-checks-dir flag. Each check lives in its own YAML file inside that directory.
- Create the custom-checks directory inside your azure-secure-infra folder by running this command:
mkdir custom-checks
- Confirm the directory was created by running:
ls
You should see custom-checks listed alongside main.tf, storage.tf, and nsg.tf.
- In your editor, create a new file at custom-checks/require_environment_tag.yaml and paste the following policy:
---
metadata:
name: "Ensure all storage accounts have an environment tag"
id: "CKV2_CUSTOM_1"
category: "GENERAL_SECURITY"
scope:
provider: azure
definition:
cond_type: "attribute"
resource_types:
- "azurerm_storage_account"
attribute: "tags.environment"
operator: "exists"
What does this policy do?
This YAML file defines a single attribute check. Here is what each section means:
- metadata gives the check a human-readable name, a unique ID (CKV2_CUSTOM_1), and a category for grouping in reports.
- scope limits the check to Azure resources so it does not fire on AWS or GCP configs.
- definition is the rule itself. It targets azurerm_storage_account resources and asserts that the attribute tags.environment must exist.
- Save custom-checks/require_environment_tag.yaml.
- Confirm the file is in place by running:
ls custom-checks/
You should see require_environment_tag.yaml listed.
Don't see the file?
- Make sure you created the file inside the custom-checks/ directory, not in the project root.
- Check the filename is exactly require_environment_tag.yaml with no extra extension.
Help me figure out why my custom check file is not showing up.
✔️ Awesome, I've got everything!
Great. Make sure you have saved the file before moving on.
ⓧ I'd like to double check the full code
---
metadata:
name: "Ensure all storage accounts have an environment tag"
id: "CKV2_CUSTOM_1"
category: "GENERAL_SECURITY"
scope:
provider: azure
definition:
cond_type: "attribute"
resource_types:
- "azurerm_storage_account"
attribute: "tags.environment"
operator: "exists"
Run the scan with your custom check
Your storage account currently has no tags at all. The custom check requires tags.environment to exist, so the check should fail.
Before you run the scan, what do you think the result will be for CKV2_CUSTOM_1? Will it pass or fail?
- Run Checkov with the external checks directory by running this command:
checkov -d . --external-checks-dir ./custom-checks
In the output, look for a line containing your check ID CKV2_CUSTOM_1 and the description Ensure all storage accounts have an environment tag. It shows FAILED for the azurerm_storage_account.example resource.
Why does your custom check appear here?
The --external-checks-dir flag tells Checkov to load any YAML or Python checks from the specified directory alongside its built-in checks. Your custom check runs in the same scan pass as the 1,000+ built-in policies.
The output format is the same as built-in checks: check ID, description, PASSED or FAILED, and the resource and file location where the issue was found.
Custom check not appearing in the output?
- Verify the YAML starts with --- on the first line. Checkov requires valid YAML document markers.
- Check that indentation uses spaces (not tabs). YAML is whitespace-sensitive.
- Confirm the path in the command matches the directory name: ./custom-checks (with a hyphen, not an underscore).
Help me figure out why my custom Checkov YAML check is not appearing in the scan results.
Fix the storage account and confirm a clean scan
The fix is straightforward. You need to add a tags block to the storage account resource in storage.tf.
- In storage.tf, find the closing } of the network_rules block. It currently looks like this:
network_rules {
default_action = "Deny"
}
}
- Add the following tags block between the network_rules closing brace and the resource closing brace, so it looks like this:
network_rules {
default_action = "Deny"
}
tags = {
environment = "production"
}
}
What does this fix?
Adding tags = { environment = "production" } satisfies the exists operator in your custom check. Checkov will now find the tags.environment attribute on the storage account and mark the check as PASSED.
- Save storage.tf.
Before you re-run the scan, do you expect CKV2_CUSTOM_1 to pass now?
- Re-run the scan with your custom check by running:
checkov -d . --external-checks-dir ./custom-checks
Find CKV2_CUSTOM_1 in the output. It now shows PASSED for azurerm_storage_account.example.
Still seeing CKV2_CUSTOM_1 as FAILED?
- Make sure you saved storage.tf after adding the tags block.
- Check that the tag key is exactly environment (lowercase). The custom policy checks for tags.environment specifically.
- Verify the tags block is inside the azurerm_storage_account resource block, not outside it.
Help me fix my storage account tags so my custom Checkov check passes.
✔️ Awesome, I've got everything!
Great. Make sure storage.tf is saved with the tags block included.
ⓧ I'd like to double check the full code
resource "azurerm_storage_account" "example" {
name = "secureinfrastorage"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
public_network_access_enabled = false
https_traffic_only_enabled = true
allow_nested_items_to_be_public = false
network_rules {
default_action = "Deny"
}
tags = {
environment = "production"
}
}
You have written and enforced your own organization-specific policy. Next up, put your skills to the test with a Secret Mission that pushes custom policies further.
Secret mission
Enforce Minimum TLS Version
You've written one custom policy to enforce tagging. Now take it further by writing a second policy that enforces encryption standards. Can you figure out the right attribute path and operator to ensure all storage accounts use TLS 1.2?
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 runs entirely locally with static analysis only, so there are no ongoing cloud costs.
Resources you used:
- Project directory ~/Desktop/azure-secure-infra/ containing Terraform files, NSG configuration, and custom Checkov policies.
- Terraform CLI installed via Homebrew.
- Checkov installed via pip.
- Terraform provider cache (.terraform/ directory inside the project folder).
Keep everything running
No action needed. Choose this if you plan to keep experimenting with Terraform and Checkov, or want to add more custom policies.
- Your project files stay in ~/Desktop/azure-secure-infra/.
- Terraform and Checkov remain installed and available from any terminal session.
- No cloud resources were created, so nothing is running or costing money.
Pause - I'll come back to this later
Since this project is entirely local with no running processes, there is nothing to pause. Your files and installed tools will be waiting for you when you return.
- No servers or processes are running, so there is nothing to stop.
- Your Terraform files and custom checks remain on disk, ready for the next scan.
Delete - I don't want to use this again
Remove all project files and uninstall the tools you installed during this project.
- Delete the project directory by running:
cd ~/Desktop
rm -rf azure-secure-infra
- Uninstall Terraform by running:
brew uninstall hashicorp/tap/terraform
- Uninstall Checkov by running:
pip3 uninstall checkov
- Confirm the project folder is gone by running:
ls ~/Desktop | grep azure
You should see no output, confirming the folder has been removed.
Nice Work!
Nice Work!
You just secured Azure infrastructure without deploying a single resource to the cloud. Your Terraform configuration now passes over a thousand built-in security checks, plus your own custom policies.
Here's what you learned:
- Ran Checkov security scans against Terraform code and interpreted the results to identify real misconfigurations.
- Fixed Azure security vulnerabilities: unrestricted network access, missing HTTPS enforcement, and SSH open to the entire internet.
- Wrote custom YAML-based Checkov policies to enforce organization-specific rules like required tagging on storage accounts.
- Secret Mission: Enforced a minimum TLS version as a custom policy-as-code check.
Ready to quiz yourself?