Three weeks ago, if you had told me I'd be writing a blog post about Infrastructure as Code (IaC), I would have laughed. Not because I didn't want to learn it, but because it seemed like this mysterious, complex thing that only seasoned DevOps engineers could master.
I was wrong. Very wrong.
This blog you're reading right now? It's deployed using Azure Static Web Apps, managed with Bicep templates, and automatically deployed through GitHub Actions. And honestly, if I can build this, anyone can.
Why I Decided to Take the Plunge
I've been working on various development projects using my Azure free credits, but I was getting tired of clicking through the Azure portal every time I wanted to deploy something. More importantly, I kept hearing about Infrastructure as Code from our DevOps team at work, and I wanted to understand what they were talking about when they mentioned "pipelines" and "deployment automation."
The final push came when one of our DevOps engineers (shoutout to you if you're reading this!) explained how IaC could save me hours of manual work while making my deployments more reliable. He was right, but the learning curve felt intimidating.
I decided to start with a personal blog project - something visible and rewarding that would force me to learn the fundamentals without the pressure of a work deadline.
The Stack: Keeping It Simple but Powerful
After some research, I settled on a stack that seemed approachable for a beginner:
- Azure Static Web Apps - Perfect for hosting static sites with built-in CI/CD
- GitHub Actions - Automation platform I already knew existed
- Bicep - Azure's domain-specific language for IaC (supposedly easier than ARM templates)
- GitHub - Where my code already lived
The beauty of this setup? Everything integrates naturally, and most of it is free for personal projects.
Setting Up the Foundation
The Service Principal Dance
Before diving into code, I needed to set up authentication between GitHub and Azure. This involves creating a "service principal" - essentially a robot user that GitHub can use to deploy resources on your behalf.
az ad sp create-for-rbac \
--name "github-actions-personal-blog" \
--role contributor \
--scopes /subscriptions/{your-subscription-id} \
--sdk-auth
The output is a JSON blob that goes into your GitHub repository secrets as AZURE_CREDENTIALS. Simple enough, though it took me a few tries to get the permissions right.
Repository Structure
I organized my project like this:
my-blog/
├── .github/workflows/deploy.yml # The automation magic
├── infrastructure/main.bicep # Infrastructure definition
├── src/index.html # The actual blog content
└── README.md # Documentation
Clean and straightforward - exactly what you want when learning something new.
The Real Learning: When Things Go Wrong
Challenge #1: Decoding GitHub Actions Logs
My first deployment failed spectacularly. The GitHub Actions log showed this cryptic error:
ERROR: "RepositoryUrl is invalid. The repository url must use HTTPS."
At first, I panicked. Then I realized this was exactly the kind of problem-solving that makes you a better developer. I started reading the logs more carefully and discovered that GitHub's github.repositoryUrl variable returns an SSH URL ([email protected]:user/repo.git), but Azure Static Web Apps expects HTTPS (https://github.com/user/repo.git).
The fix was simple:
# Convert SSH URL to HTTPS format
REPO_URL="https://github.com/${{ github.repository }}.git"
The lesson was valuable: Error messages aren't scary - they're clues. Learning to read deployment logs is a superpower.
Challenge #2: Security Warnings
My Bicep template initially had these outputs:
output repositoryToken string = staticWebApp.listSecrets().properties.repositoryToken
output deploymentToken string = staticWebApp.listSecrets().properties.apiKey
The deployment warned me that outputs shouldn't contain secrets. Fair point! I learned to retrieve secrets during the deployment process instead of exposing them in template outputs.
These weren't roadblocks - they were learning opportunities that made me understand the security considerations behind IaC.
The Bicep Template: Infrastructure as Code in Action
Here's the core of my infrastructure definition:
resource staticWebApp 'Microsoft.Web/staticSites@2022-09-01' = {
name: staticWebAppName
location: location
sku: {
name: 'Free'
tier: 'Free'
}
properties: {
repositoryUrl: repositoryUrl
branch: branch
buildProperties: {
appLocation: appLocation
apiLocation: apiLocation
outputLocation: outputLocation
}
stagingEnvironmentPolicy: 'Enabled'
}
}
What I love about this: it's declarative. I'm describing what I want, not how to create it. Azure figures out the implementation details.
The GitHub Actions Pipeline: Automation Magic
My deployment workflow does three main things:
- Validates the infrastructure before deploying
- Deploys the Azure resources
- Publishes the website content
The validation step was a game-changer for me:
- name: What-If Analysis
run: |
az deployment group what-if \
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
--template-file infrastructure/main.bicep \
--parameters repositoryUrl="$REPO_URL"
This shows exactly what changes will be made before making them. It's like having a safety net for your infrastructure changes.
The Moment of Truth
After fixing those initial hiccups, I pushed my code to the main branch and watched the GitHub Actions tab like a hawk.
Green checkmarks across the board.
Then I clicked the deployment URL and saw my blog live on the internet with an SSL certificate and everything. That feeling of seeing your code running in the cloud? Absolutely incredible.
What I Actually Learned
Technical Skills
- Bicep syntax and Azure resource definitions
- GitHub Actions workflow design and troubleshooting
- Azure Static Web Apps configuration and deployment
- Infrastructure validation and what-if analysis
- Security best practices for secrets and authentication
Mindset Shifts
- Infrastructure as Code isn't magic - it's just configuration files that get executed
- Error messages are helpful when you take time to read them carefully
- Iteration is normal - nobody gets deployments right on the first try
- Documentation matters - future you will thank present you
Resources That Made This Possible
I couldn't have done this without some excellent learning materials:
Special thanks to my own personal Brent who introduced me to these concepts and encouraged me to dive in. Having someone to ask "probably dumb" questions made this journey so much smoother.
What's Next: Phase 2 and Beyond
This blog is just the beginning. I'm already planning to add:
- Azure Functions for dynamic functionality (contact forms, visitor counters)
- Multiple environments (dev/staging/production) with promotion workflows
- Monitoring and analytics with Application Insights
- Custom domain setup and advanced deployment strategies
The foundation is solid, and adding features will be an opportunity to learn more advanced IaC patterns.
Why You Should Try This Too
If you're a developer who's been curious about DevOps but felt intimidated, I want to tell you something: you already have most of the skills you need.
You understand code structure, debugging, and problem-solving. IaC is just applying those same skills to infrastructure management. The concepts that seemed foreign three weeks ago now feel like natural extensions of development practices I already knew.
And if you're not a developer - if you're a sysadmin like me who's transitioned into security engineering, spending your days wrangling PowerShell scripts and Python automation - you're actually closer than you think. Every time you've automated a repetitive task, standardized a deployment process, or written a script to configure multiple servers consistently, you've been thinking in Infrastructure as Code principles. You just didn't call it that.
The jump from "I automate my daily tasks" to "I define my infrastructure in code" is smaller than it appears. You already understand the pain points IaC solves because you've lived them.
Start small. Pick a simple project like a static website. Use the free tiers. Break things. Fix them. Learn from the error messages.
Most importantly, don't let perfect be the enemy of good. My first deployment failed. My Bicep template had security warnings. My workflow needed multiple iterations. That's not failure - that's learning.
The Real Victory
The best part about this project isn't the working blog (though I'm pretty proud of it). It's the confidence that comes from demystifying something that seemed impossibly complex just a few weeks ago.
Now when our DevOps team talks about deployment pipelines and infrastructure automation, I don't just nod along - I actually understand what they're discussing. Better yet, I can contribute to those conversations.
That's the real power of hands-on learning.