This repository demonstrates a number of capabilities in GitHub and Microsoft Azure:
- Continuous Planning using GitHub Issues
- Continuous Integration using GitHub Repositories and GitHub Actions
- Continuous Deployment to App Services and Azure SQL using GitHub Actions
- Continuous Security using GitHub Advanced Security
- Continuous Monitoring using Azure Monitor and Application Insights
- Continuous Quality using unit tests and GitHub Actions
- Database migration using Entity Framework and GitHub Actions
- Blue/green deployments to App Services using Deployment Slots
- Local development environments using Dev Containers
-
Create and export development certificate from Windows:
New-Item $env:USERPROFILE/.aspnet/https -ItemType Directory -Force dotnet dev-certs https -ep $env:USERPROFILE/.aspnet/https/aspnetapp.pfx --password "<YourStrong@Passw0rd>" dotnet dev-certs https --trust
-
Copy development certificate to WSL:
sudo cp /mnt/c/Users/[Windows-Username]/.aspnet/https/aspnetapp.pfx ~/.aspnet/https
-
Fork the repository.
-
Clone repository to WSL and open in Visual Studio Code.
-
Open in Dev Container.
-
Run locally:
# Update packages dotnet outdated --upgrade # Restore dotnet restore # Build dotnet build # Set development connection string: dotnet user-secrets set "ConnectionStrings:Default" "Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres" --project src/MovieApi/ # Update database: dotnet ef database update --project src/MovieApi/ # Test dotnet test # Run dotnet watch run --project src/MovieApi/
-
Browse to https://localhost:8001/Movies to inspect API:
[ { "id": 5, "title": "12 Angry Men", "director": "Sidney Lumet", "year": 1957 }, { "id": 8, "title": "Pulp Fiction", "director": "Quentin Tarantino", "year": 1994 }, ... ]
-
Login to Azure and GitHub:
az login gh auth login
-
Declare input variables
SUBSCRIPTION=$(az account show --query id --output tsv) RESOURCE_GROUP="GitHubDemo" GITHUB_ORGANIZATION="ondfisk" REPOSITORY="GitHubDemo" APP_REGISTRATION_DISPLAY_NAME="$GITHUB_ORGANIZATION-$REPOSITORY"
-
Create a Microsoft Entra application (SPN) and connect it to GitHub:
CLIENT_ID=$(az ad app create --display-name $APP_REGISTRATION_DISPLAY_NAME --query appId --output tsv) OBJECT_ID=$(az ad sp create --id $CLIENT_ID --query id --output tsv) az role assignment create --assignee $OBJECT_ID --role "Owner" --scope "/subscriptions/$SUBSCRIPTION" az ad app federated-credential create --id $CLIENT_ID --parameters "{ \"name\": \"$GITHUB_ORGANIZATION-$REPOSITORY-Environment-Staging\", \"issuer\": \"https://token.actions.githubusercontent.com\", \"subject\": \"repo:$GITHUB_ORGANIZATION/$REPOSITORY:environment:Staging\", \"description\": \"Deploy to staging environment\", \"audiences\": [ \"api://AzureADTokenExchange\" ] }" az ad app federated-credential create --id $CLIENT_ID --parameters "{ \"name\": \"$GITHUB_ORGANIZATION-$REPOSITORY-Environment-Production\", \"description\": \"Deploy to production environment\", \"issuer\": \"https://token.actions.githubusercontent.com\", \"subject\": \"repo:$GITHUB_ORGANIZATION/$REPOSITORY:environment:Production\", \"audiences\": [ \"api://AzureADTokenExchange\" ] }"
-
Set repository secrets:
TENANT=$(az account show --query tenantId --output tsv) gh secret set AZURE_TENANT_ID --body "$TENANT" gh secret set AZURE_SUBSCRIPTION_ID --body "$SUBSCRIPTION" gh secret set AZURE_CLIENT_ID --body "$CLIENT_ID" gh secret set AZURE_CLIENT_DISPLAY_NAME --body "$APP_REGISTRATION_DISPLAY_NAME"
-
Create a Log Analytics Workspace and an App Service Plan if needed.
-
Update infrastructure/main.bicepparam.
-
Push the changes to trigger the infrastructure workflow.
-
Grant SPN access to PostgreSQL server:
DATABASE_SERVER=$(az postgres flexible-server list --resource-group $RESOURCE_GROUP --query [].name --output tsv) az postgres flexible-server microsoft-entra-admin create --display-name "$APP_REGISTRATION_DISPLAY_NAME" --object-id $OBJECT_ID --resource-group $RESOURCE_GROUP --server-name $DATABASE_SERVER --type ServicePrincipal
-
Grant web app permission to database:
WEB_APP=$(az webapp list --resource-group $RESOURCE_GROUP --query [].name --output tsv) SLOT="staging" DATABASE="Movies" STAGING_DATABASE="MoviesStaging" az extension add --name serviceconnector-passwordless az webapp connection create postgres-flexible --resource-group $RESOURCE_GROUP --name $WEB_APP --target-resource-group $RESOURCE_GROUP --server $DATABASE_SERVER --database $DATABASE --system-identity --client-type dotnet --connection $DATABASE --new --opt-out configinfo az webapp connection create postgres-flexible --resource-group $RESOURCE_GROUP --name $WEB_APP --slot $SLOT --target-resource-group $RESOURCE_GROUP --server $DATABASE_SERVER --database $STAGING_DATABASE --system-identity --client-type dotnet --connection $STAGING_DATABASE --new --opt-out configinfo
-
Run the application workflow from GitHub.
az group delete --name $RESOURCE_GROUP
az ad app delete --id $CLIENT_ID
To lint repository locally run (from WSL):
docker run -e DEFAULT_BRANCH=main -e RUN_LOCAL=true -e VALIDATE_GIT_COMMITLINT=false -e VALIDATE_JSCPD=false -e FIX_JSON=true -e FIX_JSON_PRETTIER=true -e FIX_JSONC=true -e FIX_JSONC_PRETTIER=true -e FIX_MARKDOWN=true -e FIX_MARKDOWN_PRETTIER=true -e FIX_YAML_PRETTIER=true -v .:/tmp/lint --rm ghcr.io/super-linter/super-linter:latest