Skip to content
Open
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
97 changes: 77 additions & 20 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ This is an ASP.NET Core 8.0 MVC web application deployed to Azure Container Apps
- **Application**: ASP.NET Core 8.0 MVC (C#)
- **Container Runtime**: Docker
- **Cloud Platform**: Azure Container Apps
- **Testing**: NUnit with Selenium WebDriver (Chrome)
- **Testing**: NUnit with Playwright (Chromium)
- **Infrastructure as Code**: Terraform
- **CI/CD**: Azure DevOps Pipelines

## Common Commands
Expand Down Expand Up @@ -69,14 +70,20 @@ HelloWorld/ # Main ASP.NET Core MVC application
├── Dockerfile # Multi-stage build for container
└── HelloWorld.csproj # Project file

HelloWorld.IntegrationTests/ # Selenium UI tests
HelloWorld.IntegrationTests/ # Playwright UI tests
└── HomeTest.cs # Page object pattern tests

Infrastructure/
└── Pipelines/ # Azure DevOps pipeline definitions
├── helloworld-dev-pipeline.yml # Build, deploy to Container Apps, test, teardown
├── container-app.bicep # Bicep template for Container App
└── reset-test.yml # Pipeline reset pattern
├── Pipelines/ # Azure DevOps pipeline definitions
│ ├── helloworld-dev-pipeline.yml # Build, deploy to Container Apps, test, teardown
│ ├── container-app.bicep # Legacy Bicep template (not used)
│ └── reset-test.yml # Pipeline reset pattern
└── Terraform/ # Terraform infrastructure as code
├── main.tf # Container App resource definition
├── variables.tf # Input variables
├── providers.tf # Azure provider configuration
├── outputs.tf # Deployment outputs
└── README.md # Terraform documentation
```

### Application Flow
Expand All @@ -90,32 +97,40 @@ Infrastructure/

1. **Docker Build**: Multi-stage Dockerfile builds application in SDK container, publishes to runtime container
2. **Container Registry**: Image pushed to Azure Container Registry
3. **Container App**: Deployed using Bicep template with ingress configuration
4. **Testing**: Integration tests run against deployed Container App
5. **Teardown**: Optional cleanup of Container App resources
3. **Terraform Plan**: Terraform generates execution plan with unique state file per build
4. **Terraform Apply**: Container App deployed to Azure with ingress configuration
5. **Testing**: Playwright integration tests run against deployed Container App
6. **Teardown**: Optional `terraform destroy` cleanup of Container App resources

## Azure DevOps Pipeline

[Infrastructure/Pipelines/helloworld-dev-pipeline.yml](Infrastructure/Pipelines/helloworld-dev-pipeline.yml) defines the Azure Container Apps deployment pipeline:

1. **Build Stage**: Builds and pushes Docker image to Azure Container Registry
2. **Deploy Stage**: Creates Azure Container App with ingress using Bicep template
3. **Test Stage**: Runs integration tests against deployed Container App URL
4. **Teardown Stage**: Optionally deletes Container App (controlled by `teardown` parameter)
1. **Build Stage**: Builds and pushes Docker image to Azure Container Registry (tagged with Build ID)
2. **TerraformPlan Stage**: Runs `terraform plan` with unique state file per build
3. **Deploy Stage**: Runs `terraform apply`, configures CAE routing, retrieves Container App URL
4. **Test Stage**: Runs Playwright integration tests against deployed Container App URL
5. **Teardown Stage**: Optionally runs `terraform destroy` (controlled by `teardown` parameter, default: true)

**Required Pipeline Variables:**
- `ContainerRegistrySC`: Service connection for ACR
- `AzureResourceManagerSC`: Azure subscription service connection
- `ContainerAppEnvironment`: Container App Environment ID
- `ContainerRegistryLoginServer`, `ContainerRegistryUsername`, `ContainerRegistryPassword`
- `LogAnalyticsWorkspace`, `LogAnalyticsResourceGroup`
- `ContainerRegistrySC`: Service connection for Azure Container Registry
- `AzureResourceManagerSC`: Azure subscription service connection with Terraform backend access
- `ContainerRegistryLoginServer`: ACR URL (e.g., `ncontracts.azurecr.io`)
- `ManagedIdentityName`: User-assigned managed identity for ACR authentication
- `EnvironmentName`: Environment name for resource naming (e.g., `poc01`, `dev`)

**Pipeline Features:**
- Unique state file per build: `helloworld-{env}-{buildId}.tfstate`
- Variables passed to Terraform via `--var` flags (no manual .tf file edits needed)
- Automatic URL retrieval and injection into test stage
- Conditional teardown based on parameter

## Integration Testing

Tests in [HelloWorld.IntegrationTests/HomeTest.cs](HelloWorld.IntegrationTests/HomeTest.cs) use:

- **Framework**: NUnit
- **Browser Automation**: Selenium WebDriver with ChromeDriver
- **Browser Automation**: Microsoft Playwright with Chromium
- **Pattern**: Page Object pattern (HelloWorldHome class)
- **Configuration**: `HomePageUrl` environment variable is automatically set in Azure DevOps pipeline to the deployed Container App URL

Expand All @@ -124,17 +139,59 @@ Test scenarios:
- Navigate to Privacy page via nav link
- Navigate back to Home via brand link

**Running Locally:**
```bash
# Set the URL to test against
$env:HomePageUrl = "https://your-container-app-url.azurecontainerapps.io"

# Build and run tests
dotnet build HelloWorld.IntegrationTests/HelloWorld.IntegrationTests.csproj --configuration Debug
pwsh HelloWorld.IntegrationTests/bin/Debug/net8.0/playwright.ps1 install chromium
dotnet test HelloWorld.IntegrationTests/HelloWorld.IntegrationTests.csproj --configuration Debug
```

## Key Dependencies

- Azure subscription with Container Apps resources
- Azure Container Registry
- .NET 8.0 SDK
- Docker
- Chrome browser (for integration tests)
- Terraform ~> 1.0
- Playwright with Chromium (for integration tests)

## Terraform Infrastructure

The Terraform configuration in [Infrastructure/Terraform/](Infrastructure/Terraform/) is designed as a simple, developer-friendly example:

**Structure:**
- **main.tf** (66 lines): Container App resource with data sources for existing infrastructure
- **variables.tf**: Well-documented input variables with sensible defaults
- **providers.tf**: Azure provider and remote state backend configuration
- **outputs.tf**: Exposes Container App URL and metadata for pipeline consumption
- **README.md**: Comprehensive documentation for developers

**Key Features:**
- No custom modules - everything is self-contained and readable
- References existing infrastructure (Resource Group, Container App Environment, Managed Identity)
- Managed identity authentication to Azure Container Registry (no passwords in state)
- Auto-scaling configuration (1-10 replicas)
- External HTTPS ingress with auto-provisioned SSL certificates

**Local Usage:**
```bash
cd Infrastructure/Terraform
terraform init
terraform plan
terraform apply
terraform output container_app_url
```

See [Infrastructure/Terraform/README.md](Infrastructure/Terraform/README.md) for detailed instructions.

## Important Notes

- Container Apps automatically handle SSL/TLS certificates and ingress
- Integration tests retrieve the Container App URL dynamically from the deployment
- Teardown stage can be controlled via pipeline parameter to preserve or delete resources
- Application uses MVC with runtime Razor compilation enabled for development
- Terraform state files are stored per-build in Azure Blob Storage with unique keys
6 changes: 5 additions & 1 deletion Infrastructure/Pipelines/helloworld-dev-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ trigger: none
# - 'HelloWorld.IntegrationTests/*'

# Ensure only one run of this pipeline executes at a time
lockBehavior: sequential
# lockBehavior: sequential

resources:
repositories:
Expand Down Expand Up @@ -87,6 +87,8 @@ stages:
workingDirectory: '$(Pipeline.Workspace)/s/helloworld/Infrastructure/Terraform'
inlineScript: |
# Define a unique state file key for this build
# NOTE: Most applications use a single shared state file per environment (e.g., "helloworld-poc01.tfstate")
# This project uses per-build state files to enable parallel testing and automatic teardown
$STATE_FILE_KEY = "helloworld-$(EnvironmentName)-$(Build.BuildId).tfstate"
Write-Host "Using state file: $STATE_FILE_KEY"

Expand Down Expand Up @@ -284,6 +286,8 @@ stages:
workingDirectory: '$(Pipeline.Workspace)/s/helloworld/Infrastructure/Terraform'
inlineScript: |
# Define a unique state file key for this build
# NOTE: Most applications use a single shared state file per environment (e.g., "helloworld-poc01.tfstate")
# This project uses per-build state files to enable parallel testing and automatic teardown
$STATE_FILE_KEY = "helloworld-$(EnvironmentName)-$(Build.BuildId).tfstate"
Write-Host "Using state file: $STATE_FILE_KEY"

Expand Down
215 changes: 0 additions & 215 deletions Infrastructure/Terraform/AGENTS.md

This file was deleted.

Loading