Skip to content

Commit ac6208d

Browse files
committedMay 6, 2021
fix: tf plan full scan
1 parent 7bc9229 commit ac6208d

19 files changed

+5338
-587
lines changed
 

‎src/cli/commands/test/iac-local-execution/parsers/terraform-plan-parser.ts

+22-46
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
TerraformPlanJson,
66
TerraformPlanResource,
77
ResourceActions,
8-
VALID_RESOURCE_ACTIONS,
8+
VALID_RESOURCE_ACTIONS_FOR_DELTA_SCAN,
9+
VALID_RESOURCE_ACTIONS_FOR_FULL_SCAN,
910
TerraformScanInput,
1011
TerraformPlanResourceChange,
1112
IaCErrorCodes,
@@ -35,19 +36,26 @@ function terraformPlanReducer(
3536
function resourceChangeReducer(
3637
scanInput: TerraformScanInput,
3738
resource: TerraformPlanResourceChange,
39+
isFullScan: boolean,
3840
): TerraformScanInput {
3941
// TODO: investigate if we need to address also `after_unknown` field.
4042
const { actions, after } = resource.change || { actions: [], after: {} };
41-
if (isValidResourceActions(actions)) {
43+
if (isValidResourceActions(actions, isFullScan)) {
4244
const resourceForReduction = { ...resource, values: after || {} };
4345
return terraformPlanReducer(scanInput, resourceForReduction);
4446
}
4547

4648
return scanInput;
4749
}
4850

49-
function isValidResourceActions(action: ResourceActions): boolean {
50-
return VALID_RESOURCE_ACTIONS.some((validAction: string[]) => {
51+
function isValidResourceActions(
52+
action: ResourceActions,
53+
isFullScan: boolean,
54+
): boolean {
55+
const VALID_ACTIONS = isFullScan
56+
? VALID_RESOURCE_ACTIONS_FOR_FULL_SCAN
57+
: VALID_RESOURCE_ACTIONS_FOR_DELTA_SCAN;
58+
return VALID_ACTIONS.some((validAction: string[]) => {
5159
if (action.length !== validAction.length) {
5260
return false;
5361
}
@@ -57,56 +65,28 @@ function isValidResourceActions(action: ResourceActions): boolean {
5765
});
5866
}
5967

60-
function extractRootModuleResources(
61-
terraformPlanJson: TerraformPlanJson,
62-
): Array<TerraformPlanResource> {
63-
return terraformPlanJson.planned_values?.root_module?.resources || [];
64-
}
65-
66-
function extractChildModulesResources(
67-
terraformPlanJson: TerraformPlanJson,
68-
): Array<TerraformPlanResource> {
69-
const childModules =
70-
terraformPlanJson?.planned_values?.root_module?.child_modules || [];
71-
const extractedChildModuleResources: Array<TerraformPlanResource> = [];
72-
childModules.forEach((childModule) =>
73-
childModule.resources.forEach((resource) =>
74-
extractedChildModuleResources.push(resource),
75-
),
76-
);
77-
return extractedChildModuleResources;
78-
}
79-
8068
function extractResourceChanges(
8169
terraformPlanJson: TerraformPlanJson,
8270
): Array<TerraformPlanResourceChange> {
8371
return terraformPlanJson.resource_changes || [];
8472
}
8573

86-
function extractResourcesForFullScan(
87-
terraformPlanJson: TerraformPlanJson,
88-
): TerraformScanInput {
89-
const rootModuleResources = extractRootModuleResources(terraformPlanJson);
90-
const childModuleResources = extractChildModulesResources(terraformPlanJson);
91-
return [
92-
...rootModuleResources,
93-
...childModuleResources,
94-
].reduce(terraformPlanReducer, { resource: {}, data: {} });
95-
}
96-
97-
function extractResourcesForDeltaScan(
74+
function extractResourcesForScan(
9875
terraformPlanJson: TerraformPlanJson,
76+
isFullScan = false,
9977
): TerraformScanInput {
10078
const resourceChanges = extractResourceChanges(terraformPlanJson);
101-
return resourceChanges.reduce(resourceChangeReducer, {
102-
resource: {},
103-
data: {},
104-
});
79+
return resourceChanges.reduce(
80+
(memo, curr) => resourceChangeReducer(memo, curr, isFullScan),
81+
{
82+
resource: {},
83+
data: {},
84+
},
85+
);
10586
}
10687

10788
export function isTerraformPlan(terraformPlanJson: TerraformPlanJson): boolean {
10889
const missingRequiredFields =
109-
!terraformPlanJson.planned_values?.root_module ||
11090
terraformPlanJson.resource_changes === undefined;
11191
return !missingRequiredFields;
11292
}
@@ -117,14 +97,10 @@ export function tryParsingTerraformPlan(
11797
{ isFullScan }: { isFullScan: boolean } = { isFullScan: false },
11898
): Array<IacFileParsed> {
11999
try {
120-
const scannableInput = isFullScan
121-
? extractResourcesForFullScan(terraformPlanJson)
122-
: extractResourcesForDeltaScan(terraformPlanJson);
123-
124100
return [
125101
{
126102
...terraformPlanFile,
127-
jsonContent: scannableInput,
103+
jsonContent: extractResourcesForScan(terraformPlanJson, isFullScan),
128104
engineType: EngineType.Terraform,
129105
},
130106
];

‎src/cli/commands/test/iac-local-execution/types.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,6 @@ export interface TerraformPlanResourceChange
170170

171171
export interface TerraformPlanJson {
172172
// there are more values, but these are the required ones for us to scan
173-
planned_values: {
174-
root_module: {
175-
resources: Array<TerraformPlanResource>;
176-
child_modules: Array<{ resources: Array<TerraformPlanResource> }>;
177-
};
178-
};
179173
resource_changes: Array<TerraformPlanResourceChange>;
180174
}
181175
export interface TerraformScanInput {
@@ -195,13 +189,19 @@ export type ResourceActions =
195189
| ['delete'];
196190

197191
// we will be scanning the `create` & `update` actions only.
198-
export const VALID_RESOURCE_ACTIONS: ResourceActions[] = [
192+
export const VALID_RESOURCE_ACTIONS_FOR_DELTA_SCAN: ResourceActions[] = [
199193
['create'],
200194
['update'],
201195
['create', 'delete'],
202196
['delete', 'create'],
203197
];
204198

199+
// scans all actions including 'no-op' in order to iterate on all resources.
200+
export const VALID_RESOURCE_ACTIONS_FOR_FULL_SCAN: ResourceActions[] = [
201+
['no-op'],
202+
...VALID_RESOURCE_ACTIONS_FOR_DELTA_SCAN,
203+
];
204+
205205
// Error codes used for Analytics & Debugging
206206
// Within a single module, increments are in 1.
207207
// Between modules, increments are in 10, according to the order of execution.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
{
2+
"resource": {
3+
"aws_codebuild_project": {
4+
"terra_ci": {
5+
"artifacts": [
6+
{
7+
"artifact_identifier": null,
8+
"encryption_disabled": false,
9+
"location": "terra-ci-artifacts-eu-west-1-000002",
10+
"name": null,
11+
"namespace_type": null,
12+
"override_artifact_name": false,
13+
"packaging": null,
14+
"path": null,
15+
"type": "S3"
16+
}
17+
],
18+
"badge_enabled": false,
19+
"build_timeout": 10,
20+
"cache": [],
21+
"description": "Deploy environment configuration",
22+
"environment": [
23+
{
24+
"certificate": null,
25+
"compute_type": "BUILD_GENERAL1_SMALL",
26+
"environment_variable": [],
27+
"image": "aws/codebuild/amazonlinux2-x86_64-standard:2.0",
28+
"image_pull_credentials_type": "CODEBUILD",
29+
"privileged_mode": false,
30+
"registry_credential": [],
31+
"type": "LINUX_CONTAINER"
32+
}
33+
],
34+
"logs_config": [
35+
{
36+
"cloudwatch_logs": [
37+
{
38+
"group_name": null,
39+
"status": "ENABLED",
40+
"stream_name": null
41+
}
42+
],
43+
"s3_logs": [
44+
{
45+
"encryption_disabled": false,
46+
"location": null,
47+
"status": "DISABLED"
48+
}
49+
]
50+
}
51+
],
52+
"name": "terra-ci-runner",
53+
"queued_timeout": 480,
54+
"secondary_artifacts": [],
55+
"secondary_sources": [],
56+
"source": [
57+
{
58+
"auth": [],
59+
"buildspec": "version: 0.2\nphases:\n install:\n commands:\n - make install_tools\n build:\n commands:\n - make plan_local resource=$TERRA_CI_RESOURCE\nartifacts:\n files:\n - ./tfplan\n name: $TERRA_CI_BUILD_NAME\n\n",
60+
"git_clone_depth": 1,
61+
"git_submodules_config": [],
62+
"insecure_ssl": false,
63+
"location": "https://github.com/p0tr3c-terraform/terra-ci-single-account.git",
64+
"report_build_status": false,
65+
"type": "GITHUB"
66+
}
67+
],
68+
"source_version": null,
69+
"tags": null,
70+
"vpc_config": []
71+
}
72+
},
73+
"aws_iam_role": {
74+
"terra_ci_job": {
75+
"assume_role_policy": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"codebuild.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n",
76+
"description": null,
77+
"force_detach_policies": false,
78+
"max_session_duration": 3600,
79+
"name": "terra_ci_job",
80+
"name_prefix": null,
81+
"path": "/",
82+
"permissions_boundary": null,
83+
"tags": null
84+
},
85+
"terra_ci_runner": {
86+
"assume_role_policy": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"states.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n",
87+
"description": null,
88+
"force_detach_policies": false,
89+
"max_session_duration": 3600,
90+
"name": "terra_ci_runner",
91+
"name_prefix": null,
92+
"path": "/",
93+
"permissions_boundary": null,
94+
"tags": null
95+
}
96+
},
97+
"aws_iam_role_policy": {
98+
"terra_ci_job": {
99+
"name_prefix": null,
100+
"role": "terra_ci_job"
101+
},
102+
"terra_ci_runner": {
103+
"name_prefix": null,
104+
"role": "terra_ci_runner"
105+
}
106+
},
107+
"aws_iam_role_policy_attachment": {
108+
"terra_ci_job_ecr_access": {
109+
"policy_arn": "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser",
110+
"role": "terra_ci_job"
111+
}
112+
},
113+
"aws_s3_bucket": {
114+
"terra_ci": {
115+
"acl": "private",
116+
"bucket": "terra-ci-artifacts-eu-west-1-000002",
117+
"bucket_prefix": null,
118+
"cors_rule": [],
119+
"force_destroy": false,
120+
"grant": [],
121+
"lifecycle_rule": [],
122+
"logging": [],
123+
"object_lock_configuration": [],
124+
"policy": null,
125+
"replication_configuration": [],
126+
"server_side_encryption_configuration": [
127+
{
128+
"rule": [
129+
{
130+
"apply_server_side_encryption_by_default": [
131+
{
132+
"kms_master_key_id": null,
133+
"sse_algorithm": "aws:kms"
134+
}
135+
],
136+
"bucket_key_enabled": false
137+
}
138+
]
139+
}
140+
],
141+
"tags": null,
142+
"website": []
143+
}
144+
},
145+
"aws_sfn_state_machine": {
146+
"terra_ci_runner": {
147+
"definition": "{\n \"Comment\": \"Run Terragrunt Jobs\",\n \"StartAt\": \"OnBranch?\",\n \"States\": {\n \"OnBranch?\": {\n \"Type\": \"Choice\",\n \"Choices\": [\n {\n \"Variable\": \"$.build.sourceversion\",\n \"IsPresent\": true,\n \"Next\": \"PlanBranch\"\n }\n ],\n \"Default\": \"Plan\"\n },\n \"Plan\": {\n \"Type\": \"Task\",\n \"Resource\": \"arn:aws:states:::codebuild:startBuild.sync\",\n \"Parameters\": {\n \"ProjectName\": \"terra-ci-runner\",\n \"EnvironmentVariablesOverride\": [\n {\n \"Name\": \"TERRA_CI_BUILD_NAME\",\n \"Value.$\": \"$$.Execution.Name\"\n },\n {\n \"Name\": \"TERRA_CI_RESOURCE\",\n \"Value.$\": \"$.build.environment.terra_ci_resource\"\n }\n ]\n },\n \"End\": true\n },\n \"PlanBranch\": {\n \"Type\": \"Task\",\n \"Resource\": \"arn:aws:states:::codebuild:startBuild.sync\",\n \"Parameters\": {\n \"ProjectName\": \"terra-ci-runner\",\n \"SourceVersion.$\": \"$.build.sourceversion\",\n \"EnvironmentVariablesOverride\": [\n {\n \"Name\": \"TERRA_CI_RESOURCE\",\n \"Value.$\": \"$.build.environment.terra_ci_resource\"\n }\n ]\n },\n \"End\": true\n }\n }\n}\n",
148+
"name": "terra-ci-runner",
149+
"tags": null,
150+
"type": "STANDARD"
151+
}
152+
}
153+
},
154+
"data": {}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"resource": {},
3+
"data": {}
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"resource": {},
3+
"data": {}
4+
}

0 commit comments

Comments
 (0)
Please sign in to comment.