Simulated AWS Backup
Yulin includes simulated AWS Backup vaults, plans, selections, backup jobs and recovery points for
tests and local development. AWS Backup types are imported from the @kensio/yulin/backup subpath.
Creating a vault, plan and selection
Section titled “Creating a vault, plan and selection”simAws.backup() gives the AWS Backup service for the default account and Region. A plan rule names
an existing vault. A selection belongs to one plan and records the resource ARNs assigned to it.
/** * Creating a daily backup plan for one DynamoDB table. */
import { CreateBackupPlanCommand, CreateBackupSelectionCommand, CreateBackupVaultCommand, GetBackupPlanCommand, GetBackupSelectionCommand,} from "@aws-sdk/client-backup";import { assertNonNullable } from "@kensio/smartass";
import { SimAws } from "@kensio/yulin";
const simAws = new SimAws();const backup = simAws.backup();
await backup.createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "application-backups", }),);
const createdPlan = await backup.createBackupPlan( new CreateBackupPlanCommand({ BackupPlan: { BackupPlanName: "application-plan", Rules: [ { RuleName: "daily", TargetBackupVaultName: "application-backups", ScheduleExpression: "cron(0 1 ? * * *)", Lifecycle: { DeleteAfterDays: 35 }, }, ], }, }),);assertNonNullable(createdPlan.BackupPlanId);
const createdSelection = await backup.createBackupSelection( new CreateBackupSelectionCommand({ BackupPlanId: createdPlan.BackupPlanId, BackupSelection: { SelectionName: "orders", IamRoleArn: "arn:aws:iam::888888888888:role/BackupRole", Resources: ["arn:aws:dynamodb:us-east-1:888888888888:table/orders"], }, }),);assertNonNullable(createdSelection.SelectionId);
const plan = await backup.getBackupPlan( new GetBackupPlanCommand({ BackupPlanId: createdPlan.BackupPlanId }),);console.log(plan.BackupPlan?.Rules?.[0]?.ScheduleExpression);// "cron(0 1 ? * * *)"
const selection = await backup.getBackupSelection( new GetBackupSelectionCommand({ BackupPlanId: createdPlan.BackupPlanId, SelectionId: createdSelection.SelectionId, }),);console.log(selection.BackupSelection?.Resources);// ["arn:aws:dynamodb:us-east-1:888888888888:table/orders"]A plan needs at least one rule. Every rule needs a name and a target vault. An omitted schedule uses
cron(0 5 ? * * *), the AWS Backup default. Six-field AWS cron expressions and rate expressions
are validated when the plan is created. A one-time at(...) expression is refused.
MoveToColdStorageAfterDays can be combined with DeleteAfterDays. The deletion must be at least
90 days after the move to cold storage. A shorter lifecycle raises
InvalidParameterValueException. Set both values to -1 to retain recovery points indefinitely.
Running scheduled backups
Section titled “Running scheduled backups”Each plan rule runs on the simulated clock. A due rule creates one recovery point for each distinct resource ARN in the plan’s selections. The recovery point records the rule, resource ARN, lifecycle and creation time. The simulation completes backup jobs at the scheduled instant.
/** * Advancing a plan through its next scheduled backup. */
import { CreateBackupPlanCommand, CreateBackupSelectionCommand, CreateBackupVaultCommand, ListBackupJobsCommand, ListRecoveryPointsByBackupVaultCommand,} from "@aws-sdk/client-backup";import { assertArrayLength, assertNonNullable } from "@kensio/smartass";
import { SimAws, SimFixedClock } from "@kensio/yulin";
const simAws = new SimAws({ clock: new SimFixedClock(new Date("2026-08-31T09:30:00.000Z")),});const backup = simAws.backup();
await backup.createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "application-backups" }),);const createdPlan = await backup.createBackupPlan( new CreateBackupPlanCommand({ BackupPlan: { BackupPlanName: "application-plan", Rules: [ { RuleName: "hourly", TargetBackupVaultName: "application-backups", ScheduleExpression: "rate(1 hour)", Lifecycle: { DeleteAfterDays: 35 }, }, ], }, }),);assertNonNullable(createdPlan.BackupPlanId);await backup.createBackupSelection( new CreateBackupSelectionCommand({ BackupPlanId: createdPlan.BackupPlanId, BackupSelection: { SelectionName: "orders", IamRoleArn: "arn:aws:iam::888888888888:role/BackupRole", Resources: ["arn:aws:dynamodb:us-east-1:888888888888:table/orders"], }, }),);
await simAws.clock().advanceBy({ hours: 1 });
const stored = backup.vault("application-backups").recoveryPoints();assertArrayLength(stored, 1);console.log(stored[0].creationDate.toISOString());// "2026-08-31T10:30:00.000Z"
const points = await backup.listRecoveryPointsByBackupVault( new ListRecoveryPointsByBackupVaultCommand({ BackupVaultName: "application-backups", }),);const jobs = await backup.listBackupJobs(new ListBackupJobsCommand({}));console.log(points.RecoveryPoints?.[0]?.ResourceArn);// "arn:aws:dynamodb:us-east-1:888888888888:table/orders"console.log(jobs.BackupJobs?.[0]?.State); // "COMPLETED"DeleteAfterDays removes a recovery point when the clock reaches its deletion time. Vault reads,
ListRecoveryPointsByBackupVault and DescribeRecoveryPoint apply the expiry before returning.
Repeated schedules keep every unexpired recovery point.
Vault Lock bounds apply when a backup starts. A lifecycle shorter than MinRetentionDays, longer
than MaxRetentionDays or indefinite under a finite maximum produces a FAILED backup job. The
vault receives no recovery point for that job. Use ListBackupJobs or DescribeBackupJob to read
the failure and its StatusMessage.
Starting an on-demand backup
Section titled “Starting an on-demand backup”StartBackupJob completes an on-demand job at the current simulated time. It applies the same
lifecycle validation and Vault Lock bounds as a scheduled rule.
/** * Creating an on-demand recovery point. */
import { CreateBackupVaultCommand, DescribeRecoveryPointCommand, StartBackupJobCommand,} from "@aws-sdk/client-backup";import { assertNonNullable } from "@kensio/smartass";
import { SimAws, SimFixedClock } from "@kensio/yulin";
const simAws = new SimAws({ clock: new SimFixedClock(new Date("2026-08-31T12:00:00.000Z")),});const backup = simAws.backup();await backup.createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "manual-backups" }),);
const started = await backup.startBackupJob( new StartBackupJobCommand({ BackupVaultName: "manual-backups", ResourceArn: "arn:aws:s3:::application-files", IamRoleArn: "arn:aws:iam::888888888888:role/BackupRole", Lifecycle: { DeleteAfterDays: 14 }, }),);assertNonNullable(started.RecoveryPointArn);
const point = await backup.describeRecoveryPoint( new DescribeRecoveryPointCommand({ BackupVaultName: "manual-backups", RecoveryPointArn: started.RecoveryPointArn, }),);console.log(point.CreationDate?.toISOString());// "2026-08-31T12:00:00.000Z"Vault Lock
Section titled “Vault Lock”PutBackupVaultLockConfiguration records minimum and maximum retention periods on a vault. Adding
ChangeableForDays creates a compliance lock. The configuration stays changeable until the grace
period ends, then becomes immutable.
/** * Advancing a compliance lock through its grace period. */
import { CreateBackupVaultCommand, DescribeBackupVaultCommand, PutBackupVaultLockConfigurationCommand,} from "@aws-sdk/client-backup";
import { SimAws, SimFixedClock } from "@kensio/yulin";
const simAws = new SimAws({ clock: new SimFixedClock(new Date("2026-08-30T10:00:00.000Z")),});const backup = simAws.backup();
await backup.createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "compliance-backups" }),);
await backup.putBackupVaultLockConfiguration( new PutBackupVaultLockConfigurationCommand({ BackupVaultName: "compliance-backups", ChangeableForDays: 3, MinRetentionDays: 7, MaxRetentionDays: 365, }),);
const changeable = await backup.describeBackupVault( new DescribeBackupVaultCommand({ BackupVaultName: "compliance-backups", }),);console.log(changeable.LockDate?.toISOString());// "2026-09-02T10:00:00.000Z"
await simAws.clock().advanceBy({ days: 3 });
try { await backup.putBackupVaultLockConfiguration( new PutBackupVaultLockConfigurationCommand({ BackupVaultName: "compliance-backups", MinRetentionDays: 14, }), );} catch (error) { console.log(error instanceof Error ? error.name : "unknown error"); // "InvalidParameterValueException"}ChangeableForDays must be between 3 and 36,500. Retention periods are whole days. A minimum cannot
exceed the maximum, and the maximum cannot exceed 36,500 days. A lock without
ChangeableForDays remains changeable.
Deploying from CloudFormation
Section titled “Deploying from CloudFormation”Simulated CloudFormation deploys AWS::Backup::BackupVault, AWS::Backup::BackupPlan and
AWS::Backup::BackupSelection. References between the resources resolve before AWS Backup creates
them.
/** * Deploying a vault, plan and selection from one template. */
import { GetBackupSelectionCommand } from "@aws-sdk/client-backup";
import { SimAws } from "@kensio/yulin";
const simAws = new SimAws();const stack = await simAws.cloudFormation().deployTemplate({ stackName: "backup-stack", template: { Resources: { Vault: { Type: "AWS::Backup::BackupVault", Properties: { BackupVaultName: "application-backups", LockConfiguration: { MinRetentionDays: 7, MaxRetentionDays: 365, }, }, }, Plan: { Type: "AWS::Backup::BackupPlan", Properties: { BackupPlan: { BackupPlanName: "application-plan", BackupPlanRule: [ { RuleName: "daily", TargetBackupVault: { Ref: "Vault" }, ScheduleExpression: "cron(0 1 ? * * *)", Lifecycle: { DeleteAfterDays: 35 }, }, ], }, }, }, Selection: { Type: "AWS::Backup::BackupSelection", Properties: { BackupPlanId: { Ref: "Plan" }, BackupSelection: { SelectionName: "orders", IamRoleArn: "arn:aws:iam::888888888888:role/BackupRole", Resources: ["arn:aws:dynamodb:us-east-1:888888888888:table/orders"], }, }, }, }, Outputs: { VaultArn: { Value: { "Fn::GetAtt": ["Vault", "BackupVaultArn"] }, }, PlanId: { Value: { Ref: "Plan" } }, SelectionId: { Value: { Ref: "Selection" } }, }, },});
await stack.waitForDeployComplete();
const selection = await simAws.backup().getBackupSelection( new GetBackupSelectionCommand({ BackupPlanId: stack.output("PlanId"), SelectionId: stack.output("SelectionId"), }),);
console.log(stack.output("VaultArn"));// "arn:aws:backup:us-east-1:888888888888:backup-vault:application-backups"console.log(selection.BackupSelection?.SelectionName); // "orders"Ref and Fn::GetAtt return these values:
AWS::Backup::BackupVaultreturns the vault name fromRef.BackupVaultArnandBackupVaultNameare available throughFn::GetAtt.AWS::Backup::BackupPlanreturns the plan ID fromRef.BackupPlanArn,BackupPlanIdandVersionIdare available throughFn::GetAtt.AWS::Backup::BackupSelectionreturns the selection ID fromRef.Id,SelectionIdandBackupPlanIdare available throughFn::GetAtt.
Stack teardown removes all three resource types from the simulation.
Permissions
Section titled “Permissions”Every supported operation is authorized by simulated IAM. Vault operations use the vault ARN. Plan
and selection operations use the plan ARN. ListBackupVaults has no resource in its request and is
authorized against *. StartBackupJob and ListRecoveryPointsByBackupVault use the vault ARN.
DescribeRecoveryPoint uses the recovery point ARN. Backup job reads use *.
/** * A Role allowed to create one named backup vault. */
import { CreateBackupVaultCommand } from "@aws-sdk/client-backup";import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
import { SimAws } from "@kensio/yulin";
const simAws = new SimAws();const role = await simAws.iam().createRole( new CreateRoleCommand({ RoleName: "BackupAdministrator", AssumeRolePolicyDocument: JSON.stringify({ Version: "2012-10-17", Statement: { Effect: "Allow", Principal: { AWS: "arn:aws:iam::888888888888:root" }, Action: "sts:AssumeRole", }, }), }),);
await simAws.iam().putRolePolicy( new PutRolePolicyCommand({ RoleName: "BackupAdministrator", PolicyName: "CreateApplicationVault", PolicyDocument: JSON.stringify({ Version: "2012-10-17", Statement: { Effect: "Allow", Action: "backup:CreateBackupVault", Resource: "arn:aws:backup:us-east-1:888888888888:backup-vault:application-backups", }, }), }),);
const created = await simAws.backup().createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "application-backups", }), { caller: { kind: "arn", arn: role.Role.Arn } },);
console.log(created.BackupVaultName); // "application-backups"Authorization runs before resource lookup. An unauthorized request for a missing vault or plan
raises AccessDeniedException, without revealing whether the resource exists.
SDK interception
Section titled “SDK interception”SimSdk routes commands from an AWS BackupClient to the simulation. Intercept the client instance
when a test owns it, or intercept the class when application code creates the client.
/** * Routing an AWS Backup client into the simulation. */
import { BackupClient, CreateBackupVaultCommand, ListBackupVaultsCommand,} from "@aws-sdk/client-backup";
import { SimSdk } from "@kensio/yulin/sdk";
using simSdk = new SimSdk();const client = new BackupClient({ region: "us-east-1" });simSdk.intercept(client);
await client.send( new CreateBackupVaultCommand({ BackupVaultName: "application-backups" }),);
const listed = await client.send(new ListBackupVaultsCommand({}));console.log(listed.BackupVaultList?.[0]?.BackupVaultName);// "application-backups"The client’s configured Region selects the simulated Region. Credentials select the account and caller when the intercepted client has them. See the SDK interception docs for class interception and credential handling.
Account and Region scoping
Section titled “Account and Region scoping”AWS Backup state belongs to one account and Region. The same vault name can exist in another scope.
/** * Keeping backup vaults inside their account and Region. */
import { CreateBackupVaultCommand, ListBackupVaultsCommand,} from "@aws-sdk/client-backup";
import { SimAws } from "@kensio/yulin";
const simAws = new SimAws();
await simAws .account("111111111111") .region("eu-west-2") .backup() .createBackupVault( new CreateBackupVaultCommand({ BackupVaultName: "application-backups" }), );
const inLondon = await simAws .account("111111111111") .region("eu-west-2") .backup() .listBackupVaults(new ListBackupVaultsCommand({}));
const inVirginia = await simAws .account("111111111111") .region("us-east-1") .backup() .listBackupVaults(new ListBackupVaultsCommand({}));
console.log(inLondon.BackupVaultList?.length); // 1console.log(inVirginia.BackupVaultList?.length); // 0Available functionality
Section titled “Available functionality”CreateBackupVault,DescribeBackupVault,DeleteBackupVaultandListBackupVaults.PutBackupVaultLockConfiguration, including compliance lock grace periods driven by simulated time.CreateBackupPlanandGetBackupPlan, with rule schedule and lifecycle validation.CreateBackupSelection,GetBackupSelectionandListBackupSelections.- Scheduled backup jobs driven by
simAws.clock()and the plan rule schedule. StartBackupJob,ListBackupJobsandDescribeBackupJob.ListRecoveryPointsByBackupVault,DescribeRecoveryPointandsimAws.backup().vault(name).recoveryPoints().- Recovery point expiry from
DeleteAfterDaysand Vault Lock retention checks. - IAM authorization for every supported command.
- SDK interception of
BackupClient. - Account and Region scoped state, ARNs and timestamps.
AWS::Backup::BackupVault,AWS::Backup::BackupPlanandAWS::Backup::BackupSelectionthrough simulated CloudFormation.
Limitations
Section titled “Limitations”- Recovery point copies and restores are outside the simulation.
- Backup jobs complete immediately at their scheduled or requested simulated instant. Start and completion windows are accepted by the SDK types and ignored by the simulation.
- Cold-storage lifecycle values are recorded. Recovery points stay in one storage state.
- Overlapping plan rules are evaluated independently. AWS Backup’s overlapping-window optimisation is outside the simulation.
- Vault deletion removes the vault and its recovery points.
- A selection stores its
Resources.Conditions,ListOfTagsand wildcard resource matching are outside the selection model. - The selection’s
IamRoleArnis stored. No backup job assumes it, and creating a selection does noiam:PassRolecheck. EncryptionKeyArnis stored on a vault. KMS calls and recovery point encryption are outside the simulation.- Backup tags are accepted and discarded.
- List operations return every item.
MaxResultsandNextTokendo not paginate the result. GetBackupPlanreturns the one stored plan version.VersionIdis accepted and ignored. Plan updates are outside the simulation.- A compliance lock becomes immutable when its grace period ends. Deleting a lock configuration is unsupported.
Software Engineering by Kensio Software
This page as plain text: llms.txt
Documenting Yulin v1.21.1
