When Azure Auto-Generated My Workflow File: A DevOps Migration Story
When Azure Auto-Generated My Workflow File: A DevOps Migration Story
How I Solved Production Deployment Conflicts Moving from Manual Docker Builds to Automated CI/CD
TL;DR
Migrating from manual Docker deployments to automated GitHub Actions on Azure, I encountered deployment conflicts because Azure auto-generated a workflow file while my original manual workflow still existed. This post covers why this happens, how to fix it, and how to prevent it in your own projects.
Key Takeaway: Azure (and most CI/CD platforms) automatically creates workflow files when you connect GitHub Actions. Always audit your .github/workflows/ folder before enabling platform automation.
The Context: Manual Deployment Fatigue
I was running a Node.js backend on Azure App Service using manual Docker deployments. The process looked like this:
# Local development complete
# Now deploy...
# 1. Build Docker image locally
docker build -t myapp:latest .
# 2. Tag for Azure Container Registry
docker tag myapp:latest myregistry.azurecr.io/myapp:latest
# 3. Push to registry
docker push myregistry.azurecr.io/myapp:latest
# 4. Manually trigger Azure deployment
# (via Azure Portal or CLI)
It worked. But it had problems:
- β° Time-consuming: 5-10 minutes per deployment
- π Manual process: Required local machine to be ready
- π« No rollback: No easy way to revert bad deployments
- π No audit trail: Beyond Git commits
- π€ Single point of failure: Only I could deploy
Time to modernize.
The Goal: Automated CI/CD with GitHub Actions
I wanted the holy grail of modern deployment:
PUSH TO GITHUB β AUTOMATIC BUILD β AUTOMATIC DEPLOY β LIVE IN PRODUCTION
No manual steps. No local builds. Just code and ship.
Azure Deployment Center promised exactly this with GitHub Actions integration.
The Setup: Connecting GitHub to Azure
The process seemed straightforward. Azure's documentation made it look like three clicks:
Step 1: Navigate to Azure Deployment Center
Azure Portal β Your App Service β Deployment β Deployment Center
Step 2: Choose GitHub Actions
Three options presented:
- Container Registry (my current manual approach)
- GitHub Actions β This one!
- Azure Pipelines
Step 3: Connect GitHub
- Click "Authorize" to connect GitHub account
- Select Organization: anupamkushwaha85
- Select Repository: pinnaclewear-shop
- Select Branch: main
- Click "Save"
Done. Or so I thought.
The Surprise: Azure's Auto-Generation
Within 30 seconds of clicking "Save," something unexpected happened:
A New Commit Appeared
Checking my GitHub repository, I saw:
Commit: Add or update the Azure App Service build and deployment workflow config Author: Azure GitHub Actions Files Changed: 1 file
- main_pinnaclewear-api.yml
Azure had pushed code to my repository.
At first, I thought: "Wait, did I give it write access?"
Then I realized: This is EXACTLY what Azure is supposed to do.
What Azure Auto-Generated
Here's what the file looked like:
# .github/workflows/main_pinnaclewear-api.yml
# AUTO-GENERATED BY AZURE
name: Build and deploy Node.js app to Azure Web App - pinnaclewear-api
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: npm install, build, and test
run: |
npm install
npm run build --if-present
npm run test --if-present
- name: Zip artifact for deployment
run: zip release.zip ./* -r
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v3
with:
name: node-app
path: release.zip
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v3
with:
name: node-app
- name: Unzip artifact for deployment
run: unzip release.zip
- name: 'Deploy to Azure Web App'
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: 'pinnaclewear-api'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_XXXXX }}
package: .
Impressive! Azure had created:
- β Multi-stage build and deploy jobs
- β Proper Node.js environment setup
- β Artifact handling with zip/unzip
- β Deployment secrets configuration
- β Environment and URL tracking
All optimized for my specific stack.
The Problem: Two Workflows, One Repository
Here's what I didn't realize: My old manual deployment workflow was still active.
My Repository Structure Before Azure
.github/ workflows/ manual-deploy.yml β My original workflow
My Repository Structure After Azure
.github/ workflows/ manual-deploy.yml β Still there! main_pinnaclewear-api.yml β New Azure file
Both Workflows Triggered on Push
My old workflow:
on:
push:
branches: [ main ]
Azure's workflow:
on:
push:
branches: [ main ]
Result: Every push to main triggered BOTH workflows simultaneously.
The Chaos: Deployment Conflicts
My next deployment attempt looked like this:
git add .
git commit -m "Update API endpoint"
git push origin main
What Happened in GitHub Actions
Two workflow runs started simultaneously:
β Manual Deploy Workflow - In Progress β Build and deploy Node.js app - In Progress
Then...
β Manual Deploy Workflow - Failed β Build and deploy Node.js app - Failed
The Error Messages
GitHub Actions Logs:
Error: Invalid workflow file (Line: 21, Col: 1): 'name' is already defined, (Line: 23, Col: 1): 'on' is already defined
Azure Build Logs showed:
Update Azure workflow to use manual secrets deleting duplicate deployment file Merge branch 'main' of https://github.com/anupamkushwaha85/pinnaclewear-shop Add or update the Azure App Service build and deployment workflow config Fix TypeError: Failed to fetch
The Git Graph Chaos
My commit history became a mess of merge commits:
- Merge branch 'main' | | * Add or update the Azure App Service build...
- | Update Azure workflow to use manual secrets |/
- deleting duplicate deployment file
- Merge branch 'main'
Production was stuck on an old version. Deployments were failing. Time to debug.
The Investigation: Understanding the Root Cause
I examined both workflow files side by side:
The Differences
| Aspect | Manual Workflow | Azure Workflow | |--------|----------------|----------------| | Docker? | Yes, multi-stage build | No, direct Node deployment | | Registry | Azure Container Registry | N/A | | Secrets | Manual ACR credentials | Azure publish profile | | Optimization | Custom | Azure-optimized | | Maintenance | Manual updates | Auto-managed by Azure |
The Conflict Points
Both workflows:
- Triggered on the same event (
push to main) - Tried to deploy to the same Azure App Service
- Used different deployment mechanisms
- Competed for deployment slots
The Solution: Merge and Consolidate
I decided to keep Azure's auto-generated workflow (better optimized) and migrate my custom configurations.
Step 1: Extract Custom Configurations
From my manual-deploy.yml, I identified what needed to be preserved:
# Custom environment variables I had configured
env:
NODE_ENV: production
API_VERSION: v1
# Custom build step
- name: Run database migrations
run: npm run migrate:prod
Step 2: Merge into Azure's Workflow
I updated main_pinnaclewear-api.yml:
jobs:
build:
runs-on: ubuntu-latest
# ADDED: Custom environment variables
env:
NODE_ENV: production
API_VERSION: v1
steps:
- uses: actions/checkout@v4
- name: Set up Node.js version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: npm install, build, and test
run: |
npm install
npm run build --if-present
npm run test --if-present
# ADDED: Custom migration step
- name: Run database migrations
run: npm run migrate:prod
- name: Zip artifact for deployment
run: zip release.zip ./* -r
# ... rest of Azure's config
Step 3: Delete Duplicate Workflow
git rm .github/workflows/manual-deploy.yml
git commit -m "Remove duplicate deployment workflow, merged configs into Azure-generated file"
git push origin main
Step 4: Validate
Checked GitHub Actions:
β Build and deploy Node.js app to Azure Web App - Success (2m 47s)
Deployment succeeded! Production updated automatically.
The Results: Automated CI/CD Achieved
Before vs. After
BEFORE (Manual): Code ready β Build locally β Push to ACR β Manual deploy β 8-10 minutes
AFTER (Automated): Push to GitHub β Auto build β Auto deploy β 3 minutes β LIVE
Benefits Realized
β
Faster deployments: 8 minutes β 3 minutes
β
Zero manual steps: Push and forget
β
Full audit trail: Every deployment logged in GitHub Actions
β
Easy rollbacks: Revert commit = auto-rollback
β
Team-friendly: Anyone can deploy
β
Build verification: Tests run automatically
The Lessons: What I Learned
1. Azure's Auto-Generation is Standard Behavior
This isn't a quirk. Every platform does this:
| Platform | Auto-Generates | Location |
|----------|---------------|----------|
| Azure | GitHub Actions workflow | workflows |
| Vercel | Build config + workflow | vercel.json + .github |
| Netlify | Build config | netlify.toml |
| Railway | Railway config | railway.json |
| Render | Render config | render.yaml |
2. Always Audit Before Connecting
The checklist I now follow:
# Before connecting ANY CI/CD platform:
# 1. Check existing workflows
ls -la .github/workflows/
# 2. Backup current configs
git mv .github/workflows/current.yml .github/workflows/current.yml.backup
# 3. Document custom configurations
# Note any env vars, secrets, or custom steps
# 4. THEN connect the platform
# 5. Review auto-generated file
# 6. Migrate custom configs
# 7. Test in staging first
# 8. Delete old workflows
3. Platform Automation is Actually Brilliant
Azure's auto-generation handled:
- β Optimal workflow structure for Node.js
- β Multi-stage build and deployment
- β Artifact management (zip/unzip)
- β Secret configuration via publish profiles
- β Environment tracking
- β Production deployment slots
I would have spent hours writing this manually. Azure did it in seconds.
4. Git History is Your Friend
When deployments broke, I used:
# See what changed
git log --oneline --graph
# Compare workflow files
git diff HEAD~5 .github/workflows/
# Check which commit introduced the conflict
git blame .github/workflows/main_pinnaclewear-api.yml
Git's history made debugging trivial.
Best Practices: How to Avoid This
For Migrating Existing Projects
# Migration Checklist
preparation:
- Audit existing .github/workflows/ folder
- Document all custom configurations
- Test platform in staging environment first
- Create backup branch: git checkout -b backup-workflows
connection:
- Connect GitHub to platform (Azure/Vercel/etc.)
- Review auto-generated configurations
- DO NOT push to production yet
validation:
- Compare old vs. new workflow files
- Migrate custom environment variables
- Migrate custom build/deployment steps
- Test deployment in staging
cleanup:
- Delete duplicate workflow files
- Update documentation
- Monitor first few production deployments
- Keep backup branch for 2 weeks
For New Projects
# Start Clean
initial_setup:
- Connect platform FIRST (let it auto-generate)
- Review generated workflow
- Customize as needed
- Never create manual workflows if platform can auto-generate
For Teams
## Team Documentation Template
### Our CI/CD Setup
**Platform:** Azure + GitHub Actions
**Auto-Generated:** Yes (DO NOT manually create workflows)
**Workflow File:** `.github/workflows/main_app-name.yml`
**Maintained By:** Azure (auto-updated when platform changes)
**Custom Configs:** Documented in `docs/deployment.md`
### Making Changes
1. Never edit workflow file directly
2. Use Azure Deployment Center for configuration
3. For custom steps, discuss with team first
4. Test in staging before production
Technical Deep Dive: How Azure Auto-Generation Works
For those curious about the mechanics:
What Happens When You Click "Save"
- Azure validates GitHub connection β
- Azure analyzes your repository
- Detects language/framework (Node.js, Python, .NET, etc.)
- Identifies build system (npm, pip, maven, etc.)
- Checks for Dockerfile β
- Azure selects optimal workflow template
- Node.js β Node.js + zip deployment template
- Docker β Docker build + container deployment
- .NET β .NET build + publish template β
- Azure generates workflow YAML
- Injects app name, subscription details
- Creates deployment secrets in GitHub
- Adds Azure publish profile to GitHub Secrets β
- Azure commits file to GitHub
- Uses GitHub API with your authorized token
- Creates commit as "Azure GitHub Actions"
- Pushes to your selected branch β
- GitHub Actions triggered immediately
- First deployment runs automatically
- Azure monitors deployment status
The Secrets Azure Configures
# Azure automatically creates this secret in your GitHub repo:
AZUREAPPSERVICE_PUBLISHPROFILE_XXXXXXXXXX
# This contains:
- Publishing URL
- Username/Password for deployment
- Site name and configuration
- FTP credentials (if enabled)
Why This Approach?
Pros:
- β Zero manual YAML writing
- β Optimized for your specific stack
- β Auto-updated when Azure improves templates
- β Less chance of configuration errors
Cons:
- β Can surprise developers (like it did me!)
- β May conflict with existing workflows
- β Less control over workflow structure
Conclusion: Embrace Platform Automation
This experience taught me that modern cloud platforms are DESIGNED to automate.
Azure didn't break my deployment - it did exactly what it promised: created automated CI/CD with zero manual YAML writing.
The "problem" was my existing manual workflow, which I should have cleaned up first.
Key Takeaways
- Auto-generation is standard - Every major platform does this
- Audit before connecting - Check workflows first
- Merge, don't fight - Combine custom configs with platform templates
- Trust platform optimization - Auto-generated workflows are usually better
- Document for your team - Prevent others from repeating your mistakes
Going Forward
I now follow a simple rule:
If a platform can auto-generate it, let it. Customize after, not before.
My deployments are faster, more reliable, and fully automated. The hour spent debugging was worth the permanent efficiency gain.