Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions .github/workflows/node-azure-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
name: Node.js CI/CD to Azure

# Teaching example for GitHub Actions with Azure deployment

on:
push:
branches: [ main, master ]
paths:
- 'nodeapp-1/**'
- '.github/workflows/node-azure-deploy.yml'
pull_request:
branches: [ main, master ]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'development'
type: choice
options:
- development
- staging
- production

env:
NODE_VERSION: '18.x'
AZURE_WEBAPP_NAME: 'webapp-az400-demo'
AZURE_WEBAPP_PACKAGE_PATH: './nodeapp-1'

jobs:
# ========================================
# JOB 1: BUILD AND TEST
# ========================================
build:
name: Build and Test
runs-on: ubuntu-latest

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: 🔧 Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/package-lock.json'

- name: 📦 Install dependencies
run: npm ci
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

- name: 🔒 Security audit
run: npm audit --audit-level=high
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
continue-on-error: true

- name: 🧹 Lint code
run: npm run lint || echo "No lint script configured"
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

- name: 🧪 Run tests
run: npm test -- --coverage || npm test
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

- name: 📊 Upload coverage reports
uses: codecov/codecov-action@v3
with:
directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/coverage
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false

- name: 🔨 Build application
run: npm run build || echo "No build required"
working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}

- name: 📤 Upload artifact for deployment
uses: actions/upload-artifact@v4
with:
name: node-app
path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}
retention-days: 5

# ========================================
# JOB 2: DEPLOY TO DEVELOPMENT
# ========================================
deploy-dev:
name: Deploy to Development
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: development
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: 📥 Download artifact
uses: actions/download-artifact@v4
with:
name: node-app
path: ./app

- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🚀 Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}-dev
package: ./app
startup-command: 'npm start'

