Skip to content

Shared Templates Refactoring Plan

Identified ~1,000 lines of duplication across mpac-smartpos/ecs-stack.yaml and mpac-pgw/ecs-stack.yaml (and partially mpac-obs/). Extracting shared nested stacks would reduce maintenance burden — e.g., the PG version upgrade and DependsOn cleanup required touching identical code in multiple files.

1. VPC Stack (~180 lines saved)

File: shared/vpc-stack.yaml

Duplicated resources (18 total):

  • VPC, InternetGateway, InternetGatewayAttachment
  • PublicSubnetA/B, PrivateSubnetA/B, DatabaseSubnetA/B
  • NatGatewayEIP, NatGateway
  • PublicRouteTable, PublicRoute, 2 public route table associations
  • PrivateRouteTable, PrivateRoute, 2 private route table associations
  • DatabaseRouteTable, 2 database route table associations

Parameters: SystemName, Environment, VpcCidr, subnet CIDR mapping

Outputs: VpcId, PublicSubnetIds, PrivateSubnetIds, DatabaseSubnetIds

Used by: mpac-smartpos, mpac-pgw


2. Bastion Host Stack (~140 lines saved)

File: shared/bastion-stack.yaml

Duplicated resources:

  • BastionSecurityGroup
  • BastionKeyPair
  • BastionInstance (t3.micro, AMI via SSM dynamic reference)
  • BastionEIP
  • RDSBastionIngress
  • RedisBastionIngress

Parameters: SystemName, Environment, VpcId, PublicSubnetId, RDSSecurityGroupId, RedisSecurityGroupId, AllowedIP

Used by: mpac-smartpos, mpac-pgw


3. Security Groups Stack (~140 lines saved)

File: shared/security-groups-stack.yaml

Duplicated resources:

  • ALBSecurityGroup (HTTP/HTTPS ingress)
  • ECSSecurityGroup (ingress from ALB)
  • RDSSecurityGroup (PostgreSQL 5432 from ECS)
  • RedisSecurityGroup (Redis 6379 from ECS)

Parameters: SystemName, Environment, VpcId, ECSIngressPorts (list — smartpos uses 8000+8080, pgw uses 8080 only)

Used by: mpac-smartpos, mpac-pgw


4. RDS PostgreSQL Stack (~100 lines saved)

File: shared/rds-postgres-stack.yaml

Duplicated resources:

  • DBSubnetGroup
  • DBParameterGroup
  • RDSInstance (gp3, encrypted, backup 7 days)
  • DBSecret
  • DBSecretAttachment

Parameters: SystemName, Environment, DBName, PostgresFamily (e.g. postgres17), EngineVersion, InstanceClass, AllocatedStorage, DatabaseSubnetIds, SecurityGroupId

Outputs: DBEndpoint, DBSecretArn

Used by: mpac-smartpos, mpac-pgw


5. ElastiCache Redis Stack (~80 lines saved)

File: shared/redis-stack.yaml

Duplicated resources:

  • RedisSubnetGroup
  • RedisParameterGroup
  • RedisCluster (engine 7.0, single-node)
  • RedisSecret

Parameters: SystemName, Environment, DatabaseSubnetIds, SecurityGroupId

Outputs: RedisEndpoint, RedisPort, RedisSecretArn

Used by: mpac-smartpos, mpac-pgw


6. ECS IAM Roles (~120 lines saved)

File: shared/ecs-iam-roles-stack.yaml

Duplicated resources:

  • ECSTaskExecutionRole (ecs-tasks.amazonaws.com, AmazonECSTaskExecutionRolePolicy, secrets/logs access)
  • Base ECSTaskRole (ecs-tasks.amazonaws.com, ssmmessages for ECS Exec)

Parameters: SystemName, Environment, SecretArns (list of secrets the execution role can read)

Outputs: ExecutionRoleArn, TaskRoleArn

Used by: mpac-smartpos, mpac-pgw, mpac-obs/ecs-stack, mpac-obs/observability-stack

Note: Task roles have service-specific policies (IoT, S3, etc.) — those remain in the parent stack and attach to the role via inline policies or managed policy ARNs.


7. ECR Repository (~25 lines saved per repo)

File: shared/ecr-repo-stack.yaml

Duplicated pattern: ECR repository with "keep last 10 images" lifecycle policy. Used 3 times (1 in mpac-pgw, 2 in mpac-smartpos).

Parameters: RepositoryName


8. Alloy Config Dedup in mpac-obs (~80 lines saved)

mpac-obs/ecs-stack.yaml and mpac-obs/observability-stack.yaml contain nearly identical inline Alloy (OTLP collector) configurations. The only difference is bearer token auth in ecs-stack.

Approach: Extract to an S3-hosted config file with environment variable substitution, or use a shared config snippet referenced by both templates.


9. mpac-obs Template Consolidation (~200+ lines saved)

mpac-obs/ecs-stack.yaml and mpac-obs/observability-stack.yaml duplicate:

  • ObservabilityCluster
  • ECSTaskExecutionRole / ECSTaskRole
  • GrafanaLogGroup / AlloyLogGroup
  • ServiceDiscoveryNamespace / AlloyServiceDiscovery
  • GrafanaALB, GrafanaTargetGroup, GrafanaListener
  • GrafanaTaskDefinition, GrafanaService, AlloyTaskDefinition, AlloyService

These appear to be alternative deployment modes (EC2-hybrid vs pure-Fargate). Consider sharing common resources via cross-stack references or a base nested stack.


Summary

Shared ModuleSavesUsed By
shared/vpc-stack.yaml~180 linessmartpos, pgw
shared/bastion-stack.yaml~140 linessmartpos, pgw
shared/security-groups-stack.yaml~140 linessmartpos, pgw
shared/rds-postgres-stack.yaml~100 linessmartpos, pgw
shared/redis-stack.yaml~80 linessmartpos, pgw
shared/ecs-iam-roles-stack.yaml~120 linessmartpos, pgw, obs x2
shared/ecr-repo-stack.yaml~75 linessmartpos, pgw
Alloy config dedup~80 linesobs x2
obs template consolidation~200+ linesobs x2
Total~1,100+ lines

Migration Strategy

Follow the two-phase migration convention:

  1. Phase 1: Create shared nested stacks, update one system (e.g. mpac-pgw) to use them. Validate in dev.
  2. Phase 2: Migrate remaining systems (mpac-smartpos, mpac-obs) to use shared stacks. Remove duplicated resources from parent stacks.

Start with VPC and RDS stacks — these had the most painful cross-template changes in this lint fix round.

MPAC — MP-Solution Advanced Cloud Service