Skip to content
This repository was archived by the owner on Dec 31, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions reference-artifacts/Custom-Scripts/lza-upgrade/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tabWidth": 2,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"lza-prep": "ts-node src/index.ts lza-prep",
"post-migration": "ts-node src/index.ts post-migration",
"migration-config": "ts-node src/index migration-config",
"disable-subscription-rules": "ts-node src/index disable-rules",
"all": "ts-node src/index migration-config; ts-node src/index snapshot pre"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@

import { DynamoDB } from '../aws/dynamodb';
import { Account } from '../outputs/accounts';
import { STS } from '../aws/sts';


export interface Environment {
accountId: string;
accountKey: string;
region: string;
}

export async function loadAccounts(tableName: string, client: DynamoDB): Promise<Account[]> {
let index = 0;
Expand All @@ -31,3 +39,22 @@ export async function loadAccounts(tableName: string, client: DynamoDB): Promise
}
return accounts;
}

export function getEnvironments(accounts: Account[], regions: string[]): Environment[] {
const environments: Environment[] = [];
for (const account of accounts) {
for (const region of regions) {
environments.push({
accountId: account.id,
accountKey: account.key,
region,
});
}
}
return environments;
}

export async function assumeRole(accountId: string, roleName: string) {
const sts = new STS();
return sts.getCredentialsForAccountAndRole(accountId, roleName);
}
105 changes: 105 additions & 0 deletions reference-artifacts/Custom-Scripts/lza-upgrade/src/disable-rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

import { Account } from './common/outputs/accounts';
import { DynamoDB } from './common/aws/dynamodb';
import { Environment, getEnvironments, loadAccounts } from './common/utils/accounts';
import { loadAseaConfig } from './asea-config/load';
import { Config } from './config';
import { AcceleratorConfig } from './asea-config';
import { STS } from './common/aws/sts';
import { throttlingBackOff } from './common/aws/backoff';
import { EventBridgeClient, DisableRuleCommand, ResourceNotFoundException } from '@aws-sdk/client-eventbridge';

export class DisableRules {
homeRegion: string;
assumeRoleName: string;
configRepositoryName: string;
accountList: Account[];
enabledRegions: string[];
sts: STS;
constructor(config: Config,
disableRuleConfig: { accountList: Account[], enabledRegions: string[], acceleratorConfig: AcceleratorConfig },
) {
this.homeRegion = config.homeRegion;
this.sts = new STS();
this.assumeRoleName = config.assumeRoleName ?? 'OrganizationAccountAccessRole';
this.configRepositoryName = config.repositoryName;
this.accountList = disableRuleConfig.accountList;
this.enabledRegions = disableRuleConfig.enabledRegions;
}

static async init(config: Config) {
const accountList = await loadAccounts(config.parametersTableName, new DynamoDB(undefined, config.homeRegion));
const acceleratorConfig = await loadAseaConfig({
filePath: 'raw/config.json',
repositoryName: config.repositoryName,
defaultRegion: config.homeRegion,
});
const enabledRegions = acceleratorConfig['global-options']['supported-regions'];
const disableRules = new DisableRules(config, {accountList, enabledRegions, acceleratorConfig});
return disableRules;
}
async disableAllAccountRules(prefix: string) {
const environments = getEnvironments(this.accountList, this.enabledRegions);
const eventBridgeClientMap = await this.getEventBridgeClientMap(environments);
const deleteRulesPromises = environments.map(environment => this.disableEventBridgeRules(eventBridgeClientMap, environment.accountId, environment.region, prefix.replaceAll('-', '')));
await Promise.all(deleteRulesPromises);
}

async disableEventBridgeRules(eventBridgeClientMap: Map<string, EventBridgeClient>, accountId: string, region: string, prefix: string,){
const client = eventBridgeClientMap.get(`${accountId}-${region}`);
if (!client) {
throw new Error(`No client found for account ${accountId} and region ${region}`);
};
const suffixes = [
'NewLogGroup_rule'
];
for (const suffix of suffixes) {
try {
const command = new DisableRuleCommand({
Name: `${prefix}-${suffix}`,
});
await throttlingBackOff (() => client.send(command));
} catch (e: any) {
if (e instanceof ResourceNotFoundException) {
continue;
}
}
console.log(`Disabling rule ${prefix}-${suffix} in ${accountId}-${region}`);
}
}

async getEventBridgeClientMap(environments: Environment[]) {
const eventBridgeClientMap = new Map<string, EventBridgeClient>();
const promises = [];
for (const environment of environments) {
promises.push(this.createEventBridgeClients(this.assumeRoleName, environment.accountId, environment.region));
}
const eventBridgeClients = await Promise.all(promises);
eventBridgeClients.forEach((client) => {
eventBridgeClientMap.set(`${client.accountId}-${client.region}`, client.client);
});
return eventBridgeClientMap;
}

async createEventBridgeClients(assumeRoleName: string, accountId: string, region: string) {
const credentials = await this.sts.getCredentialsForAccountAndRole(accountId, assumeRoleName);
const client = new EventBridgeClient({ credentials, region });
return {
accountId,
region,
client,
};
}
}
7 changes: 7 additions & 0 deletions reference-artifacts/Custom-Scripts/lza-upgrade/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { PostMigration } from './post-migration';
import { Preparation } from './preparation';
import { ResourceMapping } from './resource-mapping';
import { Snapshot } from './snapshot';
import { DisableRules } from './disable-rules';

async function main() {
const args = process.argv.slice(2);
Expand Down Expand Up @@ -49,6 +50,8 @@ async function main() {
case 'asea-prep':
const preparation = new Preparation(config);
await preparation.prepareAsea();
const disableRulesInPrep = await DisableRules.init(config);
await disableRulesInPrep.disableAllAccountRules(config.aseaPrefix);
break;
case 'lza-prep':
const lzaPreparation = new Preparation(config);
Expand All @@ -71,6 +74,10 @@ async function main() {
break;
}
break;
case 'disable-rules':
const disableRules = await DisableRules.init(config);
await disableRules.disableAllAccountRules(config.aseaPrefix);
break;
case 'post-migration':
await new PostMigration(config, args).process();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export async function disableASEARules(prefix: string) {
'CreateOrganizationalUnit_rule',
'MoveAccount_rule',
'PolicyChanges_rule',
'RemoveAccount_rule',
'RemoveAccount_rule'
];

const disableRuleCommands: DisableRuleCommand[] = suffixes.map((suffix) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export async function snapshotConfiguration(
}
}

async function getCredentials(accountId: string, roleName: string): Promise<AwsCredentialIdentity | undefined> {
export async function getCredentials(accountId: string, roleName: string): Promise<AwsCredentialIdentity | undefined> {
const stsClient = new STSClient({ maxAttempts: 10 });
let stsResponse: AssumeRoleCommandOutput;
try {
stsResponse = await stsClient.send(
Expand All @@ -105,13 +106,13 @@ async function getCredentials(accountId: string, roleName: string): Promise<AwsC
sessionToken: stsResponse.Credentials?.SessionToken!,
};
return credentials;
} catch (e) {
} catch (e: any) {
console.error(`Failed to assume role ${roleName} in account ${accountId}`);
return undefined;
throw new Error(e);
}
}

async function getAccountList(): Promise<Account[]> {
export async function getAccountList(): Promise<Account[]> {
const organizationsClient = new OrganizationsClient({ region: 'us-east-1', maxAttempts: 10 });

const accounts: Account[] = [];
Expand Down
Loading