|
1 | 1 | import * as cxapi from '@aws-cdk/cx-api';
|
2 | 2 | import { CloudFormation } from 'aws-sdk';
|
3 |
| -import { Mode, SdkProvider, ISDK } from '../aws-auth'; |
| 3 | +import { ISDK, Mode, SdkProvider } from '../aws-auth'; |
4 | 4 | import { Deployments } from '../deployments';
|
5 | 5 | import { EvaluateCloudFormationTemplate, LazyListStackResources } from '../evaluate-cloudformation-template';
|
6 | 6 |
|
7 | 7 | // resource types that have associated CloudWatch Log Groups that should _not_ be monitored
|
8 | 8 | const IGNORE_LOGS_RESOURCE_TYPES = ['AWS::EC2::FlowLog', 'AWS::CloudTrail::Trail', 'AWS::CodeBuild::Project'];
|
9 | 9 |
|
10 |
| -// Resource types that will create a CloudWatch log group with a specific name if one is not provided. |
11 |
| -// The keys are CFN resource types, and the values are the name of the physical name property of that resource |
12 |
| -// and the service name that is used in the automatically created CloudWatch log group. |
13 |
| -const RESOURCE_TYPES_WITH_IMPLICIT_LOGS: { [cfnResourceType: string]: { [key: string]: string } } = { |
14 |
| - 'AWS::Lambda::Function': { |
15 |
| - PhysicalNamePropertyName: 'FunctionName', |
16 |
| - LogGroupServiceName: 'lambda', |
17 |
| - }, |
18 |
| -}; |
19 |
| - |
20 | 10 | /**
|
21 | 11 | * Configuration needed to monitor CloudWatch Log Groups
|
22 | 12 | * found in a given CloudFormation Stack
|
@@ -84,38 +74,68 @@ function isReferencedFromIgnoredResource(
|
84 | 74 | logGroupResource: CloudFormation.StackResourceSummary,
|
85 | 75 | evaluateCfnTemplate: EvaluateCloudFormationTemplate,
|
86 | 76 | ): boolean {
|
87 |
| - let foundReference = false; |
88 | 77 | const resourcesReferencingLogGroup = evaluateCfnTemplate.findReferencesTo(logGroupResource.LogicalResourceId);
|
89 |
| - for (const reference of resourcesReferencingLogGroup) { |
90 |
| - if (IGNORE_LOGS_RESOURCE_TYPES.includes(reference.Type)) { |
91 |
| - foundReference = true; |
92 |
| - } |
93 |
| - } |
94 |
| - return foundReference; |
| 78 | + return resourcesReferencingLogGroup.some(reference => { |
| 79 | + return IGNORE_LOGS_RESOURCE_TYPES.includes(reference.Type); |
| 80 | + }); |
95 | 81 | }
|
96 | 82 |
|
| 83 | +type CloudWatchLogsResolver = ( |
| 84 | + resource: CloudFormation.StackResourceSummary, |
| 85 | + evaluateCfnTemplate: EvaluateCloudFormationTemplate |
| 86 | +) => string | undefined; |
| 87 | + |
| 88 | +const cloudWatchLogsResolvers: Record<string, CloudWatchLogsResolver> = { |
| 89 | + 'AWS::Logs::LogGroup': (resource, evaluateCfnTemplate) => { |
| 90 | + if (isReferencedFromIgnoredResource(resource, evaluateCfnTemplate)) { |
| 91 | + return undefined; |
| 92 | + } |
| 93 | + return resource.PhysicalResourceId?.toString(); |
| 94 | + }, |
| 95 | + |
| 96 | + // Resource types that will create a CloudWatch log group with a specific name if one is not provided. |
| 97 | + // The keys are CFN resource types, and the values are the name of the physical name property of that resource |
| 98 | + // and the service name that is used in the automatically created CloudWatch log group. |
| 99 | + 'AWS::Lambda::Function': (resource, evaluateCfnTemplate) => { |
| 100 | + const loggingConfig = evaluateCfnTemplate.getResourceProperty(resource.LogicalResourceId, 'LoggingConfig'); |
| 101 | + if (loggingConfig?.LogGroup) { |
| 102 | + // if LogGroup is a string then use it as the LogGroupName as it is referred by LogGroup.fromLogGroupArn in CDK |
| 103 | + if (typeof loggingConfig.LogGroup === 'string') { |
| 104 | + return loggingConfig.LogGroup; |
| 105 | + } |
| 106 | + |
| 107 | + // if { Ref: '...' } is used then try to resolve the LogGroupName from the referenced resource in the template |
| 108 | + if (typeof loggingConfig.LogGroup === 'object') { |
| 109 | + if (loggingConfig.LogGroup.Ref) { |
| 110 | + return evaluateCfnTemplate.getResourceProperty(loggingConfig.LogGroup.Ref, 'LogGroupName'); |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + return `/aws/lambda/${resource.PhysicalResourceId}`; |
| 116 | + }, |
| 117 | +}; |
| 118 | + |
97 | 119 | /**
|
98 | 120 | * Find all CloudWatch Log Groups in the deployed template.
|
99 |
| - * This will find both explicitely created Log Groups (excluding those associated with ignored resources) |
100 |
| - * as well as Log Groups created implicitely (i.e. Lambda Functions) |
| 121 | + * This will find both explicitly created Log Groups (excluding those associated with ignored resources) |
| 122 | + * and Log Groups created implicitly (i.e. Lambda Functions) |
101 | 123 | */
|
102 | 124 | function findAllLogGroupNames(
|
103 | 125 | stackResources: CloudFormation.StackResourceSummary[],
|
104 | 126 | evaluateCfnTemplate: EvaluateCloudFormationTemplate,
|
105 | 127 | ): string[] {
|
106 |
| - return stackResources.reduce((logGroupNames: string[], resource) => { |
107 |
| - let logGroupName; |
108 |
| - if (resource.ResourceType === 'AWS::Logs::LogGroup') { |
109 |
| - if (!isReferencedFromIgnoredResource(resource, evaluateCfnTemplate)) { |
110 |
| - logGroupName = resource.PhysicalResourceId; |
| 128 | + const logGroupNames: string[] = []; |
| 129 | + |
| 130 | + for (const resource of stackResources) { |
| 131 | + const logGroupResolver = cloudWatchLogsResolvers[resource.ResourceType]; |
| 132 | + if (logGroupResolver) { |
| 133 | + const logGroupName = logGroupResolver(resource, evaluateCfnTemplate); |
| 134 | + if (logGroupName) { |
| 135 | + logGroupNames.push(logGroupName); |
111 | 136 | }
|
112 |
| - } else if (RESOURCE_TYPES_WITH_IMPLICIT_LOGS[resource.ResourceType]) { |
113 |
| - const servicePart = RESOURCE_TYPES_WITH_IMPLICIT_LOGS[resource.ResourceType].LogGroupServiceName; |
114 |
| - logGroupName = `/aws/${servicePart}/${resource.PhysicalResourceId}`; |
115 |
| - } |
116 |
| - if (logGroupName) { |
117 |
| - logGroupNames.push(logGroupName); |
118 | 137 | }
|
119 |
| - return logGroupNames; |
120 |
| - }, []); |
| 138 | + } |
| 139 | + |
| 140 | + return logGroupNames; |
121 | 141 | }
|
0 commit comments