All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
README.md: new "AWS Services Used — and Why" section explaining each service's role and the on-prem equivalent it replaces — covers Compute, Networking, Storage, Database, and Operations groups
README.md: removed stale reference toscripts/migrate.sh(script does not exist)
cdk/lib/hubzero-stack.ts: migrated fromAWS::AutoScaling::LaunchConfigurationtoAWS::EC2::LaunchTemplate— AWS no longer allows LaunchConfiguration creation in new accountscdk/lib/hubzero-stack.ts: createdInstanceRoleexplicitly (required when usingLaunchTemplate); replaced allasg.rolereferences withinstanceRolecdk/lib/hubzero-stack.ts: removedcfnAsg.addPropertyOverride("InstanceRefresh", ...)—InstanceRefreshis not supported on ASGs backed by LaunchConfigurations; no longer needed now that LaunchTemplate is usedcdk/lib/hubzero-stack.ts: changed S3 bucket encryption fromKMS_MANAGEDtoS3_MANAGEDfor non-prod — CDK'sautoDeleteObjectsLambda lacks KMS key grants and would fail to empty the bucket on destroycdk/lib/hubzero-stack.ts: removed duplicatecdk.Tags.of(this).add(...)blockcdk/lib/hubzero-stack.ts: addedvpcIdnull check with a clear error message at synth timecdk/lib/hubzero-stack.ts: changedspotPricetospotOptions: { maxPrice, requestType }(spotPriceis not a validLaunchTemplatePropsfield)cdk/cdk.context.example.json: changeduseBakedAmidefault from"true"to"false"— lookup fails on first deploy when nohubzero-base-*AMI existscdk/package.json: addedaws-cdkCLI as adevDependency(CDK CLI must matchaws-cdk-libversion; global CLI may be incompatible)README.md: updated CDK deploy instructions withCDK_DEFAULT_ACCOUNT/CDK_DEFAULT_REGIONrequirement; added CDK destroy section with DLM policy cleanup commands
docs/migration-guide.md: replaced reference to non-existentmigrate.shscript with explicit manual migration steps (mysqldump pipeline, rsync, config update)docs/migration-guide.md: corrected on-premapp/path — HubZero 2.4 uses/var/www/hubzero/app/, not/var/www/html/app/; note added for older installsdocs/migration-guide.md: pre-migration checklist no longer assumesenable_alb=truefor the health check stepterraform/environments/test.tfvars,staging.tfvars,prod.tfvars: added commentedaws_regionhint — variable must be set if deploying outsideus-east-1and must match at bothapplyanddestroytimeREADME.md: destroy example no longer hardcodesus-west-2; added note explaining theaws_regionrequirementdocs/getting-started-aws.md: removed stale "I destroyed the stack" advice about S3 bucket manual emptying (now handled byforce_destroy = true) and CloudWatch log groups; replaced with note about EBS snapshots and the state backend teardown script
terraform/main.tf: addedforce_destroy = truetoaws_s3_bucket— versioned buckets previously required manual emptying beforeterraform destroycould succeedterraform/main.tf: removedlifecycle { prevent_destroy = true }fromaws_dlm_lifecycle_policy— it blockedterraform destroyentirelyterraform/main.tf: added explicitskip_destroy = falseto all threeaws_cloudwatch_log_groupresources for unambiguous destroy behaviourREADME.md: added Destroying the Stack section documenting the requiredaws_regionvariable, theterraform destroycommand, and manual cleanup steps for EBS snapshots created by the DLM policy
scripts/bake.sh:dnf install curlnow uses--allowerasingto replace AL2023's pre-installedcurl-minimal(conflicting package)scripts/bake.sh: removedmod_headersfrom dnf install — it is built intohttpdon AL2023 and has no separate packagescripts/bake.sh: replaced MariaDB community repo install with native AL2023mariadb105package (community repoMariaDB-clientpackage name not valid on AL2023)scripts/bake.sh: removedphp8.2-curlandphp8.2-jsonfrom dnf install — both are bundled inphp8.2-commonon AL2023 and have no separate packagesscripts/bake.sh: exportHOMEandCOMPOSER_HOMEbefore Composer installer runs — cloud-init executes with a minimal environment that may not haveHOMEsetscripts/bake.sh: corrected HubZero CMS git branch from2.4to2.4-mainscripts/bake.sh: corrected ApacheDocumentRootfrom/var/www/hubzero/publicto/var/www/hubzero(HubZero 2.4 serves from the repo root)scripts/bake.sh: runcomposer installincore/subdirectory (that is wherecomposer.jsonlives in HubZero 2.4, not the repo root)scripts/bake.sh: create.htaccesswithmod_rewriterules after clone — HubZero does not commit one to the repo, but it is required for framework URL routingscripts/bake.sh: createapp/,app/config/,app/logs/,app/tmp/directories owned byapache— required by the web installer before it can write config filesscripts/userdata.sh: replaced MariaDB communityMariaDB-serverwith native AL2023mariadb105-serverscripts/userdata.sh:touchlog file beforechmodand changeexecredirect totee -a— fixes race condition wherechmodran beforeteehad created the file in piped-bash execution (cloud-initcurl | bashcontext)docs/getting-started-aws.md,README.md: added deployment monitoring section with commands to track bootstrap progress (terraform applycompletes in 2–3 min; full bootstrap takes 10–15 min)
0.7.0 - 2026-03-18
- Deployment profiles — new
deployment_profilevariable (minimal|graviton|spot; default:minimal) replacing hardcoded per-environment instance typesminimal: t3.medium x86_64 on-demand (~$30/mo compute). Default for all environments.graviton: t4g.medium ARM64 on-demand (~$24/mo compute). AL2023, Apache, PHP, and MariaDB all run on ARM64; ~20% cheaper for equivalent workloads.spot: t3.medium x86_64 spot pricing (~$4–8/mo compute). Enforcesuse_rds=trueandenable_efs=truevia precondition to prevent data loss on interruption.- New
instance_typevariable overrides the profile's default instance size without changing other profile settings
- AMI architecture awareness — AL2023 AMI filter now parameterised on
local.cpu_arch(x86_64/arm64); baked AMI filter adds architecture tag to prevent cross-arch mismatch
env_configvolume sizes reduced to reflect realistic needs: test 30 GB (was 100), staging 100 GB (was 500), prod 200 GB (was 1000)test.tfvarsnow explicitly setsdeployment_profile=minimal,use_rds=false,enable_alb=false,enable_vpc_endpoints=false,enable_waf=false,enable_efs=false— represents the cheapest viable deployment (~$30–35/mo)- ASG switches from
launch_templateblock tomixed_instances_policyblock — enables spot support while preserving on-demand behaviour for minimal/graviton profiles - CDK:
ENV_CONFIGdropsinstanceType(now fromPROFILE_CONFIG);deploymentProfilecontext variable added; ARM64 machine image selected automatically for graviton profile; spot profile setsspotPriceon the ASG cdk.context.example.json:deploymentProfile: "minimal"added as default; defaults lean toward minimal-cost options (useRds: "false",enableAlb: "false",enableVpcEndpoints: "false")
0.6.0 - 2026-03-18
- Issue #18: EFS shared web root via
enable_efsvariable (default:true)aws_efs_file_system: encrypted,generalPurposeperformance modeaws_security_group.efs: ingress port 2049 from EC2 SGaws_efs_mount_target: one per subnet inefs_subnet_ids(defaults to[subnet_id])aws_efs_access_point: POSIX uid/gid 48 (apache), root path/hubzero- IAM policy:
elasticfilesystem:ClientMount,ClientWrite,ClientRootAccess scripts/userdata.sh: installsamazon-efs-utils, mounts EFS via TLS+IAM+access point, adds/etc/fstabentry for persistence- Terraform:
efs_idoutput;HUBZERO_EFS_IDandHUBZERO_EFS_ACCESS_POINT_IDexported to userdata - CDK:
efs.FileSystem,efs.AccessPoint, equivalent IAM grant, exports
- Issue #19: Auto Scaling Group (min=1, max=1) replacing direct
aws_instanceaws_launch_template.hubzero: same AMI, instance type, SG, IAM profile, userdata as formeraws_instanceaws_autoscaling_group.hubzero: min=1, max=1, desired=1;health_check_type = "ELB";target_group_arnsattached to ALB; rolling instance refresh viainstance_refreshblock (min_healthy_percentage = 0)aws_autoscaling_attachment: attaches ASG to ALB target group- CloudWatch alarm dimensions updated from
InstanceIdtoAutoScalingGroupNamefor EC2/ASG alarms; CWAgent instance-level alarms retainInstanceId(targets most-recent launch) - Outputs:
instance_idreplaced byasg_name;ssm_connect_commandupdated to dynamically look up running instance viaaws ec2 describe-instances - CDK:
autoscaling.AutoScalingGroup, rolling instance refresh viaCfnAutoScalingGroup.addPropertyOverride
- Issue #20: CloudFront CDN distribution via
enable_cdnvariable (default:false)aws_cloudfront_distribution: ALB as origin (https-only), static asset paths (/media/*,/assets/*,/css/*,/js/*) useCachingOptimizedpolicy; default behavior usesCachingDisabled- HTTP→HTTPS redirect for all behaviors;
PriceClass_100(US/EU/Canada) enable_cloudfront_wafvariable added (default:false) — CloudFront-scoped WAF must reside inus-east-1; requires a provider alias (documented constraint)web_urloutput: prefers CloudFront domain whenenable_cdn=true- New output:
cloudfront_domain - CDK:
cloudfront.Distributionwithorigins.LoadBalancerV2Origin
0.5.0 - 2026-03-18
- Issue #15: Packer AMI baking (
packer/hubzero.pkr.hcl,scripts/bake.sh)packer/hubzero.pkr.hcl: AL2023 base,t3.mediumbake instance, shell provisioner runsbake.sh, AMI namedhubzero-base-<date>, tagged withProject=hubzeroandGitSHAscripts/bake.sh: extracted static installs fromuserdata.sh— base packages, fail2ban config, Apache + security headers + vhost config, MariaDB client, PHP 8.2 packages + config, Composer + HubZero CMS clonescripts/userdata.sh: refactored to env-specific only — services start, certbot, local MariaDB server (if !RDS), Docker+Solr, credentials file, firewall, CWAgent config+start- Terraform:
use_baked_amivariable (defaulttrue);data.aws_ami.hubzero_bakeddata source (owner=self, filter=hubzero-base-*); EC2 uses baked AMI when available, falls back to AL2023;bake.shprepended to userdata whenuse_baked_ami=false - CDK:
useBakedAmicontext variable;MachineImage.lookupfor baked AMI; falls back tolatestAmazonLinux2023 - CI:
packerjob validatespacker/hubzero.pkr.hclon every PR/push; actualpacker buildgated by secrets
- Issue #16: SSM Patch Manager via
enable_patch_managervariable (default:true)aws_ssm_patch_baseline: AL2023, Security/Critical+Important classification, 7-day approvalaws_ssm_patch_group,aws_ssm_maintenance_window(Sunday 03:00 UTC), maintenance window target + task (AWS-RunPatchBaseline)- EC2 instance tagged with
Patch Group = hubzero-${environment}for SSM targeting - CloudWatch alarm:
SSM/NonCompliantCount > 0for the patch group - CDK:
CfnPatchBaseline,CfnAssociationwith schedule;Patch Grouptag on instance
- Issue #17: SSM Parameter Store via
enable_parameter_storevariable (default:true)- Parameters:
domain_name,db_host,db_name,db_user,s3_bucket,enable_monitoring,cw_log_prefixunder/hubzero/${environment}/ - IAM policy:
ssm:GetParametersByPath+ssm:GetParameteron the environment path scripts/userdata.sh: at startup, sources SSM parameters viaget-parameters-by-pathand maps to env vars; env var fallbacks remain for compatibility- CDK:
StringParameterconstructs for each parameter; IAM policy grant on instance role
- Parameters:
0.4.0 - 2026-03-18
- Issue #12: Application Load Balancer + ACM TLS via
enable_albvariable (default:true)- EC2 security group: HTTP/HTTPS ingress from
allowed_cidris removed when ALB is enabled; ALB SG → EC2 ingress rule added viaaws_security_group_rule aws_security_group.alb,aws_lb,aws_lb_target_group,aws_lb_target_group_attachmentaws_lb_listener.http(HTTP→HTTPS 301 redirect),aws_lb_listener.https(TLS forward)aws_acm_certificatewith DNS validation (created whenacm_certificate_arnis empty anddomain_nameis set)web_urloutput updated: uses ALB DNS name or custom domain when ALB is enabled- New outputs:
alb_dns_name,acm_certificate_validation_cname scripts/userdata.sh: certbot section guarded byHUBZERO_ENABLE_ALB != true- CDK:
ApplicationLoadBalancer,ApplicationTargetGroup,ListenerAction.redirect,acm.Certificate,SslPolicy.RECOMMENDED_TLS
- EC2 security group: HTTP/HTTPS ingress from
- Issue #13: AWS WAF v2 (regional) via
enable_wafvariable (default:true, requiresenable_alb)aws_wafv2_web_aclwith three managed rule groups in Block mode: CommonRuleSet, KnownBadInputsRuleSet, SQLiRuleSetaws_wafv2_web_acl_associationattaches WAF to ALB- CloudWatch alarm:
BlockedRequestsspike > 100 (informational) - CDK:
CfnWebACL+CfnWebACLAssociationconstructs
- Issue #14: VPC endpoints via
enable_vpc_endpointsvariable (default:true)- Gateway endpoint: S3 (free)
- Interface endpoints: SSM, SSMMessages, EC2Messages, SecretsManager, CloudWatch Logs
- Dedicated VPC endpoint security group allowing HTTPS from EC2 SG
- CDK:
GatewayVpcEndpointfor S3,InterfaceVpcEndpointfor remaining services
0.3.0 - 2026-03-18
- Issue #9: Migrated from Rocky Linux 8 to Amazon Linux 2023 (AL2023)
- Replaced Rocky 8 AMI data source with AL2023 (
owners = ["137112412989"], name filteral2023-ami-2023.*-x86_64) - CDK: replaced
MachineImage.lookupwithMachineImage.latestAmazonLinux2023 scripts/userdata.sh: removed EPEL install; switched to MariaDB community repo (mariadb_repo_setup) with capital-M packages (MariaDB-server,MariaDB-client); switched to versioned PHP packages (php8.2,php8.2-fpm, etc.); replaced S3 RPM CloudWatch Agent install withdnf install -y amazon-cloudwatch-agent
- Replaced Rocky 8 AMI data source with AL2023 (
- Issue #10: S3-backed file storage via
enable_s3_storagevariable (default:true)- Terraform:
aws_s3_bucketwith versioning, KMS SSE, public access block, 90-day STANDARD_IA lifecycle;aws_iam_role_policygrantings3:GetObject/PutObject/DeleteObject/ListBucketto EC2 role - CDK:
s3.Bucketconstruct with equivalent properties;grantReadWriteto instance role userdata.sh: exportsHUBZERO_S3_BUCKET; note in credentials file for filesystem adapter config- Terraform:
s3_bucket_nameoutput
- Terraform:
- Issue #11: RDS is now the recommended default
use_rdsdefault changed fromfalsetotrueaws_instancelifecycle precondition:use_rds=falseraises an error for staging/prod (useenvironment=testto bypass)staging.tfvarsandprod.tfvars: addeduse_rds = truewith commented examplerds_subnet_ids- CDK
cdk.context.example.json:useRdsdefault changed to"true"; addedenableS3Storage: "true" - README: architecture section updated to reflect AL2023 and RDS-as-default
0.2.0 - 2026-03-18
- CloudWatch monitoring and alerting gated by
enable_monitoringflag (default: true) - Terraform:
enable_monitoringandalarm_emailvariables;monitoring_configlocals with per-environment log retention, CPU thresholds, alarm periods, and evaluation periods - Terraform: IAM policy
cloudwatchgrantingPutMetricDataand log management actions to the EC2 instance role (unconditional, required for CWAgent to start) - Terraform: Three CloudWatch log groups per environment (
/userdata,/apache-access,/apache-error) with environment-specific retention - Terraform: SNS topic
hubzero-{env}-alarmswith optional email subscription - Terraform: Four EC2 CloudWatch alarms: CPU, StatusCheckFailed, mem_used_percent,
disk_used_percent; three RDS alarms (CPU, DatabaseConnections, FreeStorageSpace),
all conditional on
enable_monitoring(RDS alarms also requireuse_rds=true) - Terraform:
sns_topic_arnoutput - CDK: Equivalent monitoring block with
MONITORING_CONFIGconstant,enableMonitoring/alarmEmailcontext reads, log groups, SNS topic, all 7 alarms, andSnsTopicArnCfnOutput scripts/userdata.sh: Section 9 — CloudWatch Agent install via S3 RPM, JSON config (mem + disk metrics, 3 log file tails), agent start; wrapped inHUBZERO_ENABLE_MONITORINGguardcdk/cdk.context.example.json:enableMonitoringandalarmEmailkeys
0.1.0 - 2026-03-18
- Terraform implementation: EC2, optional RDS MariaDB 10.11, EBS DLM snapshots, IAM roles, Secrets Manager integration, SSM Session Manager access
- AWS CDK (TypeScript) implementation with feature parity to Terraform
- Three environments (test / staging / prod) with environment-specific instance types, storage, RDS sizing, backup retention, and deletion protection
- EC2 bootstrap script (
scripts/userdata.sh) for Rocky Linux 8: Apache 2.4 + PHP-FPM 8.2, HubZero CMS v2.4 via Composer, optional MariaDB 10.11, optional Docker + Apache Solr 9.7 - On-premises migration script (
scripts/migrate.sh) with SSH-based data transfer, database export/import, and Solr index rebuild - On-premises migration guide (
docs/migration-guide.md) - Terraform state backend bootstrap script (
scripts/bootstrap-terraform-backend.sh) - GitHub Actions CI: Terraform fmt/validate, CDK TypeScript build and lint
- Apache 2.0 LICENSE
- Security hardening: IMDSv2 enforcement, fail2ban, HSTS + security headers, PHP hardening, Docker user namespace remapping, Composer integrity checks, encrypted EBS and RDS at rest, Secrets Manager for RDS credentials