13 KiB
Deployment Configuration Guide
This guide explains how to configure the Powder Coating App for different environments (Development, Staging, Production).
Overview
The application uses environment-specific configuration files and supports multiple secure configuration methods:
- Development:
appsettings.Development.json(included in repo) - Production: Environment variables, User Secrets, or Azure Key Vault
Configuration Files
Development Environment
File: src/PowderCoating.Web/appsettings.Development.json
- Contains actual development values
- Safe to commit to source control (dev-only credentials)
- Used automatically when
ASPNETCORE_ENVIRONMENT=Development
File: src/PowderCoating.Api/appsettings.Development.json
- API-specific development settings
- Contains dev JWT secret and CORS origins
Production Environment
File: src/PowderCoating.Web/appsettings.json
- Contains placeholders only (
USE_USER_SECRETS_OR_ENVIRONMENT_VARIABLE) - NEVER contains actual secrets
- Safe to commit to source control
File: src/PowderCoating.Api/appsettings.json
- Same approach - placeholders only
- Production secrets configured via environment variables or Key Vault
Required Configuration Values
1. Database Connection String
Development:
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=PowderCoatingDb;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=true"
}
}
Production (Windows Server with SQL Server):
# Environment Variable
set ConnectionStrings__DefaultConnection="Server=PROD_SERVER;Database=PowderCoatingDb;User Id=app_user;Password=SecurePassword123!;MultipleActiveResultSets=true;Encrypt=true"
Production (Linux/Docker):
export ConnectionStrings__DefaultConnection="Server=PROD_SERVER;Database=PowderCoatingDb;User Id=app_user;Password=SecurePassword123!;MultipleActiveResultSets=true;Encrypt=true"
Production (Azure Web App):
- Navigate to: Configuration > Connection strings
- Name:
DefaultConnection - Value: Your production connection string
- Type:
SQLServer
2. JWT Settings (API Only)
Development:
{
"JwtSettings": {
"SecretKey": "DEV-ONLY-SecretKey-MinimumLength32CharactersRequired!@#$",
"Issuer": "PowderCoatingAPI",
"Audience": "PowderCoatingMobileApp",
"ExpirationMinutes": 15,
"RefreshTokenExpirationDays": 7
}
}
Production:
Generate a strong secret key (minimum 32 characters):
# PowerShell - Generate random secret
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | ForEach-Object {[char]$_})
# Linux - Generate random secret
openssl rand -base64 64
Set as environment variable:
# Windows
set JwtSettings__SecretKey="YOUR_GENERATED_SECRET_HERE"
# Linux
export JwtSettings__SecretKey="YOUR_GENERATED_SECRET_HERE"
# Azure Web App - Application Settings
JwtSettings__SecretKey = YOUR_GENERATED_SECRET_HERE
3. CORS Origins (API Only)
Development:
{
"CorsSettings": {
"AllowedOrigins": [
"http://localhost:3000",
"http://localhost:5173",
"http://localhost:58461",
"https://localhost:58461"
]
}
}
Production:
{
"CorsSettings": {
"AllowedOrigins": [
"https://yourapp.com",
"https://www.yourapp.com",
"https://app.yourdomain.com"
]
}
}
⚠️ NEVER use * (wildcard) in production!
4. Allowed Hosts
Development:
{
"AllowedHosts": "localhost;127.0.0.1"
}
Production:
{
"AllowedHosts": "yourapp.com;www.yourapp.com;app.yourdomain.com"
}
5. Logging Configuration
Development (detailed logging):
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
}
}
Production (reduced logging):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
Security Checklist
✅ Development Environment
- Use
appsettings.Development.jsonfor local development - Development secrets can be simple (e.g., "Dev123!")
- CORS can allow
localhostorigins - Detailed logging enabled for debugging
- Database uses Trusted Connection (Windows Auth) or simple password
✅ Production Environment
- CRITICAL: Never use development secrets in production
- CRITICAL: Use strong, randomly generated secrets (64+ characters)
- CRITICAL: Store secrets in environment variables, User Secrets, or Azure Key Vault
- CRITICAL: Enable HTTPS with valid SSL certificate
- CRITICAL: Configure CORS with specific allowed origins (NO wildcards)
- Configure AllowedHosts with actual production domain(s)
- Use encrypted database connection with strong password
- Reduce logging verbosity (Warning/Error only)
- Set
ASPNETCORE_ENVIRONMENT=Production - Enable HSTS (Strict-Transport-Security headers)
- Configure rate limiting and DDoS protection
- Set up monitoring and alerting
- Regular security patches and updates
Configuration Methods by Environment
Method 1: Environment Variables (Recommended for Production)
Advantages:
- Secure (not stored in files)
- Easy to manage in cloud platforms
- Platform-agnostic
Windows:
# Set for current session
$env:ConnectionStrings__DefaultConnection="Server=...;Database=...;User Id=...;Password=..."
$env:JwtSettings__SecretKey="YourSecretKey"
# Set permanently (System-wide)
[Environment]::SetEnvironmentVariable("ConnectionStrings__DefaultConnection", "Server=...;", "Machine")
Linux:
# Add to ~/.bashrc or /etc/environment
export ConnectionStrings__DefaultConnection="Server=...;Database=...;User Id=...;Password=..."
export JwtSettings__SecretKey="YourSecretKey"
Docker:
# docker-compose.yml
services:
powdercoating-web:
environment:
- ConnectionStrings__DefaultConnection=Server=db;Database=PowderCoatingDb;User Id=sa;Password=YourPassword
- ASPNETCORE_ENVIRONMENT=Production
Method 2: User Secrets (Development Only)
Setup:
cd src/PowderCoating.Web
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=...;Database=...;"
dotnet user-secrets set "JwtSettings:SecretKey" "YourSecretKey"
View secrets:
dotnet user-secrets list
⚠️ User Secrets are for development only - they are stored locally on your machine and won't deploy to production.
Method 3: Azure Key Vault (Production)
Setup:
- Create Azure Key Vault
- Add secrets to Key Vault:
ConnectionStrings--DefaultConnectionJwtSettings--SecretKey
- Grant App Service Managed Identity access to Key Vault
Code (add to Program.cs):
if (builder.Environment.IsProduction())
{
var keyVaultUri = new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/");
builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}
Deployment Steps
Local Development
- Clone repository
- Ensure
ASPNETCORE_ENVIRONMENT=Development - Verify
appsettings.Development.jsonhas correct local SQL Server connection - Run migrations:
cd src/PowderCoating.Web dotnet ef database update --project ../PowderCoating.Infrastructure - Run application:
dotnet run
Production Deployment (Windows Server/IIS)
-
Publish Application:
cd src/PowderCoating.Web dotnet publish -c Release -o C:\Publish\PowderCoatingApp -
Configure Environment Variables (IIS):
- Open IIS Manager
- Select Application Pool > Advanced Settings
- Under Environment Variables, add:
ASPNETCORE_ENVIRONMENT = ProductionConnectionStrings__DefaultConnection = <your connection string>
-
Install SQL Server Database:
# Run migrations on production database dotnet ef database update --project ../PowderCoating.Infrastructure --configuration Release -
Configure IIS:
- Enable HTTPS binding with valid SSL certificate
- Set Application Pool to "No Managed Code"
- Install ASP.NET Core Hosting Bundle
-
Security Hardening:
- Disable directory browsing
- Remove unnecessary HTTP headers
- Configure IP restrictions if needed
- Enable request filtering
Production Deployment (Azure App Service)
-
Create Azure Resources:
- Azure SQL Database
- Azure App Service (Windows or Linux)
- Azure Key Vault (optional, but recommended)
-
Configure App Service:
-
Configuration > General Settings:
- Stack: .NET 8
- Platform: 64-bit
- HTTPS Only: On
-
Configuration > Application Settings:
ASPNETCORE_ENVIRONMENT = Production JwtSettings__SecretKey = <your secret> JwtSettings__ExpirationMinutes = 15 CorsSettings__AllowedOrigins__0 = https://yourapp.com AllowedHosts = yourapp.com;www.yourapp.com -
Configuration > Connection Strings:
Name: DefaultConnection Value: Server=tcp:yourserver.database.windows.net,1433;Database=PowderCoatingDb;User ID=sqladmin;Password=...;Encrypt=True; Type: SQLServer
-
-
Deploy Code:
# Via Azure CLI az webapp deployment source config-zip --resource-group YourRG --name YourAppName --src publish.zip -
Run Database Migrations:
- Option 1: Use Azure Cloud Shell or local machine with connection to Azure SQL
- Option 2: Enable migrations on startup (NOT recommended for production)
Password Requirements
Development: Minimum 8 characters
Production (enforced by application):
- Minimum 12 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 digit
- At least 1 special character (!@#$%^&*)
- At least 4 unique characters
- Account lockout after 5 failed attempts (15-minute lockout)
Security Headers (Automatically Applied)
The application automatically adds these security headers in production:
X-Frame-Options: DENY- Prevent clickjackingX-Content-Type-Options: nosniff- Prevent MIME sniffingX-XSS-Protection: 1; mode=block- XSS protectionStrict-Transport-Security: max-age=31536000; includeSubDomains- Force HTTPSContent-Security-Policy- Restrict resource loadingReferrer-Policy: strict-origin-when-cross-origin- Control referrer infoPermissions-Policy- Disable unnecessary browser features
Troubleshooting
Issue: "Connection string not found"
Solution: Ensure environment variable is set with double underscores (__):
ConnectionStrings__DefaultConnection # Correct
ConnectionStrings:DefaultConnection # Wrong (this is for User Secrets only)
Issue: "JWT secret key is too short"
Solution: Generate a new secret with minimum 32 characters (64+ recommended)
Issue: "CORS policy blocked"
Solution: Add your frontend domain to CorsSettings:AllowedOrigins array
Issue: "Cookies not working"
Solution:
- Ensure HTTPS is enabled in production
- Check that
CookieSecurePolicy.Alwaysis compatible with your hosting - For development over HTTP, set
SecurePolicy = CookieSecurePolicy.SameAsRequest
Monitoring and Logs
Development:
- Console output
- Files in
/logs/directorypowdercoating-{date}.txt- All logserrors-{date}.txt- Errors only
Production:
- Configure Azure Application Insights (recommended)
- Or use Serilog sinks (Seq, Elasticsearch, etc.)
- Set up alerts for errors and performance issues
Summary
| Configuration | Development | Production |
|---|---|---|
| Secrets | appsettings.Development.json | Environment Variables / Key Vault |
| HTTPS | Optional | Required |
| CORS | localhost:* | Specific domains only |
| Logging | Debug/Information | Warning/Error |
| AllowedHosts | localhost;127.0.0.1 | yourdomain.com |
| JWT Expiration | 15 minutes | 15 minutes |
| Password Policy | Strong (12 chars) | Strong (12 chars) |
| Session Cookies | Secure, HttpOnly, SameSite=Strict | Same + SecurePolicy=Always |
Questions?
If you encounter any issues with deployment or configuration, check:
- This guide
- Application logs (Diagnostics > View Logs in the app)
- Environment-specific appsettings files
- Environment variables are correctly set
Security Tip: Regularly rotate secrets (JWT keys, database passwords) every 90 days in production.