Secure a CI/CD Pipeline with DevSecOps
Block insecure code from shipping by adding SAST scanning to a CI/CD pipeline.
Introduction
30 Second Summary
Every time code gets pushed without a security check, there is a chance a hardcoded password or leaked credential slips straight into production. Most teams only discover these issues after deployment, when the damage is already done.
In this project, you will build a CI/CD pipeline on AWS that automatically scans every code push for security vulnerabilities and blocks deployment when it finds them.
What You'll Build
Push a Python app with a planted hardcoded password to GitHub, watch your pipeline catch it red-handed in the build logs, then fix the code and see the pipeline go green.
By the end of this project, you'll have:
- A live CI/CD pipeline on AWS that automatically triggers on every push to your GitHub repo and runs a security scan before code can proceed.
- A firsthand experience of the DevSecOps loop. Push vulnerable code, see Bandit catch it in the build logs, then fix the vulnerability and watch the pipeline go green.
- A reusable buildspec.yml that integrates SAST scanning into AWS CodeBuild, ready to drop into any future project.
- Secret Mission: Add severity-based thresholds so only HIGH and MEDIUM findings block the pipeline, while LOW findings are logged but allowed through.
Are there any prerequisites?
This project assumes you have an active AWS account, a GitHub account, and the AWS CLI installed on your Mac. If you need to set up any of these, do that before starting Step 1.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your Project and Repository
Your pipeline needs code to scan. Before you can build a CI/CD pipeline or add security checks, you need source code living in a repository that AWS can access.
In this step, you will create a Python app that deliberately contains a security vulnerability (a hardcoded database password). You will push it to GitHub and connect that repository to your AWS account so CodePipeline can pull from it later.
In this step, get ready to:
- Create a Python app with a hardcoded password vulnerability and a basic build specification.
- Push the code to a new GitHub repository.
- Connect GitHub to AWS using CodeConnections.
Create the Python app with an intentional vulnerability
A security scanner needs real code to flag. You will write a small Python app that connects to a database using a hardcoded password. This is a common real-world vulnerability that tools like Bandit are built to detect.
- Open your terminal. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal and press Enter to open it.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir devsecops-pipeline
cd devsecops-pipeline
- Confirm you are inside the new folder by running:
pwd
You should see a path ending in devsecops-pipeline.
- Create the app.py file by running this command:
cat > app.py << 'EOF'
def get_database_connection():
db_host = "localhost"
db_port = 5432
db_name = "myapp"
db_user = "admin"
db_password = "SuperSecret123!"
connection_string = (
f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
)
return connection_string
def main():
conn_string = get_database_connection()
print("Connecting to database...")
print(f"Using host: {db_host}, port: {db_port}")
if __name__ == "__main__":
main()
EOF
What does this code do?
This app simulates a database connection setup. The key detail is on the db_password line: the password SuperSecret123! is written directly in the source code.
Hardcoding passwords is a real security vulnerability. Anyone who reads this file can see the credentials. This is exactly the kind of issue a SAST scanner like Bandit flags.
- Confirm app.py was created by running:
ls
You should see app.py listed in the output.
- Create the buildspec.yml file by running this command:
cat > buildspec.yml << 'EOF'
version: 0.2
phases:
build:
commands:
- echo "Building application..."
- python -c "import app; print('Build successful')"
EOF
What is a buildspec?
A buildspec.yml is a configuration file that tells AWS CodeBuild what to do when it runs a build. Right now, this buildspec only imports the app to verify it loads. No security scanning yet.
The version: 0.2 at the top is the buildspec format version required by CodeBuild.
- Confirm both files exist by running:
ls
You should see app.py and buildspec.yml in the output.
Don't see both files?
Make sure you ran each cat > filename << 'EOF' command exactly as shown. If a file is missing, re-run the corresponding command. The EOF on the last line must be on its own line with no spaces before it.
help me debug why my app.py or buildspec.yml file was not created
✔️ Awesome, I've got everything!
Great. You have both project files ready to go.
ⓧ I'd like to double check the full code
Scroll up and confirm you have both files. Here is what each file should contain:
app.py:
def get_database_connection():
db_host = "localhost"
db_port = 5432
db_name = "myapp"
db_user = "admin"
db_password = "SuperSecret123!"
connection_string = (
f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
)
return connection_string
def main():
conn_string = get_database_connection()
print("Connecting to database...")
print(f"Using host: {db_host}, port: {db_port}")
if __name__ == "__main__":
main()
buildspec.yml:
version: 0.2
phases:
build:
commands:
- echo "Building application..."
- python -c "import app; print('Build successful')"
Create the GitHub repository and push your code
Your code needs to live on GitHub so that AWS CodePipeline can pull it automatically on every push. You will create a new repository on GitHub and push your local files to it.
- Open your browser and go to github.com.
- In the upper-right corner, click the + menu, then click New repository.
- In the Repository name field, type devsecops-pipeline.
- Set the visibility to Public.
- Do NOT check Add a README file or any other initialization option. You already have local files to push.
- Click Create repository.
GitHub shows a Quick Setup page with commands for pushing an existing repository. You will use those next.
Why keep the repo empty?
If you add a README or other files during creation, GitHub initializes the repo with a commit. Your local repo will then have a different history, and pushing will fail with a merge conflict. Starting empty keeps things clean.
- Back in your terminal, initialize a Git repository by running:
git init
- Stage and commit your files by running these commands:
git add .
git commit -m "Initial commit with vulnerable app"
- Rename the default branch to main and add your GitHub repo as the remote by running:
git branch -M main
git remote add origin [[GITHUB_REPO_URL="https://github.com/your-username/devsecops-pipeline.git"]]
Find your repository URL
Copy the URL from the Quick Setup page on GitHub. It will look like https://github.com/your-username/devsecops-pipeline.git. You can also find it by clicking the green Code button on your repository page.
Before you push, take a moment to predict what you expect to see. Will the push succeed or fail?
- Push your code to GitHub by running:
git push -u origin main
You should see output showing objects being written and a confirmation that main was pushed successfully.
- Open your repository page on GitHub in your browser.
- Confirm you can see app.py and buildspec.yml listed in the file list.
Push failed or files missing on GitHub?
- If git push asks for credentials, make sure you are signed in to git with your GitHub account. You may need to set up a personal access token or use GitHub CLI for authentication.
- If you see a merge conflict, you likely initialized the GitHub repo with a README. Delete the repo on GitHub, recreate it without any initialization, and try pushing again.
help me fix a git push error to my GitHub repository
Connect GitHub to AWS
AWS CodePipeline needs permission to pull code from your GitHub repo. You will create a connection using AWS CodeConnections (formerly CodeStar Connections) so the pipeline can access your repository automatically.
- Open the AWS Management Console in your browser and sign in.
- In the search bar at the top, type Developer Tools and select Developer Tools from the results.
- In the left sidebar, choose Settings, then Connections.
- Click Create connection.
- Under Select a provider, choose GitHub.
- In Connection name, enter devsecops-github.
- Click Connect to GitHub.
What happens next?
AWS opens a GitHub authorization page. This installs the AWS Connector for GitHub app on your GitHub account so AWS can read your repositories.
- Click Authorize AWS Connector for GitHub when the GitHub authorization page appears.
- Under GitHub Apps, click Install a new app.
- Select your GitHub account on the installation page.
- Leave the default settings and click Install.
- You are returned to the Connect to GitHub page. Your new installation appears under GitHub Apps.
- Click Connect to finalize the connection.
Your new connection should now appear in the Connections list with a status of Available.
Connection stuck in Pending?
- If the status shows Pending instead of Available, click Update pending connection and complete the GitHub authorization flow again.
- If you see an error about organization permissions, make sure you are the owner of the GitHub account or repository you are connecting.
help me get my AWS CodeConnections GitHub connection to Available status
Your code is on GitHub and AWS can access it. Next up, you will build a CI/CD pipeline that automatically pulls this code and builds it on every push.
Build Your CI/CD Pipeline
Your code is on GitHub and your AWS account can see the repo. Now you need something that actually builds your code every time you push.
In this step, you will create an AWS CodeBuild project and an AWS CodePipeline pipeline that work together. CodePipeline watches your GitHub repo for changes and CodeBuild runs your buildspec.yml to build the code. Right now the pipeline has no security scanning, so it will happily build and pass even though your code contains a hardcoded password.
In this step, get ready to:
- Create a CodeBuild project that builds your Python app from GitHub.
- Create a CodePipeline pipeline that triggers on every push and runs the build.
- Watch the pipeline ship code with a hardcoded password without checking for it.
Create a CodeBuild project
CodeBuild is a managed build service that compiles your source code and runs commands defined in a buildspec.yml file. You will create a CodeBuild project that points at your GitHub repo and uses the buildspec you already pushed.
- Open the AWS CodeBuild console by searching for CodeBuild in the AWS Management Console search bar and selecting it.
- In the left navigation pane, expand Build, then choose Build projects.
- Click Create build project.
What is CodeBuild?
AWS CodeBuild is a fully managed build service. You give it source code and a build specification, and it provisions a temporary container, runs your commands, and reports back. You pay only for the build minutes you use.
- In the Project configuration section, enter a name for your build project in the Project name field (for example, devsecops-security-scan).
- Scroll down to the Source section.
- For Source provider, choose GitHub.
- For Credential, choose Default source credential.
Don't see your repositories listed?
If no repositories appear, you need to link your GitHub connection first. Click Manage default source credential.
- For Connection type, choose GitHub App.
- Under Connection, select the connection you created in Step 1.
- Click Save.
- For Repository, choose Repository in my GitHub account and select the repository you created in Step 1.
- Scroll down to the Environment section.
- For Provisioning model, leave On-demand selected.
- For Environment image, choose Managed image.
- For Compute, choose EC2.
- For Operating system, choose Amazon Linux.
- For Runtime(s), choose Standard.
- For Image, choose aws/codebuild/amazonlinux-x86_64-standard:5.0.
Why this image and compute type?
The aws/codebuild/amazonlinux-x86_64-standard:5.0 image includes Python 3.12 as a selectable runtime. The build.general1.small compute type gives you 100 free build minutes per month, so this project costs nothing.
- For Service role, leave New service role selected. CodeBuild will create an IAM role with the permissions it needs to run builds.
- Scroll down to the Buildspec section.
- For Build specifications, choose Use a buildspec file. CodeBuild will look for buildspec.yml in the root of your repository automatically.
- Leave all other settings at their defaults.
- Scroll to the bottom of the page and click Create build project.
You should see a success banner confirming your build project was created.
Don't see your GitHub repo in the source list?
Make sure the GitHub connection you created in Step 1 has a status of Available. To check, open Settings in the Developer Tools console, then select Connections. If the status shows Pending, click on the connection and complete the authorization with GitHub.
Help me troubleshoot my GitHub connection in CodeBuild.
Create a CodePipeline pipeline
A CodeBuild project on its own only runs when you manually start a build. To trigger builds automatically on every push, you need a pipeline.
CodePipeline orchestrates the workflow: it detects changes in your repo and kicks off CodeBuild.
- Open the AWS CodePipeline console by searching for CodePipeline in the AWS Management Console search bar and selecting it.
- Click Create pipeline.
- On Step 1: Choose creation option, select Build custom pipeline.
- Click Next.
- On Step 2: Choose pipeline settings, enter a name for your pipeline in the Pipeline name field (for example, devsecops-pipeline).
- For Pipeline type, choose V2.
- For Service role, leave New service role selected. CodePipeline will create an IAM role for itself.
- Leave Artifact store set to Default location. CodePipeline will create an S3 bucket to store pipeline artifacts automatically.
- Click Next.
What is a V2 pipeline?
AWS recommends V2 for all new pipelines. V2 pipelines support Git tag triggers, pipeline-level variables, and better GitHub integration. One active V2 pipeline per month is included in the free tier.
- On Step 3: Add source stage, for Source provider, choose GitHub (via GitHub App).
- Under Connection, select the GitHub connection you created in Step 1.
- In Repository name, select your GitHub repository.
- In Default branch, make sure main is selected.
- Leave Output artifact format set to CodePipeline default.
- Click Next.
- On Step 4: Add build stage, choose Other providers.
- For Build provider, choose AWS CodeBuild.
- For Project name, select the CodeBuild project you just created.
- Leave all other settings at their defaults.
- Click Next.
- On Step 5: Add test stage, click Skip test stage.
- A confirmation dialog appears. Click Skip to confirm.
- On Step 6: Add deploy stage, click Skip deploy stage.
- Click Skip again to confirm.
- On Step 7: Review, review your pipeline configuration.
- Click Create pipeline.
The pipeline starts running immediately after creation. You will see it pull the source from GitHub and kick off the build.
What happens behind the scenes?
When you created the pipeline, CodePipeline automatically created an S3 artifact bucket to pass files between stages. It also created an IAM service role with the permissions it needs.
The Source stage pulls a zip of your code from GitHub, and the Build stage sends it to CodeBuild to run your buildspec.yml commands.
Pipeline stuck or Source stage failing?
Make sure your GitHub connection status is Available in the Developer Tools console. If the Source stage shows an error, click Details on the failed action to see the error message.
Help me fix my CodePipeline source stage connection.
- Wait for both the Source and Build stages to show a green Succeeded status before continuing.
Trigger the pipeline and observe success
The pipeline ran automatically when you created it. Now trigger it yourself by pushing a small change to your code.
- Switch back to your terminal.
- Add a comment to app.py by running this command:
echo "# Pipeline trigger test" >> app.py
- Commit and push the change by running these commands:
git add .
git commit -m "Test pipeline trigger"
git push
- Return to the CodePipeline console and click on your pipeline name to view it.
Before you look at the result, take a moment to think. Your app.py file contains db_password = "SuperSecret123!" in plain text. Do you think the pipeline will catch this and fail, or will it pass?
- Watch the pipeline stages update. Both the Source and Build stages should show a green Succeeded status.
The pipeline passed. Your code shipped successfully with a hardcoded database password baked right into it, and the pipeline did not even blink.
It ran the build commands from buildspec.yml, they completed without error, and that was it. No security check, no warning, nothing.
Why is this a problem?
In a real-world deployment, this code would go straight to production with a hardcoded password anyone can read in the source repo. An attacker who gains access to your repo instantly has your database credentials.
This is exactly the gap that DevSecOps exists to close. In the next step, you will add a security scanner directly into the pipeline so it catches vulnerabilities like this before they ship.
Your pipeline builds and ships code on every push. But it has a blind spot: it does not check for security vulnerabilities. Next up, you will add a security scanner to the pipeline that catches that hardcoded password and blocks the build.
Add Security Scanning to the Pipeline
In the last step, your pipeline built and passed. But app.py still has a hardcoded database password, and the pipeline shipped it without checking.
That is exactly the problem DevSecOps solves. In this step, you will add Bandit, a Python security scanner, to your buildspec so every push gets scanned for vulnerabilities before it can proceed.
In this step, get ready to:
- Update buildspec.yml to install and run Bandit as a security scanner.
- Push the changes and watch the pipeline catch the hardcoded password.
Update buildspec.yml to install and run Bandit
Bandit is a SAST (Static Application Security Testing) tool built specifically for Python. It scans your source code for common security issues like hardcoded passwords, SQL injection, and unsafe function calls.
When Bandit finds a problem, it exits with a non-zero exit code. CodeBuild treats any non-zero exit code as a build failure. This means Bandit acts as a security gate automatically.
Why Bandit instead of other SAST tools?
Bandit installs with a single pip command, requires no external service or account, and its non-zero exit code integrates perfectly with CodeBuild's pass/fail logic. Alternatives like SonarQube require running a server, and Semgrep needs more configuration for a simple security gate.
- Open buildspec.yml in your code editor.
- Replace the entire contents of the file with this updated version:
version: 0.2
phases:
install:
runtime-versions:
python: 3.12
commands:
- echo "Installing Bandit security scanner..."
- pip install bandit
build:
commands:
- echo "Running Bandit SAST scan..."
- bandit -r . -x ./venv
- echo "Security scan passed. Building application..."
- python -c "import app; print('Build successful')"
What does this code do?
- install phase: sets the Python runtime to 3.12 and installs Bandit via pip install bandit.
- build phase: runs bandit -r . -x ./venv to recursively scan all Python files in the project, excluding the ./venv directory.
- If Bandit finds any issues, it exits with a non-zero code. CodeBuild stops the build immediately, and the remaining commands never run.
- If Bandit finds no issues, the build continues to the python -c "import app; print('Build successful')" command.
What is shift-left security?
Shift-left security means moving security checks earlier in the development process. Instead of waiting for a separate security team to review code after deployment, you catch vulnerabilities right inside the pipeline. The earlier you catch a bug, the cheaper and safer it is to fix.
- Save buildspec.yml.
✔️ Awesome, I've got everything!
Great. Double check you have saved buildspec.yml before moving on.
ⓧ I'd like to double check the full code
Here is the complete buildspec.yml file. Make sure your file matches this exactly:
version: 0.2
phases:
install:
runtime-versions:
python: 3.12
commands:
- echo "Installing Bandit security scanner..."
- pip install bandit
build:
commands:
- echo "Running Bandit SAST scan..."
- bandit -r . -x ./venv
- echo "Security scan passed. Building application..."
- python -c "import app; print('Build successful')"
YAML not looking right?
- YAML is indentation-sensitive. Make sure you use spaces, not tabs, and that each level is indented by exactly two spaces.
- Check that runtime-versions is nested under install, not under commands.
- help me fix my buildspec.yml formatting
Push the updated buildspec and watch the pipeline fail
Your buildspec now includes Bandit. Time to push the change and let the pipeline pick it up.
- Commit and push the updated buildspec by running these commands:
git add buildspec.yml
git commit -m "Add Bandit security scanning"
git push
Before you check the pipeline, take a moment to predict. Your code still has db_password = "SuperSecret123!" sitting in app.py. Do you think the pipeline will pass or fail this time?
- Return to the CodePipeline console in your browser.
- Click on your pipeline name to view its details.
- Wait for the pipeline to trigger automatically from your push. The Source stage should complete first, then the Build stage begins.
This time, the Build stage shows a red Failed status. That is exactly what you want. The security gate caught the hardcoded password and blocked the build from proceeding.
- Click the Details link on the failed Build stage to open the CodeBuild build details.
- Scroll down to the Build logs section to see the full Bandit output.
In the logs, you should see Bandit reporting a finding with test ID B105 (hardcoded_password_string). It found the db_password = "SuperSecret123!" line in app.py and flagged it as a possible hardcoded password.
What did Bandit find?
Bandit's B105 test scans for variables with names like password, secret, or token that are assigned a string literal. Your db_password variable matched the password pattern and its value is a hardcoded string, so Bandit flagged it.
Because Bandit exited with a non-zero code, CodeBuild treated the build as failed. The pipeline stopped. No insecure code got through.
Pipeline succeeded instead of failing?
- Make sure you saved buildspec.yml before committing. Run git diff to check if your changes were included in the commit.
- Confirm the bandit -r . -x ./venv line is in the build phase of your buildspec. If it is in the install phase, Bandit runs without scanning your code.
- Check that app.py still contains db_password = "SuperSecret123!" and has not already been fixed.
- help me figure out why Bandit did not catch the hardcoded password in my pipeline
Your security gate is working. Bandit caught the hardcoded password and blocked the pipeline from going any further. Next up, you will fix the vulnerability and watch the pipeline go green.
Fix the Vulnerability and Ship Clean Code
Your security gate did exactly what it was designed to do. In the previous step, Bandit caught the hardcoded password in app.py and CodeBuild failed the build, blocking the code from going any further.
Now you will fix the vulnerability by replacing the hardcoded password with an environment variable reference. Once you push the fix, the pipeline will run the same security scan on your updated code and this time the build will pass.
In this step, get ready to:
- Fix the hardcoded password in app.py by using an environment variable.
- Push the fix and confirm the pipeline succeeds.
Fix the hardcoded password in app.py
Hardcoded passwords should never live in source code. The standard fix is to read sensitive values from environment variables at runtime, so the secret never appears in the codebase.
- Open app.py in your editor.
- Add import os at the very top of the file, above the def get_database_connection(): line:
import os
- Now find this line inside the get_database_connection() function:
db_password = "SuperSecret123!"
- Replace "SuperSecret123!" with os.environ.get("DB_PASSWORD"), so the line looks like this:
db_password = os.environ.get("DB_PASSWORD")
What do these changes do?
The import os line gives your script access to Python's os module, which can read environment variables from the system.
os.environ.get("DB_PASSWORD") reads the password from an environment variable at runtime instead of storing it in the code. Bandit no longer sees a hardcoded string, so it has nothing to flag.
- Save app.py.
✔️ Awesome, I've got everything!
Great. Double check you have saved app.py before moving on.
ⓧ I'd like to double check the full code
Here is the complete app.py after both edits. Compare it line by line with yours.
import os
def get_database_connection():
db_host = "localhost"
db_port = 5432
db_name = "myapp"
db_user = "admin"
db_password = os.environ.get("DB_PASSWORD")
connection_string = (
f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
)
return connection_string
def main():
conn_string = get_database_connection()
print("Connecting to database...")
print(f"Using host: {db_host}, port: {db_port}")
if __name__ == "__main__":
main()
Push the fix and watch the pipeline go green
Your fixed code is saved locally. Now you need to push it to GitHub so CodePipeline picks up the change and runs the security scan again.
- Commit and push the fix by running these commands in your terminal:
git add app.py
git commit -m "Fix hardcoded password with environment variable"
git push
What does this do?
These three commands stage your changed file, create a commit with a descriptive message, and push it to your GitHub repository. The push triggers your CodePipeline automatically.
Push rejected or authentication error?
- Make sure you are in your project directory (the folder containing app.py).
- If you see a credentials error, confirm that your GitHub authentication (SSH key or personal access token) is still valid.
Help me troubleshoot my git push
Before you check the pipeline, think about this: do you expect the build to succeed or fail this time? What is different about your code now compared to the last push?
- Return to the CodePipeline console in your browser.
- Click on your pipeline name to open the pipeline detail view.
- Wait for the Source stage to complete and the Build stage to start. This may take a minute or two.
- Watch the Build stage until it finishes.
The Build stage should show a green Succeeded status. Bandit scanned your updated code, found no hardcoded password, and exited with code 0. CodeBuild treated that as a passing build.
- Click Details on the Build stage to open the CodeBuild logs.
- Scroll through the build output. You should see Bandit completing its scan with no issues reported, followed by the Build successful message.
What just happened?
You just completed the full DevSecOps loop. You wrote code, pushed it, and a security scanner automatically caught a vulnerability and blocked the build. Then you fixed the vulnerability, pushed again, and the scanner confirmed the code was clean.
This is the shift-left security pattern in action. Security problems get caught early in the development pipeline, not after deployment when they are far more expensive and dangerous to fix.
Build stage still showing Failed?
- Open the CodeBuild logs and look for Bandit's output. If it still shows a hardcoded password finding, your app.py change did not make it to GitHub. Run git status to check if the file was committed and pushed.
- Check that the db_password line uses os.environ.get("DB_PASSWORD") and not a string literal. Even a different hardcoded string triggers Bandit.
- Confirm that import os is at the top of app.py. Without it, the script will fail with an ImportError during the build step.
Help me debug my pipeline build failure
Secret mission
Add Severity Thresholds to Your Security Gate
In real-world pipelines, not every finding deserves to block a deployment. Configure Bandit's severity thresholds so only MEDIUM and HIGH findings fail the build, while LOW findings are allowed through.
Clean Up Your Resources
Clean Up Your Resources
This project uses AWS Free Tier services, so there are no ongoing costs while your pipeline and build project sit idle. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- AWS CodePipeline pipeline (1 free active pipeline per month).
- AWS CodeBuild project (100 free build minutes per month on build.general1.small).
- AWS CodeConnections GitHub connection.
- S3 artifact bucket (created automatically by CodePipeline).
- IAM service roles for CodePipeline and CodeBuild.
- GitHub repository.
- Local project files (app.py, buildspec.yml).
Keep everything running
No action needed. Choose this if you're still actively building or plan to extend this pipeline.
- Your pipeline and build project are free tier eligible and do not incur charges while idle.
- The pipeline only runs (and consumes build minutes) when you push code to your connected GitHub repo.
- You have 1 free active pipeline and 100 free build minutes per month on the build.general1.small compute type.
Pause - I'll come back to this later
Your resources do not incur charges while idle. The pipeline only triggers when you push code, so there is nothing to shut down.
- Avoid pushing to the connected GitHub repository branch while you are away to prevent using build minutes.
- All your AWS resources will be waiting for you when you return.
- If you create other pipelines while paused, keep in mind that the free tier covers only 1 active pipeline per month.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow these steps in order.
- Open the AWS CodePipeline console. Select your pipeline, then choose Delete pipeline. Confirm the deletion when prompted.
- Open the AWS CodeBuild console. Select your build project, then choose Delete build project. Confirm the deletion when prompted.
- Open the AWS Developer Tools console. Go to Settings, then Connections. Select your GitHub connection and delete it.
- Open the Amazon S3 console. Find the artifact bucket created by CodePipeline (its name contains codepipeline). Select the bucket, choose Empty to remove all objects, then choose Delete to remove the bucket itself.
- Open the IAM console and go to Roles. Search for roles containing your pipeline name or codepipeline and codebuild. Select each service role and delete it.
- On GitHub, open your repository and go to Settings. Scroll down to the Danger Zone section and choose Delete this repository. Confirm the deletion when prompted.
- Delete the local project folder on your Desktop by running this command in your terminal:
macOS
cd ~/Desktop
ls
Find the name of your project folder in the output, then run:
rm -rf your-project-folder-name
Windows
cd ~\Desktop
dir
Find the name of your project folder in the output, then run:
rmdir /s /q your-project-folder-name
Nice Work!
Nice Work!
You just built a DevSecOps pipeline on AWS that catches security vulnerabilities before they ship. Your code gets scanned on every single push, and nothing gets through unless it passes the security gate.
What you learned:
- Built a CI/CD pipeline from scratch using AWS CodePipeline and AWS CodeBuild connected to a GitHub repository.
- Experienced the core DevSecOps problem firsthand by watching a pipeline ship code with a hardcoded password, then added Bandit SAST scanning to catch and block the vulnerability.
- Fixed the vulnerability and completed the full shift-left security loop: write code, get blocked by the security gate, fix the issue, and watch the pipeline go green.
- Secret Mission: Configured severity-based thresholds so only MEDIUM and HIGH findings block deployment, while LOW findings are logged but allowed through.
Ready to quiz yourself?