Skip to content

Commit 2a604e2

Browse files
committed
[#316] Add Cloudtrail modules
1 parent 37e071b commit 2a604e2

File tree

22 files changed

+615
-18
lines changed

22 files changed

+615
-18
lines changed

.github/wiki/Security.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ This document provides an overview of the security modules available in the infr
44

55
## Available Security Modules
66

7+
### CloudTrail
8+
9+
The CloudTrail module provides comprehensive API activity logging and monitoring for your AWS infrastructure to enhance security auditing and compliance.
10+
11+
#### Overview
12+
13+
AWS CloudTrail records API calls and events across your AWS account. This module:
14+
15+
- **Comprehensive event logging**: Captures management events, data events, and insight events based on configuration
16+
- **Multi-region support**: Can be configured to log events across all AWS regions for complete visibility
17+
- **CloudWatch integration**: Sends logs to CloudWatch for real-time monitoring and alerting
18+
- **SNS notifications**: Integrates with SNS topics for immediate alerting on critical events
19+
- **S3 storage**: Stores all CloudTrail logs securely in Amazon S3 with configurable key prefix organization
20+
721
### VPC Flow Log
822

923
The VPC Flow Log module captures network traffic information in your VPC to help with security monitoring and network analysis.

src/commands/install/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ describe('Install add-on command', () => {
103103
it('throws an error', async () => {
104104
expect(stdoutSpy).toHaveBeenCalledWith(
105105
expect.stringContaining(
106-
'Expected invalid to be one of: vpc, securityGroup, alb, bastion, ecr, ecs, cloudwatch, rds, s3, ssm'
106+
'Expected invalid to be one of: vpc, securityGroup, alb, bastion, cloudtrail, ecr, ecs, cloudwatch, rds, s3, ssm, vpcFlowLog'
107107
)
108108
);
109109
});

src/generators/addons/aws/advanced.test.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { remove } from '@/helpers/file';
22

3-
import { AwsOptions } from '.';
3+
import { AwsAddonModules, AwsOptions } from '.';
44
import { applyAdvancedTemplate } from './advanced';
55
import {
66
applyAwsAlb,
77
applyAwsBastion,
8+
applyAwsCloudtrail,
89
applyAwsEcr,
910
applyAwsEcs,
1011
applyAwsCloudwatch,
@@ -73,6 +74,10 @@ describe('AWS advanced template', () => {
7374
it('does NOT apply VPC Flow Log add-on', () => {
7475
expect(applyAwsVpcFlowLog).not.toHaveBeenCalled();
7576
});
77+
78+
it('does NOT apply CloudTrail add-on', () => {
79+
expect(applyAwsCloudtrail).not.toHaveBeenCalled();
80+
});
7681
});
7782

7883
describe('given enabledSecurityFeatures is true', () => {
@@ -83,7 +88,7 @@ describe('AWS advanced template', () => {
8388
infrastructureType: 'advanced',
8489
awsRegion: 'ap-southeast-1',
8590
enabledSecurityFeatures: true,
86-
addonModules: ['vpcFlowLog'],
91+
addonModules: [AwsAddonModules.VPC_FLOW_LOG],
8792
};
8893

8994
beforeAll(async () => {
@@ -102,6 +107,7 @@ describe('AWS advanced template', () => {
102107
);
103108
});
104109
});
110+
105111
describe('given addonModules does NOT include vpcFlowLog', () => {
106112
const optionsEnabledSecurityFeaturesWithoutVpcFlowLog: AwsOptions = {
107113
projectName: projectDir,
@@ -124,6 +130,58 @@ describe('AWS advanced template', () => {
124130
expect(applyAwsVpcFlowLog).not.toHaveBeenCalled();
125131
});
126132
});
133+
134+
describe('given addonModules includes cloudtrail', () => {
135+
const optionsEnabledSecurityFeaturesWithCloudTrail: AwsOptions = {
136+
projectName: projectDir,
137+
provider: 'aws',
138+
infrastructureType: 'advanced',
139+
awsRegion: 'ap-southeast-1',
140+
enabledSecurityFeatures: true,
141+
addonModules: [AwsAddonModules.CLOUDTRAIL],
142+
};
143+
144+
beforeAll(async () => {
145+
jest.clearAllMocks();
146+
await applyAdvancedTemplate(
147+
optionsEnabledSecurityFeaturesWithCloudTrail
148+
);
149+
});
150+
151+
afterAll(() => {
152+
jest.clearAllMocks();
153+
remove('/', projectDir);
154+
});
155+
156+
it('applies CloudTrail add-on when flag is set', () => {
157+
expect(applyAwsCloudtrail).toHaveBeenCalledWith(
158+
optionsEnabledSecurityFeaturesWithCloudTrail
159+
);
160+
});
161+
});
162+
163+
describe('given addonModules does NOT include cloudtrail', () => {
164+
const optionsEnabledSecurityFeaturesWithoutCloudTrail: AwsOptions = {
165+
projectName: projectDir,
166+
provider: 'aws',
167+
infrastructureType: 'advanced',
168+
awsRegion: 'ap-southeast-1',
169+
enabledSecurityFeatures: true,
170+
addonModules: [], // No cloudtrail
171+
};
172+
173+
afterAll(() => {
174+
jest.clearAllMocks();
175+
remove('/', projectDir);
176+
});
177+
178+
it('does NOT apply CloudTrail add-on when flag is set but module is not included', async () => {
179+
await applyAdvancedTemplate(
180+
optionsEnabledSecurityFeaturesWithoutCloudTrail
181+
);
182+
expect(applyAwsCloudtrail).not.toHaveBeenCalled();
183+
});
184+
});
127185
});
128186
});
129187
});

src/generators/addons/aws/advanced.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { AwsOptions } from '.';
1+
import { AwsAddonModules, AwsOptions } from '.';
22
import {
33
applyAwsAlb,
44
applyAwsBastion,
5+
applyAwsCloudtrail,
56
applyAwsEcr,
67
applyAwsEcs,
78
applyAwsCloudwatch,
@@ -25,8 +26,10 @@ const applyAdvancedTemplate = async (options: AwsOptions) => {
2526
await Promise.all(
2627
options.addonModules.map((module) => {
2728
switch (module) {
28-
case 'vpcFlowLog':
29+
case AwsAddonModules.VPC_FLOW_LOG:
2930
return applyAwsVpcFlowLog(options);
31+
case AwsAddonModules.CLOUDTRAIL:
32+
return applyAwsCloudtrail(options);
3033
default:
3134
throw new Error(`Module ${module} has not been implemented!`);
3235
}

src/generators/addons/aws/dependencies.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AwsOptions } from '@/generators/addons/aws';
55
import {
66
applyAwsAlb,
77
applyAwsBastion,
8+
applyAwsCloudtrail,
89
applyAwsEcr,
910
applyAwsEcs,
1011
applyAwsCloudwatch,
@@ -50,6 +51,12 @@ const AWS_MODULES: Record<AwsModuleName | string, AwsModule> = {
5051
mainContent: 'module "bastion"',
5152
applyModuleFunction: (options: AwsOptions) => applyAwsBastion(options),
5253
},
54+
cloudtrail: {
55+
name: 'cloudtrail',
56+
path: 'modules/cloudtrail',
57+
mainContent: 'module "cloudtrail"',
58+
applyModuleFunction: (options: AwsOptions) => applyAwsCloudtrail(options),
59+
},
5360
ecr: {
5461
name: 'ecr',
5562
path: 'modules/ecr',

src/generators/addons/aws/index.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ import {
1212
applyAwsVpc,
1313
} from './modules';
1414

15+
enum AwsAddonModules {
16+
VPC_FLOW_LOG = 'vpcFlowLog',
17+
CLOUDTRAIL = 'cloudtrail',
18+
}
19+
1520
type AwsOptions = GeneralOptions & {
1621
infrastructureType?: 'blank' | 'advanced';
1722
awsRegion?: string;
1823
enabledSecurityFeatures?: boolean;
19-
addonModules?: string[];
24+
addonModules?: AwsAddonModules[];
2025
};
2126

2227
const awsChoices = [
@@ -50,13 +55,13 @@ const awsChoices = [
5055
message: 'Which security features do you want to add?',
5156
choices: [
5257
{
53-
key: 'vpcFlowLog',
54-
value: 'vpcFlowLog',
58+
key: AwsAddonModules.VPC_FLOW_LOG,
59+
value: AwsAddonModules.VPC_FLOW_LOG,
5560
name: 'VPC Flow Logs',
5661
},
5762
{
58-
key: 'cloudTrail',
59-
value: 'cloudTrail',
63+
key: AwsAddonModules.CLOUDTRAIL,
64+
value: AwsAddonModules.CLOUDTRAIL,
6065
name: 'CloudTrail',
6166
},
6267
],
@@ -107,4 +112,4 @@ const generateAwsTemplate = async (
107112
};
108113

109114
export type { AwsOptions };
110-
export { generateAwsTemplate };
115+
export { generateAwsTemplate, AwsAddonModules };

src/generators/addons/aws/modules/alb.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import {
1111
INFRA_CORE_MAIN_PATH,
1212
INFRA_CORE_OUTPUTS_PATH,
1313
INFRA_CORE_VARIABLES_PATH,
14-
MODULES_LOCALS_INDICATOR,
1514
} from '@/generators/terraform/constants';
16-
import { appendToFile, copy, injectToFile } from '@/helpers/file';
15+
import { appendToFile, copy } from '@/helpers/file';
1716

1817
import {
1918
AWS_SECURITY_GROUP_MAIN_PATH,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { AwsOptions } from '@/generators/addons/aws';
2+
import { applyTerraformCore } from '@/generators/terraform';
3+
import { remove } from '@/helpers/file';
4+
5+
import applyAwsCloudtrail, {
6+
cloudtrailModuleContent,
7+
cloudtrailOutputsContent,
8+
cloudtrailVariablesContent,
9+
} from './cloudtrail';
10+
import applyTerraformAwsProvider from './core/provider';
11+
12+
jest.mock('inquirer', () => {
13+
return {
14+
prompt: jest.fn().mockResolvedValue({ apply: true }),
15+
};
16+
});
17+
18+
describe('CloudTrail add-on', () => {
19+
describe('given valid AWS options', () => {
20+
const projectDir = 'cloudtrail-addon-test';
21+
22+
beforeAll(async () => {
23+
const awsOptions: AwsOptions = {
24+
projectName: projectDir,
25+
provider: 'aws',
26+
infrastructureType: 'advanced',
27+
};
28+
29+
await applyTerraformCore(awsOptions);
30+
await applyTerraformAwsProvider(awsOptions);
31+
await applyAwsCloudtrail(awsOptions);
32+
});
33+
34+
afterAll(() => {
35+
jest.clearAllMocks();
36+
remove('/', projectDir);
37+
});
38+
39+
it('creates expected files', () => {
40+
const expectedFiles = [
41+
'core/main.tf',
42+
'core/providers.tf',
43+
'core/outputs.tf',
44+
'core/variables.tf',
45+
'modules/cloudtrail/main.tf',
46+
'modules/cloudtrail/variables.tf',
47+
'modules/cloudtrail/outputs.tf',
48+
];
49+
50+
expect(projectDir).toHaveFiles(expectedFiles);
51+
});
52+
53+
it('adds cloudtrail module to main.tf', () => {
54+
expect(projectDir).toHaveContentInFile(
55+
'core/main.tf',
56+
cloudtrailModuleContent
57+
);
58+
});
59+
60+
it('adds cloudtrail variables to variables.tf', () => {
61+
expect(projectDir).toHaveContentInFile(
62+
'core/variables.tf',
63+
cloudtrailVariablesContent
64+
);
65+
});
66+
67+
it('adds cloudtrail outputs to outputs.tf', () => {
68+
expect(projectDir).toHaveContentInFile(
69+
'core/outputs.tf',
70+
cloudtrailOutputsContent
71+
);
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)