Security | Supply Chain | DevSecOps

Mapping My Cloud Resume Software Supply Chain

Before adding security tools, I mapped how code moves from my laptop to GitHub, Azure, Cloudflare, and production.

Why I Started With A Baseline

After finishing the core Azure Cloud Resume Challenge, I wanted to extend the project with the security version of the challenge: securing the software supply chain. My first instinct was to start adding tools right away, but that would have skipped an important step.

Before adding CodeQL, Dependabot, SBOM generation, or vulnerability scanning, I needed to understand what I was protecting. That is what this first phase was about: mapping how code gets from my local machine to production and identifying where risk exists today.

For anyone new to cloud or DevSecOps, this is a useful habit. A security tool is only helpful if you know which part of the system it protects.

What A Software Supply Chain Means

A software supply chain is everything involved in building, testing, deploying, and running software. It includes the code I write, the dependencies I install, the GitHub Actions workflows that run automation, the secrets and identities used for deployments, and the cloud services that receive the final application.

Even a small resume site has a real supply chain. My project is not just an HTML file sitting on a server. It includes a frontend repo, a backend repo, an infrastructure repo, GitHub Actions, Azure resources, Cloudflare, Python dependencies, and deployment credentials.

That means there are several places where a mistake or compromise could affect production.

The Repositories In Scope

I mapped three public GitHub repositories:

  • frontend-website: the static resume site, blog pages, visitor counter JavaScript, and frontend deployment workflow.
  • backend-api: the Python Azure Function that increments and returns the visitor count.
  • cloud-resume-infra: the Bicep infrastructure modules and project architecture documentation.

Each repo has a different role, so each one has a different risk profile. The frontend repo can deploy public website changes. The backend repo owns application logic and Python dependencies. The infra repo describes the Azure resources that support the whole project.

Current Production Flow

The high-level software delivery path looks like this:

Developer workstation
  -> Git commit
  -> GitHub repository
  -> GitHub Actions
  -> Azure deployment target
  -> Cloudflare cache purge
  -> Public website and API

The live request path looks like this:

Visitor
  -> Cloudflare DNS/CDN
  -> Azure Storage Static Website
  -> Browser JavaScript fetch()
  -> Azure Function HTTP API
  -> Cosmos DB Table API

Those two flows are related but not the same. The first is how code reaches production. The second is how users interact with the system after it is deployed. Both matter for security.

Frontend Supply Chain

The frontend repo currently has the strongest deployment controls. The GitHub Actions workflow logs in to Azure using OpenID Connect instead of a long-lived Azure client secret. That means GitHub receives a short-lived identity token, Azure validates the repo and branch, and the workflow gets access without storing an Azure password in GitHub.

The frontend workflow also uses explicit permissions:

permissions:
  id-token: write
  contents: read

That is a good pattern because workflows should only receive the permissions they actually need.

After uploading HTML, CSS, and JavaScript files to Azure Storage, the workflow calls Cloudflare's API to purge cache. That makes the public site update quickly instead of waiting for cached files to expire.

Secrets and variables currently used by the frontend workflow are:

  • AZURE_CLIENT_ID
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID
  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ZONE_ID

The important part is that none of those values are committed to the repo.

Backend Supply Chain

The backend repo contains the Azure Function and Python dependency list. It currently has a GitHub Actions workflow that installs dependencies and runs pytest.

The dependency file is small, which is good for learning and auditing:

  • azure-functions
  • azure-data-tables==12.6.0
  • pytest

This repo is the best first target for SBOM and vulnerability scanning later because it has real runtime dependencies. If a vulnerable Python package is introduced, I want the pipeline to catch it before it ships.

One thing I noted during the baseline is that the visible backend workflow is a test workflow. If deployment is handled elsewhere or added later, that path should be documented clearly so the security model is accurate.

Infrastructure Supply Chain

The infrastructure repo contains Bicep modules for the Azure resources behind the project. This includes the static website storage account, Cosmos DB Table API account, Function App, Flex Consumption plan, and Function runtime storage.

This repo does not deploy automatically yet. That is intentional for now. The current safe use is validation:

az bicep build --file infra/main.bicep

And previewing changes before applying anything:

az deployment group what-if \
  --resource-group rg-cloudresume-prod \
  --template-file infra/main.bicep \
  --parameters infra/main.parameters.json

Because the Azure resources were originally created manually, I do not want to blindly apply Bicep to production. The right next step is to review the full what-if output and tune the template until the proposed changes are expected.

Trust Boundaries

A trust boundary is a place where control changes hands. In this project, the main boundaries are:

  • My workstation: where code changes and commits start.
  • GitHub repositories: where source code, workflows, secrets, and variables live.
  • GitHub Actions runners: where tests and deployment automation execute.
  • Azure: where the static site origin, Function App, Cosmos DB, and storage resources live.
  • Cloudflare: where DNS, CDN, HTTPS, and cache purge are handled.

Thinking in trust boundaries helped me organize the security work. Signed commits protect the path from workstation to GitHub. Branch protection helps control what reaches default branches. Code scanning and dependency scanning protect the CI path. OIDC reduces long-lived cloud credential risk.

What Was Already In Good Shape

The baseline showed a few good controls already in place:

  • All three repos are public, which allows GitHub security features like CodeQL to be used more easily.
  • Secret scanning is enabled on the repos.
  • Push protection is enabled, which helps block accidental secret commits.
  • The frontend deployment uses Azure OIDC instead of a stored Azure client secret.
  • The frontend workflow uses minimal explicit permissions.
  • Cloudflare cache purge is automated after frontend deployment.
  • The infra repo now documents the current Azure architecture in Bicep.

That is a decent starting point. The project is not starting from zero.

The Gaps I Found

The baseline also found several gaps that are worth fixing:

  • Default branches are not protected yet.
  • The latest checked commits are unsigned.
  • Dependabot security updates are disabled.
  • CodeQL is not enabled yet.
  • The backend does not generate an SBOM yet.
  • Grype or a second dependency vulnerability scanner is not configured yet.
  • The infra repo does not have a Bicep build or what-if workflow yet.
  • Cloudflare settings are operational but not fully documented as configuration.

None of these findings are surprising. This is exactly why a baseline matters. It turns a vague goal like "secure the project" into a concrete checklist.

Why The Next Step Is Commit Signing And Branch Protection

The next phase will focus on repository trust and commit integrity. Before adding more scanners, I want to make sure changes to important branches are controlled.

Signed commits help prove that commits came from a trusted identity. Branch protection or repository rules help prevent direct, unchecked changes to production branches. Required checks make sure automation passes before code is merged.

For a solo project, this might feel like extra ceremony. But for cloud and security work, it is a practical habit. The same pattern scales to teams where multiple people can change code, workflows, or infrastructure.

What I Learned

The biggest lesson from Phase 1 is that supply-chain security starts with visibility. Before I can protect the pipeline, I need to know what the pipeline is.

I also learned that a small project can still demonstrate real security thinking. This resume site has cloud identity, CI/CD, secrets, dependencies, DNS, CDN behavior, and infrastructure code. Those are the same categories that show up in larger systems, just at a smaller scale.

Tomorrow's work will start Phase 2: signed commits and branch protection across the frontend, backend, and infrastructure repos.