Running Concourse CI/CD on Windows Docker is uncommon enough that the setup patterns deserve their own reference. The Terraform provider, key generation, and security hardening steps differ significantly from Linux-based deployments.
For a complete working example, see the concourse-terraform-windows directory in the IaC repository.
Docker Transport
Windows Docker uses named pipes instead of Unix sockets:
provider "docker" {
host = "npipe:////./pipe/docker_engine"
}
This is the first thing to verify when a Terraform Docker provider fails on Windows. The connection string is different, and not all provider features work identically on the Windows engine.
Key Generation Without ssh-keygen
On a Windows host, ssh-keygen may not be available. Terraform’s tls_private_key resource generates the key natively:
resource "tls_private_key" "worker_key" {
algorithm = "RSA"
rsa_bits = 4096
}
The output is PEM format. Concourse needs SSH public key format for authorized_worker_keys. A PowerShell script bridges the gap using a Terraform external data source:
# convert_to_ssh_format.ps1
$tempFile = [System.IO.Path]::GetTempFileName()
$pemContent = $env:pem_private_key
Set-Content -Path $tempFile -Value $pemContent
$sshPublicKey = ssh-keygen -y -f $tempFile 2>&1
Remove-Item $tempFile
return ($sshPublicKey | ConvertTo-Json)
data "external" "worker_public_key_ssh" {
program = ["pwsh", "./scripts/convert_to_ssh_format.ps1"]
query = {
pem_private_key = tls_private_key.worker_key.private_key_pem
}
}
Permission Hardening
Windows file permissions are not restrictive enough by default for private keys:
# set_permissions.ps1
icacls $keyPath /inheritance:r
icacls $keyPath /grant "${env:USERNAME}:(R,D)"
The inheritance:r flag removes all inherited permissions. The (R,D) grant gives the current user read and delete access only. Without this, the Concourse process or other users on the machine could read the key material.
Architecture
The Terraform config provisions three containers:
concourse-db (postgres:13)
-> concourse-web (custom image, port 8080)
-> concourse-worker (privileged, TSA to web:2222)
The web container entrypoint uses dumb-init for proper PID 1 handling, which is especially important on Windows containers where signal propagation can be unpredictable.
Production Pipeline Example
The repository includes three pipeline YAMLs. The full pipeline (pipeline_full_stubbed.yaml) demonstrates a production-grade Concourse pipeline for what appears to be a Cadence API service:
- Resources: Git, ECR, semver, pull request.
- Jobs: unit test (with race detection), lint, build, integration, deploy staging, deploy production.
- Patterns: parallel test execution, semantic versioning with
semverresource, PR status checks, Golang race detection.
Acceptance Criteria
- Terraform applies on Windows Docker without Linux compatibility layer.
- Keys are generated by
tls_private_keywithoutssh-keygen. - Private key files have Windows permissions restricting access to the service account.
- Workers authenticate to the web TSA using the generated keys.
- Pipeline YAMLs can be loaded and triggered.
- Cleanup removes containers, keys, and local state.