# 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**: ```json { "ConnectionStrings": { "DefaultConnection": "Server=.\\SQLEXPRESS;Database=PowderCoatingDb;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=true" } } ``` **Production** (Windows Server with SQL Server): ```bash # Environment Variable set ConnectionStrings__DefaultConnection="Server=PROD_SERVER;Database=PowderCoatingDb;User Id=app_user;Password=SecurePassword123!;MultipleActiveResultSets=true;Encrypt=true" ``` **Production** (Linux/Docker): ```bash 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**: ```json { "JwtSettings": { "SecretKey": "DEV-ONLY-SecretKey-MinimumLength32CharactersRequired!@#$", "Issuer": "PowderCoatingAPI", "Audience": "PowderCoatingMobileApp", "ExpirationMinutes": 15, "RefreshTokenExpirationDays": 7 } } ``` **Production**: Generate a strong secret key (minimum 32 characters): ```powershell # PowerShell - Generate random secret -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | ForEach-Object {[char]$_}) ``` ```bash # Linux - Generate random secret openssl rand -base64 64 ``` Set as environment variable: ```bash # 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**: ```json { "CorsSettings": { "AllowedOrigins": [ "http://localhost:3000", "http://localhost:5173", "http://localhost:58461", "https://localhost:58461" ] } } ``` **Production**: ```json { "CorsSettings": { "AllowedOrigins": [ "https://yourapp.com", "https://www.yourapp.com", "https://app.yourdomain.com" ] } } ``` ⚠️ **NEVER use `*` (wildcard) in production!** --- ### 4. Allowed Hosts **Development**: ```json { "AllowedHosts": "localhost;127.0.0.1" } ``` **Production**: ```json { "AllowedHosts": "yourapp.com;www.yourapp.com;app.yourdomain.com" } ``` --- ### 5. Logging Configuration **Development** (detailed logging): ```json { "Logging": { "LogLevel": { "Default": "Debug", "Microsoft.AspNetCore": "Information", "Microsoft.EntityFrameworkCore": "Information" } } } ``` **Production** (reduced logging): ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Warning" } } } ``` --- ## Security Checklist ### ✅ Development Environment - [x] Use `appsettings.Development.json` for local development - [x] Development secrets can be simple (e.g., "Dev123!") - [x] CORS can allow `localhost` origins - [x] Detailed logging enabled for debugging - [x] 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**: ```powershell # 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**: ```bash # Add to ~/.bashrc or /etc/environment export ConnectionStrings__DefaultConnection="Server=...;Database=...;User Id=...;Password=..." export JwtSettings__SecretKey="YourSecretKey" ``` **Docker**: ```yaml # 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**: ```bash 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**: ```bash 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**: 1. Create Azure Key Vault 2. Add secrets to Key Vault: - `ConnectionStrings--DefaultConnection` - `JwtSettings--SecretKey` 3. Grant App Service Managed Identity access to Key Vault **Code** (add to `Program.cs`): ```csharp 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 1. Clone repository 2. Ensure `ASPNETCORE_ENVIRONMENT=Development` 3. Verify `appsettings.Development.json` has correct local SQL Server connection 4. Run migrations: ```bash cd src/PowderCoating.Web dotnet ef database update --project ../PowderCoating.Infrastructure ``` 5. Run application: ```bash dotnet run ``` --- ### Production Deployment (Windows Server/IIS) 1. **Publish Application**: ```bash cd src/PowderCoating.Web dotnet publish -c Release -o C:\Publish\PowderCoatingApp ``` 2. **Configure Environment Variables** (IIS): - Open IIS Manager - Select Application Pool > Advanced Settings - Under Environment Variables, add: - `ASPNETCORE_ENVIRONMENT = Production` - `ConnectionStrings__DefaultConnection = ` 3. **Install SQL Server Database**: ```bash # Run migrations on production database dotnet ef database update --project ../PowderCoating.Infrastructure --configuration Release ``` 4. **Configure IIS**: - Enable HTTPS binding with valid SSL certificate - Set Application Pool to "No Managed Code" - Install ASP.NET Core Hosting Bundle 5. **Security Hardening**: - Disable directory browsing - Remove unnecessary HTTP headers - Configure IP restrictions if needed - Enable request filtering --- ### Production Deployment (Azure App Service) 1. **Create Azure Resources**: - Azure SQL Database - Azure App Service (Windows or Linux) - Azure Key Vault (optional, but recommended) 2. **Configure App Service**: - **Configuration > General Settings**: - Stack: .NET 8 - Platform: 64-bit - HTTPS Only: On - **Configuration > Application Settings**: ``` ASPNETCORE_ENVIRONMENT = Production JwtSettings__SecretKey = 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 ``` 3. **Deploy Code**: ```bash # Via Azure CLI az webapp deployment source config-zip --resource-group YourRG --name YourAppName --src publish.zip ``` 4. **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 clickjacking - `X-Content-Type-Options: nosniff` - Prevent MIME sniffing - `X-XSS-Protection: 1; mode=block` - XSS protection - `Strict-Transport-Security: max-age=31536000; includeSubDomains` - Force HTTPS - `Content-Security-Policy` - Restrict resource loading - `Referrer-Policy: strict-origin-when-cross-origin` - Control referrer info - `Permissions-Policy` - Disable unnecessary browser features --- ## Troubleshooting ### Issue: "Connection string not found" **Solution**: Ensure environment variable is set with double underscores (`__`): ```bash 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.Always` is compatible with your hosting - For development over HTTP, set `SecurePolicy = CookieSecurePolicy.SameAsRequest` --- ## Monitoring and Logs **Development**: - Console output - Files in `/logs/` directory - `powdercoating-{date}.txt` - All logs - `errors-{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: 1. This guide 2. Application logs (Diagnostics > View Logs in the app) 3. Environment-specific appsettings files 4. Environment variables are correctly set **Security Tip**: Regularly rotate secrets (JWT keys, database passwords) every 90 days in production.