mirror of
https://github.com/Sea-Haven-Industries/file-share.git
synced 2026-09-30 07:43:17 +00:00
fix(infra): adopt existing data volume + cache AMI to stop replacement churn
- Import vol-04d951cccacc435b5 (the real 500GiB data volume, orphaned by the 2026-06-05 redeploy) via Volume.fromVolumeAttributes — unmanaged, so CloudFormation can attach but never create/replace/delete it. - Remove the inline /dev/xvdf blockDevice (this is what created the empty volume that shadowed the data). - cachedInContext: true + committed cdk.context.json so AMI updates are deliberate (uncached lookup replaced the instance on every new AL2023 release). - Includes the previously-deployed-but-uncommitted 2026-05-27 work: standalone volume pattern, volume-wait userdata, SFTP access. Deploy replaces the instance once; userdata's blkid guard mounts the existing filesystem without formatting.
This commit is contained in:
parent
dac9f4ed2b
commit
ea9ab1c463
2 changed files with 44 additions and 11 deletions
|
|
@ -43,5 +43,6 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ssm:account=328440206208:parameterName=/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64:region=us-east-1": "ami-0b183bb1259186479"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export class FileShareStack extends cdk.Stack {
|
|||
sg.addIngressRule(ec2.Peer.ipv4("10.20.0.0/16"), ec2.Port.tcp(445), "SMB from VPC");
|
||||
sg.addIngressRule(ec2.Peer.ipv4("10.10.0.0/16"), ec2.Port.tcp(8080), "FileBrowser from office VPN");
|
||||
sg.addIngressRule(ec2.Peer.ipv4("10.20.0.0/16"), ec2.Port.tcp(8080), "FileBrowser from VPC");
|
||||
sg.addIngressRule(ec2.Peer.ipv4("10.10.0.0/16"), ec2.Port.tcp(22), "SFTP from office VPN");
|
||||
|
||||
const role = new iam.Role(this, "InstanceRole", {
|
||||
roleName: "file-share-instance",
|
||||
|
|
@ -50,7 +51,11 @@ export class FileShareStack extends cdk.Stack {
|
|||
userData.addCommands(
|
||||
"set -euxo pipefail",
|
||||
"",
|
||||
"# ── Data volume ──",
|
||||
"# ── Data volume (wait for CfnVolumeAttachment) ──",
|
||||
"until lsblk -dno NAME | grep -v $(lsblk -no PKNAME $(findmnt -n -o SOURCE /) 2>/dev/null || echo xvda) | grep -q .; do",
|
||||
" echo 'Waiting for data volume...'",
|
||||
" sleep 5",
|
||||
"done",
|
||||
"DATA_DEVICE=/dev/$(lsblk -dno NAME | grep -v $(lsblk -no PKNAME $(findmnt -n -o SOURCE /) 2>/dev/null || echo xvda) | head -1)",
|
||||
"if ! blkid \"$DATA_DEVICE\"; then",
|
||||
" mkfs.ext4 -L file-share-data \"$DATA_DEVICE\"",
|
||||
|
|
@ -139,6 +144,19 @@ export class FileShareStack extends cdk.Stack {
|
|||
"",
|
||||
"systemctl daemon-reload",
|
||||
"systemctl enable --now filebrowser",
|
||||
"",
|
||||
"# ── SFTP access (password auth, same creds as SMB) ──",
|
||||
"usermod -s /bin/bash -d /data/share adam",
|
||||
"echo \"$SMB_PASSWORD\" | passwd --stdin adam",
|
||||
"cat >> /etc/ssh/sshd_config << 'SSHEOF'",
|
||||
"",
|
||||
"Match User adam",
|
||||
" ForceCommand internal-sftp",
|
||||
" PasswordAuthentication yes",
|
||||
" AllowTcpForwarding no",
|
||||
" X11Forwarding no",
|
||||
"SSHEOF",
|
||||
"systemctl restart sshd",
|
||||
);
|
||||
|
||||
const instance = new ec2.Instance(this, "Instance", {
|
||||
|
|
@ -148,6 +166,10 @@ export class FileShareStack extends cdk.Stack {
|
|||
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.SMALL),
|
||||
machineImage: ec2.MachineImage.latestAmazonLinux2023({
|
||||
cpuType: ec2.AmazonLinuxCpuType.ARM_64,
|
||||
// Cache the resolved AMI in cdk.context.json so deploys don't pick up
|
||||
// new AL2023 releases implicitly (AMI change forces instance replacement).
|
||||
// Refresh deliberately with: cdk context --reset <ami key> && cdk synth
|
||||
cachedInContext: true,
|
||||
}),
|
||||
securityGroup: sg,
|
||||
role,
|
||||
|
|
@ -160,18 +182,26 @@ export class FileShareStack extends cdk.Stack {
|
|||
encrypted: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
deviceName: "/dev/xvdf",
|
||||
volume: ec2.BlockDeviceVolume.ebs(500, {
|
||||
volumeType: ec2.EbsDeviceVolumeType.GP3,
|
||||
encrypted: true,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
cdk.Tags.of(instance).add("file-share-backup", "true");
|
||||
|
||||
// The data volume is deliberately UNMANAGED (imported by ID): CloudFormation
|
||||
// can attach it but can never create, replace, or delete it. The volume
|
||||
// survives any instance replacement. Created 2026-05-27; holds /data/share.
|
||||
// Tags (Name, file-share-backup for DLM) are set directly on the volume.
|
||||
const dataVolume = ec2.Volume.fromVolumeAttributes(this, "DataVolume", {
|
||||
volumeId: "vol-04d951cccacc435b5",
|
||||
availabilityZone: "us-east-1a",
|
||||
});
|
||||
|
||||
new ec2.CfnVolumeAttachment(this, "DataVolumeAttachment", {
|
||||
instanceId: instance.instanceId,
|
||||
volumeId: dataVolume.volumeId,
|
||||
device: "/dev/xvdf",
|
||||
});
|
||||
|
||||
const dlmRole = new iam.Role(this, "DlmRole", {
|
||||
roleName: "file-share-dlm",
|
||||
assumedBy: new iam.ServicePrincipal("dlm.amazonaws.com"),
|
||||
|
|
@ -193,8 +223,10 @@ export class FileShareStack extends cdk.Stack {
|
|||
name: "file-share-nightly",
|
||||
createRule: { interval: 24, intervalUnit: "HOURS", times: ["06:00"] },
|
||||
retainRule: { count: 30 },
|
||||
// copyTags already propagates file-share-backup=true from the volume to each
|
||||
// snapshot; an explicit tagsToAdd of the same key triggers DLM's duplicate-tag
|
||||
// error ("Tag file-share-backup is already defined") and puts the policy in ERROR.
|
||||
copyTags: true,
|
||||
tagsToAdd: [{ key: "file-share-backup", value: "true" }],
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
|
@ -205,7 +237,7 @@ export class FileShareStack extends cdk.Stack {
|
|||
|
||||
new cdk.CfnOutput(this, "PrivateIp", {
|
||||
value: instance.instancePrivateIp,
|
||||
description: "Use for SMB (smb://<ip>/files) and FileBrowser (http://<ip>:8080)",
|
||||
description: "SMB (smb://<ip>/files), FileBrowser (http://<ip>:8080), SFTP (sftp://<ip>)",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue