seahaven-org-baseline/lib/paychex-integrations-hcptf-stack.ts
Adam Moussa 227a5d91a2
Some checks are pending
Deploy / deploy-management (push) Waiting to run
Deploy / deploy-external-dev (push) Waiting to run
Deploy / deploy-security (push) Waiting to run
Deploy / deploy-dev (push) Waiting to run
Deploy / deploy-prod (push) Waiting to run
feat(hcptf): import hcptf-paychex-integrations apply and plan roles into seahaven-hcptf (PLAT-251) (#179)
* feat(hcptf): import hcptf-paychex-integrations apply and plan roles into seahaven-hcptf (PLAT-251)

PaychexIntegrationsRoles is nested in the prod seahaven-hcptf stack for the
paychex-integrations-prod workspace. The two roles already exist and are
imported with -c hcptfPaychexImport=true, which names the live inline
policies and omits role tags and outputs. The default template replaces the
inline policies with managed policies at /tf-managed/:

- paychex-integrations-hcptf-iam: the ported scoped IAM document plus
  CreateRole and the write set on tf-managed/githubdeploy-paychex-integrations
  (no permissions boundary) and iam:GetOpenIDConnectProvider. TagHcptfRoles is
  dropped; the roles are no longer Terraform-managed.
- paychex-integrations-hcptf-services: the ported services document plus SSM
  writes on /paychex-integrations/deploy/* and DescribeParameters.
- paychex-integrations-hcptf-plan: the ported refresh document plus the deploy
  role, the GitHub OIDC provider, and the deploy parameters.

Trust is unchanged. Every resource is Retain.

* docs(hcptf): describe the paychex-integrations import as a sequence

* chore(ci): retrigger checks after the GitHub Actions incident
2026-10-05 21:53:58 +00:00

688 lines
23 KiB
TypeScript

import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
/**
* Prod exec roles for the paychex-integrations HCP workspace (PLAT-251).
*
* Nested in the prod seahaven-hcptf stack. `hcptf-paychex-integrations` and
* `hcptf-paychex-integrations-plan` already exist. They were created by
* create-hcptf-bootstrap-roles.sh and then managed by the paychex-integrations
* workspace itself through a bootstrap credential swap. That workspace forgets
* them with `removed` blocks before this construct imports them. Do not create
* them. A plain create fails because the roles already exist.
*
* Import identifiers are the two role names. Construct ids stay ApplyRole and
* PlanRole under PaychexIntegrations. The three /tf-managed/ managed policies
* do not exist before the deploy that follows the import.
*
* `importExisting` is the `-c hcptfPaychexImport=true` template. It names the
* roles' live inline policies (`paychex-integrations-services`,
* `scoped-iam-management`, `paychex-integrations-plan-refresh`) so the
* following deploy can delete them, and it omits role tags and the role ARN
* outputs. CloudFormation rejects both on an IAM role import. The default
* template is the managed-policy state.
*
* Beyond the ported documents, the apply role can create and manage
* `githubdeploy-paychex-integrations` at /tf-managed/ with no permissions
* boundary, and can write the deploy contract under SSM
* /paychex-integrations/deploy/*. The plan role can refresh both.
*/
export interface PaychexIntegrationsRolesProps {
/** Synthesize the import template. Set from `-c hcptfPaychexImport=true`. */
importExisting?: boolean;
}
const VIEW_ONLY = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess";
const WORKSPACE =
"organization:seahaven:project:seahaven-prod:workspace:paychex-integrations-prod";
export class PaychexIntegrationsRoles extends Construct {
constructor(scope: Construct, id: string, props: PaychexIntegrationsRolesProps = {}) {
super(scope, id);
const importing = props.importExisting === true;
// Overrides the parent stack's Project=payments-dashboard tag.
cdk.Tags.of(this).add("Project", "paychex-integrations", { priority: 200 });
if (importing) {
const roleOnly = { priority: 300, includeResourceTypes: ["AWS::IAM::Role"] };
cdk.Tags.of(this).remove("Project", roleOnly);
cdk.Tags.of(this).remove("Owner", roleOnly);
cdk.Tags.of(this).remove("ManagedBy", roleOnly);
}
const account = cdk.Stack.of(this).account;
const hcpOidc = `arn:aws:iam::${account}:oidc-provider/app.terraform.io`;
const deployRole = `arn:aws:iam::${account}:role/tf-managed/githubdeploy-paychex-integrations`;
const deployParams = `arn:aws:ssm:us-east-1:${account}:parameter/paychex-integrations/deploy/*`;
const iamDocument = scopedIamPolicy(account, deployRole);
const servicesDocument = servicesPolicy(account, deployParams);
const planDocument = planPolicy(account, deployRole, deployParams);
const apply = new iam.CfnRole(this, "ApplyRole", {
roleName: "hcptf-paychex-integrations",
maxSessionDuration: 3600,
assumeRolePolicyDocument: trust(hcpOidc, "apply"),
...(importing
? {
policies: [
{ policyName: "paychex-integrations-services", policyDocument: servicesDocument },
{ policyName: "scoped-iam-management", policyDocument: iamDocument },
],
}
: {
managedPolicyArns: [
managedPolicy(this, "IamPolicy", "paychex-integrations-hcptf-iam", iamDocument).ref,
managedPolicy(
this,
"ServicesPolicy",
"paychex-integrations-hcptf-services",
servicesDocument,
).ref,
],
tags: roleTags(),
}),
});
retain(apply);
const plan = new iam.CfnRole(this, "PlanRole", {
roleName: "hcptf-paychex-integrations-plan",
maxSessionDuration: 3600,
assumeRolePolicyDocument: trust(hcpOidc, "plan"),
...(importing
? {
managedPolicyArns: [VIEW_ONLY],
policies: [
{ policyName: "paychex-integrations-plan-refresh", policyDocument: planDocument },
],
}
: {
managedPolicyArns: [
VIEW_ONLY,
managedPolicy(this, "PlanPolicy", "paychex-integrations-hcptf-plan", planDocument).ref,
],
tags: roleTags(),
}),
});
retain(plan);
if (!importing) {
const applyArn = new cdk.CfnOutput(this, "ApplyRoleArn", { value: apply.attrArn });
const planArn = new cdk.CfnOutput(this, "PlanRoleArn", { value: plan.attrArn });
applyArn.overrideLogicalId("PaychexIntegrationsApplyRoleArn");
planArn.overrideLogicalId("PaychexIntegrationsPlanRoleArn");
}
}
}
function managedPolicy(
scope: Construct,
id: string,
name: string,
policyDocument: object,
): iam.CfnManagedPolicy {
const policy = new iam.CfnManagedPolicy(scope, id, {
managedPolicyName: name,
path: "/tf-managed/",
policyDocument,
});
retain(policy);
return policy;
}
function retain(resource: cdk.CfnResource): void {
resource.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
resource.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
}
function roleTags(): cdk.CfnTag[] {
return [
{ key: "Project", value: "paychex-integrations" },
{ key: "Owner", value: "adam@seahavenind.com" },
{ key: "ManagedBy", value: "cdk" },
];
}
function trust(providerArn: string, phase: "apply" | "plan"): object {
return {
Version: "2012-10-17",
Statement: [
{
Sid: phase === "apply" ? "HcpApply" : "HcpPlan",
Effect: "Allow",
Action: "sts:AssumeRoleWithWebIdentity",
Principal: { Federated: providerArn },
Condition: {
StringEquals: {
"app.terraform.io:aud": "aws.workload.identity",
"app.terraform.io:sub": `${WORKSPACE}:run_phase:${phase}`,
},
},
},
],
};
}
/** Ported from paychex-integrations terraform/hcp_iam.tf hcptf_scoped_iam plus the deploy role. */
function scopedIamPolicy(account: string, deployRole: string): object {
const execRoles = `arn:aws:iam::${account}:role/tf-managed/paychex-*`;
const execBoundaries = [
`arn:aws:iam::${account}:policy/tf-managed/paychex-*`,
`arn:aws:iam::${account}:policy/seahaven-lambda-execution-boundary-paychex-integrations`,
];
return {
Version: "2012-10-17",
Statement: [
{
Sid: "DenyCreatePolicy",
Effect: "Deny",
Action: ["iam:CreatePolicy", "iam:CreatePolicyVersion"],
Resource: "*",
},
{
Sid: "CreateExecRoleWithBoundary",
Effect: "Allow",
Action: "iam:CreateRole",
Resource: execRoles,
Condition: { StringLike: { "iam:PermissionsBoundary": execBoundaries } },
},
{
Sid: "MutateExecRoleWithBoundary",
Effect: "Allow",
Action: ["iam:AttachRolePolicy", "iam:PutRolePolicy", "iam:PutRolePermissionsBoundary"],
Resource: execRoles,
Condition: { StringLike: { "iam:PermissionsBoundary": execBoundaries } },
},
{
Sid: "WriteExecRoles",
Effect: "Allow",
Action: [
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription",
],
Resource: execRoles,
},
{
Sid: "PassExecRolesToLambda",
Effect: "Allow",
Action: "iam:PassRole",
Resource: execRoles,
Condition: { StringEquals: { "iam:PassedToService": "lambda.amazonaws.com" } },
},
{
Sid: "PassPayrollScheduleToScheduler",
Effect: "Allow",
Action: "iam:PassRole",
Resource: `arn:aws:iam::${account}:role/tf-managed/paychex-payroll-schedule-invoke`,
Condition: { StringEquals: { "iam:PassedToService": "scheduler.amazonaws.com" } },
},
{
// GitHub Actions deploy role, owned by the workspace. Path /tf-managed/
// keeps it outside DenySelfMutation's role/githubdeploy-* pattern. No
// permissions boundary: it is not a Lambda execution role.
Sid: "CreateDeployRole",
Effect: "Allow",
Action: "iam:CreateRole",
Resource: deployRole,
Condition: { Null: { "iam:PermissionsBoundary": "true" } },
},
{
Sid: "WriteDeployRole",
Effect: "Allow",
Action: [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription",
],
Resource: deployRole,
},
{
Sid: "IamReadOnly",
Effect: "Allow",
Action: [
"iam:GetOpenIDConnectProvider",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:ListPolicies",
"iam:ListPolicyTags",
"iam:ListPolicyVersions",
"iam:ListRolePolicies",
"iam:ListRoles",
"iam:ListRoleTags",
],
Resource: "*",
},
{
Sid: "DenySelfMutation",
Effect: "Deny",
Action: [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteRolePermissionsBoundary",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription",
],
Resource: [
`arn:aws:iam::${account}:role/hcptf-*`,
`arn:aws:iam::${account}:role/github-cfn-execution-role`,
`arn:aws:iam::${account}:role/githubdeploy-*`,
`arn:aws:iam::${account}:role/cdk-hnb659fds-*`,
`arn:aws:iam::${account}:role/OrganizationAccountAccessRole`,
`arn:aws:iam::${account}:role/seahaven-*`,
],
},
{
Sid: "DenyBoundaryTampering",
Effect: "Deny",
Action: ["iam:DeleteRolePermissionsBoundary", "iam:DeleteUserPermissionsBoundary"],
Resource: [`arn:aws:iam::${account}:role/*`, `arn:aws:iam::${account}:user/*`],
},
{
Sid: "DenyBoundaryPolicyEdit",
Effect: "Deny",
Action: [
"iam:CreatePolicyVersion",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:SetDefaultPolicyVersion",
],
Resource: `arn:aws:iam::${account}:policy/seahaven-*`,
},
],
};
}
/** Ported from paychex-integrations terraform/hcp_iam.tf hcptf_apply_services plus the deploy contract. */
function servicesPolicy(account: string, deployParams: string): object {
const functions = `arn:aws:lambda:us-east-1:${account}:function:paychex-*`;
const artifacts = `arn:aws:s3:::paychex-integrations-artifacts-${account}`;
return {
Version: "2012-10-17",
Statement: [
{
Sid: "LambdaAll",
Effect: "Allow",
Action: "lambda:*",
Resource: functions,
},
{
Sid: "LambdaEventSourceMappingRead",
Effect: "Allow",
Action: [
"lambda:GetEventSourceMapping",
"lambda:ListTags",
"lambda:TagResource",
"lambda:UntagResource",
],
Resource: "*",
},
{
Sid: "LambdaEventSourceMappings",
Effect: "Allow",
Action: [
"lambda:CreateEventSourceMapping",
"lambda:DeleteEventSourceMapping",
"lambda:UpdateEventSourceMapping",
],
Resource: "*",
Condition: { "ForAnyValue:StringLike": { "lambda:FunctionArn": functions } },
},
{
Sid: "LambdaList",
Effect: "Allow",
Action: [
"lambda:ListFunctions",
"lambda:ListLayers",
"lambda:ListEventSourceMappings",
"lambda:GetAccountSettings",
],
Resource: "*",
},
{
Sid: "CloudWatchLogs",
Effect: "Allow",
Action: [
"logs:CreateLogGroup",
"logs:DeleteLogGroup",
"logs:PutRetentionPolicy",
"logs:DeleteRetentionPolicy",
"logs:TagResource",
"logs:UntagResource",
"logs:ListTagsForResource",
],
Resource: [
`arn:aws:logs:us-east-1:${account}:log-group:/aws/lambda/paychex-*`,
`arn:aws:logs:us-east-1:${account}:log-group:/aws/apigateway/paychex-webhooks`,
`arn:aws:logs:us-east-1:${account}:log-group:/aws/apigateway/paychex-webhooks:*`,
],
},
{
Sid: "CloudWatchLogsDescribe",
Effect: "Allow",
Action: "logs:DescribeLogGroups",
Resource: "*",
},
{
// HTTP API access logging is delivered through CloudWatch vended logs.
// None of these actions accept a resource ARN.
Sid: "CloudWatchLogsDelivery",
Effect: "Allow",
Action: [
"logs:CreateLogDelivery",
"logs:GetLogDelivery",
"logs:UpdateLogDelivery",
"logs:DeleteLogDelivery",
"logs:ListLogDeliveries",
"logs:PutResourcePolicy",
"logs:DescribeResourcePolicies",
],
Resource: "*",
},
{
Sid: "LambdaArtifactsBucket",
Effect: "Allow",
Action: "s3:*",
Resource: [artifacts, `${artifacts}/*`],
},
{
Sid: "CloudWatchAlarms",
Effect: "Allow",
Action: "cloudwatch:*",
Resource: `arn:aws:cloudwatch:us-east-1:${account}:alarm:paychex-*`,
},
{
Sid: "SiteAlertsSns",
Effect: "Allow",
Action: ["sns:Publish", "sns:GetTopicAttributes"],
Resource: `arn:aws:sns:us-east-1:${account}:site-alerts`,
},
{
Sid: "PaychexSecretShell",
Effect: "Allow",
Action: [
"secretsmanager:DeleteSecret",
"secretsmanager:DescribeSecret",
"secretsmanager:GetResourcePolicy",
"secretsmanager:PutResourcePolicy",
"secretsmanager:DeleteResourcePolicy",
"secretsmanager:TagResource",
"secretsmanager:UntagResource",
],
Resource: `arn:aws:secretsmanager:us-east-1:${account}:secret:paychex-integrations/*`,
},
{
Sid: "PaychexSecretCreate",
Effect: "Allow",
Action: "secretsmanager:CreateSecret",
Resource: "*",
Condition: { StringLike: { "secretsmanager:Name": "paychex-integrations/*" } },
},
{
Sid: "DynamoDBTable",
Effect: "Allow",
Action: "dynamodb:*",
Resource: [
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-worker-ledger`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-worker-ledger/index/*`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-webhook-notifications`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-webhook-notifications/index/*`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-payroll-notices`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-payroll-notices/index/*`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-posted`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-posted/index/*`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-period`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-period/index/*`,
],
},
{
Sid: "DynamoDBList",
Effect: "Allow",
Action: "dynamodb:ListTables",
Resource: "*",
},
{
Sid: "SqsQueues",
Effect: "Allow",
Action: "sqs:*",
Resource: queueArns(account),
},
{
Sid: "SqsList",
Effect: "Allow",
Action: "sqs:ListQueues",
Resource: "*",
},
{
Sid: "HttpApi",
Effect: "Allow",
Action: "apigateway:*",
Resource: [
"arn:aws:apigateway:us-east-1::/apis",
"arn:aws:apigateway:us-east-1::/apis/*",
"arn:aws:apigateway:us-east-1::/tags/*",
],
},
{
Sid: "PayrollSchedules",
Effect: "Allow",
Action: [
"scheduler:CreateSchedule",
"scheduler:UpdateSchedule",
"scheduler:DeleteSchedule",
"scheduler:GetSchedule",
"scheduler:ListTagsForResource",
"scheduler:TagResource",
"scheduler:UntagResource",
],
Resource: scheduleArns(account),
},
{
// Deploy contract read by the thin deploy.yaml caller.
Sid: "WriteDeployContract",
Effect: "Allow",
Action: [
"ssm:AddTagsToResource",
"ssm:DeleteParameter",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListTagsForResource",
"ssm:PutParameter",
"ssm:RemoveTagsFromResource",
],
Resource: deployParams,
},
{
// DescribeParameters accepts only Resource "*".
Sid: "DescribeParameters",
Effect: "Allow",
Action: "ssm:DescribeParameters",
Resource: "*",
},
],
};
}
/** Ported from paychex-integrations terraform/hcp_iam.tf hcptf_plan_refresh plus the deploy role and contract. */
function planPolicy(account: string, deployRole: string, deployParams: string): object {
const artifacts = `arn:aws:s3:::paychex-integrations-artifacts-${account}`;
return {
Version: "2012-10-17",
Statement: [
{
Sid: "RefreshIamRoles",
Effect: "Allow",
Action: [
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListRolePolicies",
"iam:ListAttachedRolePolicies",
],
Resource: [
`arn:aws:iam::${account}:role/tf-managed/paychex-*`,
deployRole,
`arn:aws:iam::${account}:role/hcptf-paychex-integrations`,
`arn:aws:iam::${account}:role/hcptf-paychex-integrations-plan`,
],
},
{
Sid: "RefreshGithubOidcProvider",
Effect: "Allow",
Action: "iam:GetOpenIDConnectProvider",
Resource: `arn:aws:iam::${account}:oidc-provider/token.actions.githubusercontent.com`,
},
{
Sid: "RefreshManagedPolicies",
Effect: "Allow",
Action: ["iam:GetPolicy", "iam:GetPolicyVersion"],
Resource: "*",
},
{
Sid: "RefreshLambda",
Effect: "Allow",
Action: "lambda:Get*",
Resource: `arn:aws:lambda:us-east-1:${account}:function:paychex-*`,
},
{
Sid: "RefreshLambdaList",
Effect: "Allow",
Action: [
"lambda:ListFunctions",
"lambda:ListEventSourceMappings",
"lambda:GetEventSourceMapping",
"lambda:GetAccountSettings",
],
Resource: "*",
},
{
Sid: "RefreshArtifactsBucket",
Effect: "Allow",
Action: ["s3:Get*", "s3:ListBucket"],
Resource: [artifacts, `${artifacts}/*`],
},
{
Sid: "RefreshCloudWatchAlarms",
Effect: "Allow",
Action: ["cloudwatch:DescribeAlarms", "cloudwatch:ListTagsForResource"],
Resource: `arn:aws:cloudwatch:us-east-1:${account}:alarm:paychex-*`,
},
{
Sid: "RefreshLogs",
Effect: "Allow",
Action: ["logs:DescribeLogGroups", "logs:ListTagsForResource"],
Resource: "*",
},
{
Sid: "RefreshSecrets",
Effect: "Allow",
Action: [
"secretsmanager:DescribeSecret",
"secretsmanager:GetResourcePolicy",
"secretsmanager:ListSecretVersionIds",
],
Resource: `arn:aws:secretsmanager:us-east-1:${account}:secret:paychex-integrations/*`,
},
{
Sid: "RefreshDynamoDB",
Effect: "Allow",
Action: [
"dynamodb:DescribeTable",
"dynamodb:DescribeTimeToLive",
"dynamodb:DescribeContinuousBackups",
"dynamodb:ListTagsOfResource",
],
Resource: [
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-worker-ledger`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-webhook-notifications`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-payroll-notices`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-posted`,
`arn:aws:dynamodb:us-east-1:${account}:table/paychex-checkcomponents-period`,
],
},
{
Sid: "RefreshSqs",
Effect: "Allow",
Action: ["sqs:GetQueueAttributes", "sqs:GetQueueUrl", "sqs:ListQueueTags"],
Resource: queueArns(account),
},
{
Sid: "RefreshSqsList",
Effect: "Allow",
Action: "sqs:ListQueues",
Resource: "*",
},
{
Sid: "RefreshHttpApi",
Effect: "Allow",
Action: "apigateway:GET",
Resource: [
"arn:aws:apigateway:us-east-1::/apis",
"arn:aws:apigateway:us-east-1::/apis/*",
"arn:aws:apigateway:us-east-1::/tags/*",
],
},
{
Sid: "RefreshPayrollSchedules",
Effect: "Allow",
Action: ["scheduler:GetSchedule", "scheduler:ListTagsForResource"],
Resource: scheduleArns(account),
},
{
Sid: "RefreshDeployContract",
Effect: "Allow",
Action: ["ssm:GetParameter", "ssm:GetParameters", "ssm:ListTagsForResource"],
Resource: deployParams,
},
{
// DescribeParameters accepts only Resource "*". The AWS provider
// calls it while refreshing aws_ssm_parameter.
Sid: "DescribeParameters",
Effect: "Allow",
Action: "ssm:DescribeParameters",
Resource: "*",
},
],
};
}
function queueArns(account: string): string[] {
return [
"paychex-webhook-events",
"paychex-webhook-events-dlq",
"paychex-login-delay",
"paychex-login-delay-dlq",
"paychex-checkcomponents",
"paychex-checkcomponents-dlq",
"paychex-payroll-schedule",
"paychex-payroll-schedule-dlq",
].map((name) => `arn:aws:sqs:us-east-1:${account}:${name}`);
}
function scheduleArns(account: string): string[] {
return [
`arn:aws:scheduler:us-east-1:${account}:schedule/default/paychex-payroll-monday`,
`arn:aws:scheduler:us-east-1:${account}:schedule/default/paychex-payroll-thursday`,
];
}