Files
spicy-automation/resources
Ryan Wilson 21f4fef6a3 Fix cluster ASG signal failure, DNS duplication, type hacks, and health check default
- Remove duplicate shebang, set -e, and redundant SSM agent install from user data
  script so cfn-signal always runs (root cause of "0 SUCCESS signals" deploy failure)
- Remove DNS record creation from service stack's configureBlueGreenDns() to avoid
  CloudFormation conflicts with the persistent ALB stack that owns those records
- Replace readonly type assertion hacks with direct property assignments on 6 ALB/listener fields
- Change default health check path from /health to / for universal compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:18:18 -08:00
..

Spicy Automation

AWS CDK infrastructure and Jenkins shared library for Spicy automation. This replaces the legacy Ansible/CloudFormation approach with type-safe TypeScript and integrated Jenkins pipelines.

Quick Start

Minimal VPC Deployment

Create a Jenkinsfile in your VPC repository:

@Library(["spicy-automation@main"]) _

spicyVPC(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-vpc",
    ownerTag: "MyTeam",
    productTag: "myproduct",
    componentTag: "vpc",
)

Minimal ECS Cluster Deployment - Using CloudFormation Imports

Minimal props: Only vpcStackName and numberOfAzs required. VPC info auto-imports from VPC stack exports.

@Library(["spicy-automation@main"]) _

spicyECSCluster(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-ecs-cluster",
    vpcStackName: "my-vpc",  // Auto-imports VPC ID, CIDR, subnets, and AZs
    numberOfAzs: 3,
    ownerTag: "MyTeam",
    productTag: "myproduct",
    componentTag: "ecs-cluster",
    environment: "dev"
)

Explicit IDs (backward compatible):

spicyECSCluster(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-ecs-cluster",
    vpcId: "vpc-12345678",
    vpcCidrBlock: "10.0.0.0/16",
    availabilityZones: "ca-central-1a,ca-central-1b,ca-central-1c",
    privateSubnetIds: "subnet-aaa,subnet-bbb,subnet-ccc",
    ownerTag: "MyTeam",
    productTag: "myproduct",
    componentTag: "ecs-cluster",
    environment: "dev"
)

ECS Service with Mixed Capacity (EC2 + Fargate Burst) - Minimal Props

Minimal props: Only clusterName required. VPC info auto-imports from CloudFormation exports.

@Library(["spicy-automation@main"]) _

spicyECSService(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-api-dev",
    serviceName: "my-api",
    clusterName: "my-ecs-cluster",  // VPC stack name and VPC ID auto-import from cluster stack exports
    image: "nexus.kodeniks.com/docker-hosted/my-api:latest",
    containerPort: 3000,
    capacityProviderStrategy: [
        [capacityProvider: "my-ecs-cluster-ec2", base: 2, weight: 3],
        [capacityProvider: "FARGATE_SPOT", weight: 1],
    ],
    ownerTag: "MyTeam",
    productTag: "myproduct",
    componentTag: "api",
    environment: "dev"
)

Explicit IDs (backward compatible):

spicyECSService(
    // ... other params ...
    clusterName: "my-ecs-cluster",
    vpcId: "vpc-12345678",
    vpcCidrBlock: "10.0.0.0/16",
    availabilityZones: "ca-central-1a,ca-central-1b,ca-central-1c",
    privateSubnetIds: "subnet-aaa,subnet-bbb,subnet-ccc",
)

Blue/Green Deployment with Instant Rollback

@Library(["spicy-automation@main"]) _

spicyECSService(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-api-prod",
    serviceName: "my-api",
    clusterName: "my-ecs-cluster-prod",
    vpcId: "vpc-12345678",
    vpcCidrBlock: "10.0.0.0/16",
    availabilityZones: "ca-central-1a,ca-central-1b",
    privateSubnetIds: "subnet-aaa,subnet-bbb",
    image: "nexus.kodeniks.com/docker-hosted/my-api:latest",
    containerPort: 3000,

    // Enable Blue/Green
    blueGreen: true,
    activeHostname: "api.example.com",
    inactiveHostname: "inactive-api.example.com",

    // ALB routing - listener ARN auto-imports from ${clusterName}-internet-facing-https-listener
    priority: 100,
    useExternalALB: true,
    // externalListenerArn auto-imports when useExternalALB=true

    // Test before swap
    blueGreenTest: { args, buildInfo ->
        sh "curl -f https://${buildInfo.inactiveHostname}/health"
    },

    // Test after swap
    smokeTest: { args, buildInfo ->
        sh "curl -f https://${buildInfo.activeHostname}/health"
    },

    ownerTag: "Platform",
    productTag: "myproduct",
    componentTag: "api",
    environment: "prod"
)

