If you’ve ever worked with CI/CD pipelines, you already know that building an application is only half the job.
The real challenge begins when you need to store, version, promote, and deploy artifacts reliably across environments.
That’s exactly where Azure Artifacts comes into play.
In this blog, we’ll walk through a complete real-time implementation of Azure Artifacts with Azure DevOps pipelines - from build to production - using a Node.js + Tailwind CSS project deployed to Azure App Service.
Why Azure Artifacts? (And How It’s Similar to Nexus & JFrog)
You may have heard of tools like:
- Nexus Repository
- JFrog Artifactory
Azure Artifacts works in a very similar way.
It acts as a central package management repository, just like:
- npm registry
- PyPI
- NuGet
- Maven
- Gradle
The idea is simple but powerful:
Build once → store the package → promote it across environments → deploy
This approach gives you:
- ✅ Better audit & compliance
- ✅ Versioned deployments
- ✅ Rollback capability
- ✅ Lifecycle & retention control
- ✅ Clean separation between build and release
Azure Artifacts Views: The Key Concept
Azure Artifacts organizes packages using Views:
- Local (default)
- Pre-release
- Release
How promotion works:
- Package is first published to Local
- Promoted to Pre-release → triggers Stage deployment
- Promoted to Release → triggers Production deployment
Each promotion can trigger a pipeline automatically.
This means:
🚀 Deployment happens only when a package is promoted, not rebuilt.
High-Level Flow of the Pipeline
Here’s the overall flow we’ll implement:
- Developer pushes code
- CI pipeline runs:
- npm install
- npm build
- npm publish → Azure Artifacts
- Package stored in Local view
- Package promoted to Pre-release
- Release pipeline triggers automatically
- App deployed to Azure App Service
- Package can later be promoted to Release for production
Step 1: Create Azure DevOps Project
- Go to Azure DevOps
- Create a new project (private)
- Import a sample repository (Nike landing page demo)
- Minor changes were made in
package.jsonto fix build issues
Step 2: Create Azure App Service
From Azure Portal:
- Create a Web App
- Runtime: Node.js 18
- OS: Linux
- Pricing plan: Free Tier (sufficient for demo)
- Region: Choose closest to you
Important Configuration (Cache Refresh)
Add these App Settings (environment variables):
WEBSITE_NODE_DEFAULT_VERSION
SCM_DO_BUILD_DURING_DEPLOYMENT
This ensures the app refreshes after every deployment.
Step 3: Verify App Service
At this point:
- App Service is live
- You’ll see the default Node.js landing page
- No application code deployed yet
This confirms infrastructure is ready.
Step 4: Create Build Pipeline (CI)
Pipeline Tasks:
- Checkout code
npm install- Custom install command for Tailwind CSS
npm run build- Publish package to Azure Artifacts
Why Custom Command?
Tailwind CSS requires additional flags, so we use a custom npm command instead of the default one.
Step 5: Create Azure Artifacts Feed
- Go to Artifacts
- Create a new feed (scoped to the project)
-
Supported package types include:
- npm
- NuGet
- Maven
- Gradle
- Pip
- Universal packages
Azure Artifacts also supports upstream sources, meaning it can pull packages from:
- npm registry
- PyPI
- NuGet.org
- and more
Step 6: Publish Package to Feed
Add an npm publish task in the pipeline:
- Working directory: folder containing
package.json - Target registry: Azure Artifacts feed
🚨 Important:
If you forget to select the registry, it may try publishing to public npm, which will fail.
Step 7: Fix Permission Error (403 Forbidden)
On first run, the pipeline fails with:
403 Forbidden - User lacks permission to publish package
Why this happens:
The Build Service account does not have contributor access to the feed.
Fix:
Go to:
- Artifacts → Feed settings → Permissions
-
Add these users as Contributors:
- Project Collection Build Service
- Project Build Service
This step is critical and often missed.
Step 8: Re-run Pipeline
- Increment version in
package.json(example:1.3.0) - Commit changes
- Pipeline runs automatically
✅ This time:
- Build succeeds
- Package is published
- Stored in Local view
You can now see:
- Version history
- Build metadata
- Git commit details
- Dependencies
Step 9: Create Release Pipeline
Why a Release Pipeline?
Azure Pipelines (YAML) do not support Azure Artifacts as an upstream trigger - this is a known limitation.
So we create a classic Release pipeline.
Release Configuration:
- Artifact source: Azure Artifacts
- Package type: npm
- Trigger view: Pre-release
- Version: Latest
This means:
Anytime a package is promoted to Pre-release → deployment starts automatically.
Step 10: Configure Deployment
- Deployment type: Azure App Service (Linux)
- Runtime stack: Static Site
- Package source: Azure Artifacts
- Post-deployment script to move files into
wwwroot
Why?
Because static sites won’t render if files stay inside a nested package directory.
Step 11: Promote Package & Deploy
- Go to Azure Artifacts
- Select package
- Click Promote
- Choose Pre-release
🎯 Result:
- Release pipeline triggers automatically
- Deployment starts
- App goes live
Step 12: Verify Deployment
Visit the App Service URL:
✅ Application loads
✅ Tailwind CSS works
✅ Fully responsive
✅ Deployment successful
Debugging & Monitoring (Very Important)
Azure App Service provides powerful tools:
- Diagnose and Solve Problems
- Application logs
- Platform logs
- Metrics
- Alerts
You also get Kudu (Advanced Tools):
- Browse deployed files
- SSH into the container
- Upload/download files
- Debug issues directly
This is extremely helpful in real production scenarios.
Cleanup (Don’t Forget This!)
After finishing:
- Delete the Resource Group
-
This automatically removes:
- App Service
- Plan
- Networking resources
This avoids unnecessary charges.
Final Thoughts
This setup demonstrates how Azure Artifacts fits perfectly into a real-world CI/CD strategy:
- Build once
- Store centrally
- Promote with confidence
- Deploy automatically
- Maintain compliance & traceability
If you’re aiming for enterprise-grade DevOps practices, this pattern is non-negotiable.
Top comments (0)