- name: 🔥 Smoke test
run: |
sleep 30
response=$(curl -s -o /dev/null -w "%{http_code}" https://${{ env.AZURE_WEBAPP_NAME }}-dev.azurewebsites.net)
if [ $response -eq 200 ]; then
echo "✅ Smoke test passed!"
else
echo "❌ Smoke test failed with status code: $response"
exit 1
fi

# ========================================
# JOB 3: DEPLOY TO STAGING
# ========================================
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: deploy-dev
environment:
name: staging
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: 📥 Download artifact
uses: actions/download-artifact@v4
with:
name: node-app
path: ./app

- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🚀 Deploy to staging slot
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
slot-name: staging
package: ./app

- name: 🧪 Run integration tests
run: |
echo "Running integration tests against staging..."
# Add your integration test commands here
echo "✅ Integration tests passed!"

# ========================================
# JOB 4: DEPLOY TO PRODUCTION
# ========================================
deploy-prod:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: ${{ steps.swap-slots.outputs.webapp-url }}

steps:
- name: 🔐 Login to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: 🔄 Swap staging to production
id: swap-slots
run: |
az webapp deployment slot swap \
--resource-group rg-az400-demo \
--name ${{ env.AZURE_WEBAPP_NAME }} \
--slot staging \
--target-slot production

echo "webapp-url=https://${{ env.AZURE_WEBAPP_NAME }}.azurewebsites.net" >> $GITHUB_OUTPUT

- name: 🏷️ Create release tag
if: success()
uses: actions/github-script@v7
with:
script: |
const tag = `v${context.runNumber}`;
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/${tag}`,
sha: context.sha
});
console.log(`✅ Created tag: ${tag}`);

- name: 📊 Monitor deployment
run: |
echo "Monitoring production deployment..."
# Add Application Insights queries here
echo "✅ Production deployment healthy!"

# ========================================
# REUSABLE WORKFLOW: SECURITY SCAN
# ========================================
security-scan:
name: Security Scanning
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: 🔍 Run CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
languages: javascript

- name: 🛡️ Run Dependabot scan
uses: github/super-linter@v5
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_JAVASCRIPT_ES: true
VALIDATE_JSON: true
VALIDATE_YAML: true
134 changes: 134 additions & 0 deletions pipelines/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Pipeline Examples for AZ-400 Training

This directory contains verified YAML pipeline examples for teaching CI/CD concepts in the AZ-400 course.

## 📁 Pipeline Files

### 1. Single-Stage CI Pipeline (`single-stage-ci.yml`)
**Purpose:** Basic continuous integration for Node.js applications

**Key Features:**
- Node.js setup and dependency installation
- Linting and code quality checks
- Unit testing with coverage
- Artifact creation and publishing
- Clear commenting for learning

**Use Case:** Teaching CI fundamentals in Segment 2

### 2. Multi-Stage CI/CD Pipeline (`multi-stage-cicd.yml`)
**Purpose:** Complete DevOps pipeline with multiple environments

**Key Features:**
- 5 stages: Build → Dev → Staging → Production → Post-Deploy
- Environment approvals and gates
- Blue-green deployment strategy
- Automated rollback on failure
- Performance and integration testing
- Deployment slots and swapping

**Use Case:** Advanced pipeline concepts in Segment 3

### 3. GitHub Actions Workflow (`../.github/workflows/node-azure-deploy.yml`)
**Purpose:** GitHub-native CI/CD to Azure

**Key Features:**
- Matrix strategy for multiple Node versions
- GitHub environments with protection rules
- Azure Web App deployment
- Release tagging
- Security scanning with CodeQL
- Reusable workflow patterns

**Use Case:** Comparing Azure Pipelines vs GitHub Actions

## 🚀 How to Use These Examples

### For Azure Pipelines:
1. Create a new pipeline in Azure DevOps
2. Select "Existing Azure Pipelines YAML file"
3. Choose the appropriate YAML file
4. Update variables (service connections, resource names)
5. Save and run

### For GitHub Actions:
1. Workflow is automatically detected in `.github/workflows/`
2. Configure secrets in repository settings:
- `AZURE_CREDENTIALS` - Service principal JSON
3. Workflow triggers on push to main/master

## 📝 Required Configuration

### Azure Resources:
```bash
# Create resource group
az group create --name rg-az400-demo --location eastus

# Create App Service Plan
az appservice plan create \
--name plan-az400-demo \
--resource-group rg-az400-demo \
--sku B1 \
--is-linux

# Create Web Apps
az webapp create \
--name webapp-az400-demo-dev \
--resource-group rg-az400-demo \
--plan plan-az400-demo \
--runtime "NODE:18-lts"
```

### Service Connection (Azure DevOps):
1. Project Settings → Service connections
2. New service connection → Azure Resource Manager
3. Service principal (automatic)
4. Name: `AZ400-ServiceConnection`

### GitHub Secrets:
```bash
# Create service principal
az ad sp create-for-rbac \
--name "sp-az400-github" \
--role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/rg-az400-demo \
--json-auth
```

## 🎓 Teaching Points

### Single-Stage Pipeline:
- Pipeline triggers and paths
- Agent pools and VM images
- Variables and expressions
- Task vs script steps
- Artifact publishing

### Multi-Stage Pipeline:
- Stage dependencies
- Deployment jobs vs regular jobs
- Environment approvals
- Deployment strategies
- Failure handling

### GitHub Actions:
- Workflow syntax differences
- Actions marketplace
- Environments and secrets
- Reusable workflows
- GitHub-specific features

## 🔧 Customization

Students can modify these pipelines to:
- Add additional testing frameworks
- Implement different deployment strategies
- Add notifications (Teams, Slack)
- Include infrastructure as code
- Add security scanning tools

## 📚 Additional Resources

- [Azure Pipelines YAML schema](https://learn.microsoft.com/azure/devops/pipelines/yaml-schema)
- [GitHub Actions documentation](https://docs.github.com/actions)
- [Azure Web Apps deployment](https://learn.microsoft.com/azure/app-service/deploy-github-actions)
Loading
Loading