Instant Rollback (30 seconds)

@Library(["spicy-automation@main"]) _

spicyRollback(
    jenkinsAwsCredentialsId: "aws-credentials",
    region: "ca-central-1",
    stackName: "my-api-prod",
    serviceName: "my-api",
    // ... same params as deploy ...
)

Documentation

Document Description
VPC Guide Complete VPC deployment guide with all options
ECS Cluster Guide ECS cluster with Spot, Capacity Providers
ECS Service Guide ECS service with mixed EC2/Fargate strategy
Persistent ALB Persistent ALB pattern (1:1 with service)
Cost Optimization Cost optimization for testing environments
CDK Synth Examples Complete CDK synth command examples
Jenkins Utilities All available Jenkins utility functions
Accounts Configuration Multi-account setup guide
Local Development Running CDK locally
NPM Example Example Jenkinsfile for NPM publishing

Features

Infrastructure (CDK)

  • SpicyVpc: Multi-AZ VPC with public/private subnets, NAT gateways, NACLs, VPC endpoints
  • SpicyEcsCluster: ECS cluster with EC2 Capacity Providers, Mixed Instances Policy for Spot, ALBs
  • SpicyEcsService: ECS service with mixed capacity strategy, auto-scaling, circuit breaker

Blue/Green Deployments

  • Zero-downtime releases with hostname-based routing
  • Instant rollback (~30 seconds) via hostname swap
  • Test before swap with blueGreenTest hook
  • Rollback window - old version kept for 2+ hours

Pipeline Hooks

  • buildCommand: Custom build logic
  • onPostBuild: Unit tests, linting, coverage
  • onPreDeploy: Setup, test preparation
  • blueGreenTest: Integration tests on inactive stack
  • onPostDeploy: Cleanup, notifications
  • smokeTest: Post-deployment verification

Jenkins Pipelines

  • spicyVPC: Deploy VPCs
  • spicyECSCluster: Deploy ECS clusters with Spot support
  • spicyECSService: Deploy ECS services with EC2 + Fargate burst, blue/green
  • spicyRollback: Instant rollback for blue/green deployments
  • buildAndPushDockerImage: Build and push Docker images to Nexus

Jenkins Setup

1. Configure Global Library

In Jenkins → Manage JenkinsConfigure SystemGlobal Pipeline Libraries:

Setting Value
Name spicy-automation
Default version main
Retrieval method Modern SCM → Git
Project Repository git@git.kodeniks.com:CORP/spicy-automation.git
Library Path jenkins/

2. Configure Credentials

Credential ID Type Description
aws-credentials Username/Password AWS Access Key ID / Secret
kodeniks-gitea-token Secret text Gitea personal access token
kodeniks-nexus-repository Username/Password Nexus Docker registry credentials

Project Structure

spicy-automation/
├── bin/spicy-cdk.ts              # CDK app entry point
├── lib/
│   ├── constructs/               # Reusable CDK constructs
│   │   ├── spicy-vpc.ts          # VPC construct
│   │   ├── spicy-ecs-cluster.ts  # ECS cluster construct
│   │   └── spicy-ecs-service.ts  # ECS service construct
│   └── stacks/                   # CDK stacks
│       ├── spicy-vpc-stack.ts
│       ├── spicy-ecs-cluster-stack.ts
│       └── spicy-ecs-service-stack.ts
├── vars/                         # Jenkins shared library
│   ├── spicyVPC.groovy           # VPC pipeline
│   ├── spicyECSCluster.groovy    # ECS cluster pipeline
│   ├── spicyECSService.groovy    # ECS service pipeline
│   ├── buildAndPushDockerImage.groovy  # Docker build pipeline
│   ├── cdkUtils.groovy           # CDK commands
│   ├── dockerUtils.groovy        # Docker/Nexus utilities
│   ├── gitUtils.groovy           # Git utilities
│   ├── giteaUtils.groovy         # Commit status updates
│   └── accounts.groovy           # Account configurations
├── docs/                         # Documentation
├── test/                         # Jest tests
└── Dockerfile                    # Jenkins agent image

Useful Commands

# No build step required - runs directly with ts-node!
npx cdk synth -c stackType=vpc -c stackName=my-vpc ...

# Or use pnpm scripts
pnpm run test     # Run Jest tests
pnpm run build    # Compile TypeScript (optional)

Migration from spicy-automation-legacy

Feature spicy-automation-legacy spicy-automation
Infrastructure CloudFormation YAML TypeScript CDK
Provisioning Ansible CDK CLI
Testing None Jest
Type Safety None Full TypeScript
Docker Registry AWS ECR Nexus

CloudFormation outputs are identical, so existing stacks referencing VPC exports continue to work.