diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index 5e8da06ced183..aa9c4f59129d0 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -4023,6 +4023,10 @@ removed:aws-cdk-lib.aws_ecs.AsgCapacityProviderProps.canContainersAccessInstance # Exposed struct with a ref interface weakened:aws-cdk-lib.aws_batch.OrderedComputeEnvironment +# Weakened guarantees of a data structure that's intended for internal construct usage +weakened:aws-cdk-lib.aws_ec2.FlowLogDestinationConfig +weakened:aws-cdk-lib.aws_ecs.ExecuteCommandLogConfiguration + # Revert of PR #36378 which introduced reference interfaces that caused runtime errors # https://github.com/aws/aws-cdk/issues/36621 removed:aws-cdk-lib.aws_apigatewayv2.HttpApiHelper diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts b/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts index e96ab6c408755..b1b04aad94d5b 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts @@ -1,7 +1,7 @@ import { IStageRef } from './apigateway.generated'; import * as firehose from '../../aws-kinesisfirehose'; -import { ILogGroup } from '../../aws-logs'; import { ValidationError } from '../../core/lib/errors'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Access log destination for a RestApi Stage. @@ -27,7 +27,7 @@ export interface AccessLogDestinationConfig { * Use CloudWatch Logs as a custom access log destination for API Gateway. */ export class LogGroupLogDestination implements IAccessLogDestination { - constructor(private readonly logGroup: ILogGroup) { + constructor(private readonly logGroup: ILogGroupRef) { } /** @@ -35,7 +35,7 @@ export class LogGroupLogDestination implements IAccessLogDestination { */ public bind(_stage: IStageRef): AccessLogDestinationConfig { return { - destinationArn: this.logGroup.logGroupArn, + destinationArn: this.logGroup.logGroupRef.logGroupArn, }; } } diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/common/access-log.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/common/access-log.ts index 609e6d0b35e36..657548b42ac57 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/common/access-log.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/common/access-log.ts @@ -1,5 +1,5 @@ import { IStage } from './stage'; -import { ILogGroup } from '../../../aws-logs'; +import { ILogGroupRef } from '../../../interfaces/generated/aws-logs-interfaces.generated'; /** * Access log destination for a HttpApi Stage. @@ -25,7 +25,7 @@ export interface AccessLogDestinationConfig { * Use CloudWatch Logs as a custom access log destination for API Gateway. */ export class LogGroupLogDestination implements IAccessLogDestination { - constructor(private readonly logGroup: ILogGroup) { + constructor(private readonly logGroup: ILogGroupRef) { } /** @@ -33,7 +33,7 @@ export class LogGroupLogDestination implements IAccessLogDestination { */ public bind(_stage: IStage): AccessLogDestinationConfig { return { - destinationArn: this.logGroup.logGroupArn, + destinationArn: this.logGroup.logGroupRef.logGroupArn, }; } } diff --git a/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts b/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts index 3cfa6f7abf939..ca103bfa06080 100644 --- a/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts +++ b/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts @@ -5,6 +5,7 @@ import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import * as logs from '../../aws-logs'; +import { toILogGroup } from '../../aws-logs/lib/private/ref-utils'; import * as s3 from '../../aws-s3'; import * as sns from '../../aws-sns'; import { Annotations, Resource, Stack, ValidationError } from '../../core'; @@ -79,7 +80,7 @@ export interface TrailProps { * Log Group to which CloudTrail to push logs to. Ignored if sendToCloudWatchLogs is set to false. * @default - a new log group is created and used. */ - readonly cloudWatchLogGroup?: logs.ILogGroup; + readonly cloudWatchLogGroup?: logs.ILogGroupRef; /** The AWS Key Management Service (AWS KMS) key ID that you want to use to encrypt CloudTrail logs. * @default - No encryption. @@ -243,11 +244,15 @@ export class Trail extends Resource { */ public readonly trailSnsTopicArn: string; + private readonly _logGroup?: logs.ILogGroupRef; + /** * The CloudWatch log group to which CloudTrail events are sent. * `undefined` if `sendToCloudWatchLogs` property is false. */ - public readonly logGroup?: logs.ILogGroup; + public get logGroup(): logs.ILogGroup | undefined { + return this._logGroup ? toILogGroup(this._logGroup) : undefined; + } private s3bucket: s3.IBucket; private managementEvents: ReadWriteType | undefined; @@ -315,9 +320,9 @@ export class Trail extends Resource { if (props.sendToCloudWatchLogs) { if (props.cloudWatchLogGroup) { - this.logGroup = props.cloudWatchLogGroup; + this._logGroup = props.cloudWatchLogGroup; } else { - this.logGroup = new logs.LogGroup(this, 'LogGroup', { + this._logGroup = new logs.LogGroup(this, 'LogGroup', { retention: props.cloudWatchLogsRetention ?? logs.RetentionDays.ONE_YEAR, }); } @@ -326,7 +331,7 @@ export class Trail extends Resource { logsRole.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['logs:PutLogEvents', 'logs:CreateLogStream'], - resources: [this.logGroup.logGroupArn], + resources: [this._logGroup.logGroupRef.logGroupArn], })); } @@ -359,7 +364,7 @@ export class Trail extends Resource { kmsKeyId: props.encryptionKey?.keyArn ?? props.kmsKey?.keyRef.keyArn, s3BucketName: this.s3bucket.bucketName, s3KeyPrefix: props.s3KeyPrefix, - cloudWatchLogsLogGroupArn: this.logGroup?.logGroupArn, + cloudWatchLogsLogGroupArn: this._logGroup?.logGroupRef.logGroupArn, cloudWatchLogsRoleArn: logsRole?.roleArn, snsTopicName: this.topic?.topicName, eventSelectors: this.eventSelectors, diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts index 60295915e93c2..11fd83499de33 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts @@ -1549,7 +1549,7 @@ export class Project extends ProjectBase { cloudwatchConfig = { status, - groupName: cloudWatchLogs.logGroup?.logGroupName, + groupName: cloudWatchLogs.logGroup?.logGroupRef.logGroupName, streamName: cloudWatchLogs.prefix, }; } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts index bced80afa18b9..3f1d9ff7925b7 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts @@ -21,6 +21,7 @@ import * as logs from '../../aws-logs'; import { CfnOutput, Resource, Token, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogStreamRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Options for Client Route Enforcement @@ -79,14 +80,14 @@ export interface ClientVpnEndpointOptions { * * @default - a new group is created */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * A CloudWatch Logs log stream for connection logging * * @default - a new stream is created */ - readonly logStream?: logs.ILogStream; + readonly logStream?: ILogStreamRef; /** * The AWS Lambda function used for connection authorization @@ -408,8 +409,8 @@ export class ClientVpnEndpoint extends Resource implements IClientVpnEndpoint { : undefined, connectionLogOptions: { enabled: logging, - cloudwatchLogGroup: logGroup?.logGroupName, - cloudwatchLogStream: props.logStream?.logStreamName, + cloudwatchLogGroup: logGroup?.logGroupRef.logGroupName, + cloudwatchLogStream: props.logStream?.logStreamRef.logStreamName, }, description: props.description, dnsServers: props.dnsServers, diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts index b26a523711159..a6015ae73a793 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts @@ -3,6 +3,7 @@ import { CfnFlowLog, FlowLogReference, IFlowLogRef, ISubnetRef } from './ec2.gen import { IVpc } from './vpc'; import * as iam from '../../aws-iam'; import * as logs from '../../aws-logs'; +import { toILogGroup } from '../../aws-logs/lib/private/ref-utils'; import * as s3 from '../../aws-s3'; import { CfnResource, @@ -200,7 +201,7 @@ export abstract class FlowLogDestination { /** * Use CloudWatch logs as the destination */ - public static toCloudWatchLogs(logGroup?: logs.ILogGroup, iamRole?: iam.IRole): FlowLogDestination { + public static toCloudWatchLogs(logGroup?: logs.ILogGroupRef, iamRole?: iam.IRole): FlowLogDestination { return new CloudWatchLogsDestination({ logDestinationType: FlowLogDestinationType.CLOUD_WATCH_LOGS, logGroup, @@ -266,7 +267,7 @@ export interface FlowLogDestinationConfig { * * @default - default log group is created for you */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * S3 bucket to publish the flow logs to @@ -397,7 +398,7 @@ class CloudWatchLogsDestination extends FlowLogDestination { public bind(scope: Construct, _flowLog: FlowLog): FlowLogDestinationConfig { let iamRole: iam.IRole; - let logGroup: logs.ILogGroup; + let logGroup: logs.ILogGroupRef; if (this.props.iamRole === undefined) { iamRole = new iam.Role(scope, 'IAMRole', { roleName: PhysicalName.GENERATE_IF_NEEDED, @@ -421,7 +422,7 @@ class CloudWatchLogsDestination extends FlowLogDestination { 'logs:DescribeLogStreams', ], effect: iam.Effect.ALLOW, - resources: [logGroup.logGroupArn], + resources: [logGroup.logGroupRef.logGroupArn], }), ); @@ -859,7 +860,14 @@ export class FlowLog extends FlowLogBase { /** * The CloudWatch Logs LogGroup to publish flow logs to */ - public readonly logGroup?: logs.ILogGroup; + private readonly _logGroup?: logs.ILogGroupRef; + + /** + * The CloudWatch Logs LogGroup to publish flow logs to + */ + public get logGroup(): logs.ILogGroup | undefined { + return this._logGroup ? toILogGroup(this._logGroup) : undefined; + } /** * The ARN of the Amazon Data Firehose delivery stream to publish flow logs to @@ -874,7 +882,7 @@ export class FlowLog extends FlowLogBase { const destination = props.destination || FlowLogDestination.toCloudWatchLogs(); const destinationConfig = destination.bind(this, this); - this.logGroup = destinationConfig.logGroup; + this._logGroup = destinationConfig.logGroup; this.bucket = destinationConfig.s3Bucket; this.iamRole = destinationConfig.iamRole; this.keyPrefix = destinationConfig.keyPrefix; @@ -911,7 +919,7 @@ export class FlowLog extends FlowLogBase { destinationOptions: destinationConfig.destinationOptions, deliverLogsPermissionArn: this.iamRole ? this.iamRole.roleArn : undefined, logDestinationType: destinationConfig.logDestinationType, - logGroupName: this.logGroup ? this.logGroup.logGroupName : undefined, + logGroupName: this._logGroup?.logGroupRef.logGroupName, maxAggregationInterval: props.maxAggregationInterval, resourceId: props.resourceType.resourceId, resourceType: props.resourceType.resourceType, diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts index 511133fd77d7c..c1ed77dd2ea66 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts @@ -1181,7 +1181,7 @@ export abstract class BaseService extends Resource resources: ['*'], })); - const logGroupArn = logConfiguration?.cloudWatchLogGroup ? `arn:${this.stack.partition}:logs:${this.env.region}:${this.env.account}:log-group:${logConfiguration.cloudWatchLogGroup.logGroupName}:*` : '*'; + const logGroupArn = logConfiguration?.cloudWatchLogGroup ? `arn:${this.stack.partition}:logs:${this.env.region}:${this.env.account}:log-group:${logConfiguration.cloudWatchLogGroup.logGroupRef.logGroupName}:*` : '*'; this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ actions: [ 'logs:CreateLogStream', diff --git a/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts b/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts index 41f2c8f7f378f..7d0ea30e7d99f 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts @@ -469,7 +469,7 @@ export class Cluster extends Resource implements ICluster { } return { cloudWatchEncryptionEnabled: logConfiguration?.cloudWatchEncryptionEnabled, - cloudWatchLogGroupName: logConfiguration?.cloudWatchLogGroup?.logGroupName, + cloudWatchLogGroupName: logConfiguration?.cloudWatchLogGroup?.logGroupRef.logGroupName, s3BucketName: logConfiguration?.s3Bucket?.bucketName, s3EncryptionEnabled: logConfiguration?.s3EncryptionEnabled, s3KeyPrefix: logConfiguration?.s3KeyPrefix, @@ -1323,7 +1323,7 @@ export interface ExecuteCommandLogConfiguration { * The name of the CloudWatch log group to send logs to. The CloudWatch log group must already be created. * @default - none */ - readonly cloudWatchLogGroup?: logs.ILogGroup; + readonly cloudWatchLogGroup?: logs.ILogGroupRef; /** * The name of the S3 bucket to send logs to. The S3 bucket must already be created. diff --git a/packages/aws-cdk-lib/aws-ecs/lib/log-drivers/aws-log-driver.ts b/packages/aws-cdk-lib/aws-ecs/lib/log-drivers/aws-log-driver.ts index ff479b9028367..eed274cc3c1b6 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/log-drivers/aws-log-driver.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/log-drivers/aws-log-driver.ts @@ -3,6 +3,7 @@ import { LogDriver, LogDriverConfig } from './log-driver'; import { removeEmpty } from './utils'; import * as iam from '../../../aws-iam'; import * as logs from '../../../aws-logs'; +import { toILogGroup } from '../../../aws-logs/lib/private/ref-utils'; import { Size, SizeRoundingBehavior, UnscopedValidationError } from '../../../core'; import { ContainerDefinition } from '../container-definition'; @@ -44,7 +45,7 @@ export interface AwsLogDriverProps { * * @default - A log group is automatically created. */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * The number of days log events are kept in CloudWatch Logs when the log @@ -105,7 +106,7 @@ export class AwsLogDriver extends LogDriver { * * Only available after the LogDriver has been bound to a ContainerDefinition. */ - public logGroup?: logs.ILogGroup; + private _logGroup?: logs.ILogGroupRef; /** * Constructs a new instance of the AwsLogDriver class. @@ -128,7 +129,7 @@ export class AwsLogDriver extends LogDriver { * Called when the log driver is configured on a container */ public bind(scope: Construct, containerDefinition: ContainerDefinition): LogDriverConfig { - this.logGroup = this.props.logGroup || new logs.LogGroup(scope, 'LogGroup', { + this._logGroup = this.props.logGroup || new logs.LogGroup(scope, 'LogGroup', { retention: this.props.logRetention || Infinity, }); @@ -144,15 +145,15 @@ export class AwsLogDriver extends LogDriver { const execRole = containerDefinition.taskDefinition.obtainExecutionRole(); execRole.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['logs:CreateLogStream', 'logs:PutLogEvents'], - resources: [this.logGroup.logGroupArn], + resources: [this._logGroup.logGroupRef.logGroupArn], })); return { logDriver: 'awslogs', options: removeEmpty({ - 'awslogs-group': this.logGroup.logGroupName, + 'awslogs-group': this._logGroup.logGroupRef.logGroupName, 'awslogs-stream-prefix': this.props.streamPrefix, - 'awslogs-region': this.logGroup.env.region, + 'awslogs-region': this._logGroup.env.region, 'awslogs-datetime-format': this.props.datetimeFormat, 'awslogs-multiline-pattern': this.props.multilinePattern, 'mode': this.props.mode, @@ -160,4 +161,22 @@ export class AwsLogDriver extends LogDriver { }), }; } + + /** + * The log group to send log streams to. + * + * Only available after the LogDriver has been bound to a ContainerDefinition. + */ + public get logGroup(): logs.ILogGroup | undefined { + return this._logGroup && toILogGroup(this._logGroup); + } + + /** + * The log group to send log streams to. + * + * Only available after the LogDriver has been bound to a ContainerDefinition. + */ + public set logGroup(logGroup: logs.ILogGroup | undefined) { + this._logGroup = logGroup; + } } diff --git a/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts b/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts index d53d4c127b184..40c388dd5d501 100644 --- a/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts +++ b/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts @@ -12,6 +12,7 @@ import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as logs from '../../aws-logs'; +import { toILogGroup } from '../../aws-logs/lib/private/ref-utils'; import * as route53 from '../../aws-route53'; import * as secretsmanager from '../../aws-secretsmanager'; import * as cdk from '../../core'; @@ -306,7 +307,7 @@ export interface LoggingOptions { * @default - a new log group is created if slow search logging is enabled * @deprecated use opensearchservice module instead */ - readonly slowSearchLogGroup?: logs.ILogGroup; + readonly slowSearchLogGroup?: logs.ILogGroupRef; /** * Specify if slow index logging should be set up. @@ -323,7 +324,7 @@ export interface LoggingOptions { * @default - a new log group is created if slow index logging is enabled * @deprecated use opensearchservice module instead */ - readonly slowIndexLogGroup?: logs.ILogGroup; + readonly slowIndexLogGroup?: logs.ILogGroupRef; /** * Specify if Elasticsearch application logging should be set up. @@ -340,7 +341,7 @@ export interface LoggingOptions { * @default - a new log group is created if app logging is enabled * @deprecated use opensearchservice module instead */ - readonly appLogGroup?: logs.ILogGroup; + readonly appLogGroup?: logs.ILogGroupRef; /** * Specify if Elasticsearch audit logging should be set up. @@ -357,7 +358,7 @@ export interface LoggingOptions { * @default - a new log group is created if audit logging is enabled * @deprecated use opensearchservice module instead */ - readonly auditLogGroup?: logs.ILogGroup; + readonly auditLogGroup?: logs.ILogGroupRef; } /** @@ -1426,7 +1427,9 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { * @attribute * @deprecated use opensearchservice module instead */ - public readonly slowSearchLogGroup?: logs.ILogGroup; + public get slowSearchLogGroup(): logs.ILogGroup | undefined { + return this._slowSearchLogGroup ? toILogGroup(this._slowSearchLogGroup) : undefined; + } /** * Log group that slow indices are logged to. @@ -1434,7 +1437,9 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { * @attribute * @deprecated use opensearchservice module instead */ - public readonly slowIndexLogGroup?: logs.ILogGroup; + public get slowIndexLogGroup(): logs.ILogGroup | undefined { + return this._slowIndexLogGroup ? toILogGroup(this._slowIndexLogGroup) : undefined; + } /** * Log group that application logs are logged to. @@ -1442,7 +1447,9 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { * @attribute * @deprecated use opensearchservice module instead */ - public readonly appLogGroup?: logs.ILogGroup; + public get appLogGroup(): logs.ILogGroup | undefined { + return this._appLogGroup ? toILogGroup(this._appLogGroup) : undefined; + } /** * Log group that audit logs are logged to. @@ -1450,7 +1457,9 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { * @attribute * @deprecated use opensearchservice module instead */ - public readonly auditLogGroup?: logs.ILogGroup; + public get auditLogGroup(): logs.ILogGroup | undefined { + return this._auditLogGroup ? toILogGroup(this._auditLogGroup) : undefined; + } /** * Master user password if fine grained access control is configured. @@ -1461,6 +1470,11 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { private readonly domain: CfnDomain; + private readonly _slowSearchLogGroup?: logs.ILogGroupRef; + private readonly _slowIndexLogGroup?: logs.ILogGroupRef; + private readonly _appLogGroup?: logs.ILogGroupRef; + private readonly _auditLogGroup?: logs.ILogGroupRef; + private accessPolicy?: ElasticsearchAccessPolicy; private encryptionAtRestOptions?: EncryptionAtRestOptions; @@ -1704,42 +1718,42 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { } // Setup logging - const logGroups: logs.ILogGroup[] = []; + const logGroups: logs.ILogGroupRef[] = []; if (props.logging?.slowSearchLogEnabled) { - this.slowSearchLogGroup = props.logging.slowSearchLogGroup ?? + this._slowSearchLogGroup = props.logging.slowSearchLogGroup ?? new logs.LogGroup(this, 'SlowSearchLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.slowSearchLogGroup); + logGroups.push(this._slowSearchLogGroup); } if (props.logging?.slowIndexLogEnabled) { - this.slowIndexLogGroup = props.logging.slowIndexLogGroup ?? + this._slowIndexLogGroup = props.logging.slowIndexLogGroup ?? new logs.LogGroup(this, 'SlowIndexLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.slowIndexLogGroup); + logGroups.push(this._slowIndexLogGroup); } if (props.logging?.appLogEnabled) { - this.appLogGroup = props.logging.appLogGroup ?? + this._appLogGroup = props.logging.appLogGroup ?? new logs.LogGroup(this, 'AppLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.appLogGroup); + logGroups.push(this._appLogGroup); } if (props.logging?.auditLogEnabled) { - this.auditLogGroup = props.logging.auditLogGroup ?? + this._auditLogGroup = props.logging.auditLogGroup ?? new logs.LogGroup(this, 'AuditLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.auditLogGroup); + logGroups.push(this._auditLogGroup); } let logGroupResourcePolicy: LogGroupResourcePolicy | null = null; @@ -1747,7 +1761,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { const logPolicyStatement = new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['logs:PutLogEvents', 'logs:CreateLogStream'], - resources: logGroups.map((lg) => lg.logGroupArn), + resources: logGroups.map((lg) => lg.logGroupRef.logGroupArn), principals: [new iam.ServicePrincipal('es.amazonaws.com')], }); @@ -1762,31 +1776,31 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { const logPublishing: Record = {}; - if (this.appLogGroup) { + if (this._appLogGroup) { logPublishing.ES_APPLICATION_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.appLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._appLogGroup.logGroupRef.logGroupArn, }; } - if (this.slowSearchLogGroup) { + if (this._slowSearchLogGroup) { logPublishing.SEARCH_SLOW_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.slowSearchLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._slowSearchLogGroup.logGroupRef.logGroupArn, }; } - if (this.slowIndexLogGroup) { + if (this._slowIndexLogGroup) { logPublishing.INDEX_SLOW_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.slowIndexLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._slowIndexLogGroup.logGroupRef.logGroupArn, }; } - if (this.auditLogGroup) { + if (this._auditLogGroup) { logPublishing.AUDIT_LOGS = { - enabled: this.auditLogGroup != null, - cloudWatchLogsLogGroupArn: this.auditLogGroup?.logGroupArn, + enabled: this._auditLogGroup != null, + cloudWatchLogsLogGroupArn: this._auditLogGroup?.logGroupRef.logGroupArn, }; } diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/log-group.ts b/packages/aws-cdk-lib/aws-events-targets/lib/log-group.ts index b9665f912933b..4d07d51463ea8 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/log-group.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/log-group.ts @@ -107,7 +107,7 @@ export interface LogGroupProps extends TargetBaseProps { */ export class CloudWatchLogGroup implements events.IRuleTarget { private target?: RuleTargetInputProperties; - constructor(private readonly logGroup: logs.ILogGroup, private readonly props: LogGroupProps = {}) {} + constructor(private readonly logGroup: logs.ILogGroupRef, private readonly props: LogGroupProps = {}) {} /** * Returns a RuleTarget that can be used to log an event into a CloudWatch LogGroup @@ -135,7 +135,7 @@ export class CloudWatchLogGroup implements events.IRuleTarget { policyStatements: [new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['logs:PutLogEvents', 'logs:CreateLogStream'], - resources: [this.logGroup.logGroupArn], + resources: [this.logGroup.logGroupRef.logGroupArn], principals: [new iam.ServicePrincipal('events.amazonaws.com')], })], }); @@ -147,7 +147,7 @@ export class CloudWatchLogGroup implements events.IRuleTarget { service: 'logs', resource: 'log-group', arnFormat: ArnFormat.COLON_RESOURCE_NAME, - resourceName: this.logGroup.logGroupName, + resourceName: this.logGroup.logGroupRef.logGroupName, }), input: this.props.event ?? this.props.logEvent, targetResource: this.logGroup, diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function.ts b/packages/aws-cdk-lib/aws-lambda/lib/function.ts index bee3512e53417..50a0041a67f83 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function.ts @@ -28,6 +28,7 @@ import * as efs from '../../aws-efs'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as logs from '../../aws-logs'; +import { toILogGroup } from '../../aws-logs/lib/private/ref-utils'; import * as sns from '../../aws-sns'; import * as sqs from '../../aws-sqs'; import { @@ -583,7 +584,7 @@ export interface FunctionOptions extends EventInvokeConfigOptions { * * @default `/aws/lambda/${this.functionName}` - default log group created by Lambda */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * Sets the logFormat for the function. @@ -1139,7 +1140,7 @@ export class Function extends FunctionBase { resource.tracingConfig = this.buildTracingConfig(props.tracing ?? Tracing.ACTIVE); } - this._logGroup = props.logGroup; + this._logGroup = props.logGroup ? toILogGroup(props.logGroup) : undefined; resource.node.addDependency(this.role); @@ -1317,7 +1318,7 @@ export class Function extends FunctionBase { logFormat: props.logFormat || props.loggingFormat, systemLogLevel: props.systemLogLevel || props.systemLogLevelV2, applicationLogLevel: props.applicationLogLevel || props.applicationLogLevelV2, - logGroup: props.logGroup?.logGroupName, + logGroup: props.logGroup?.logGroupRef.logGroupName, }; return loggingConfig; } diff --git a/packages/aws-cdk-lib/aws-logs-destinations/lib/firehose.ts b/packages/aws-cdk-lib/aws-logs-destinations/lib/firehose.ts index 327c6f055aea7..7cc02726debf3 100644 --- a/packages/aws-cdk-lib/aws-logs-destinations/lib/firehose.ts +++ b/packages/aws-cdk-lib/aws-logs-destinations/lib/firehose.ts @@ -3,6 +3,7 @@ import * as iam from '../../aws-iam'; import * as firehose from '../../aws-kinesisfirehose'; import * as logs from '../../aws-logs'; import { Stack } from '../../core'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Customize the Amazon Data Firehose Logs Destination @@ -28,7 +29,7 @@ export class FirehoseDestination implements logs.ILogSubscriptionDestination { constructor(private readonly stream: firehose.IDeliveryStream, private readonly props: FirehoseDestinationProps = {}) { } - public bind(scope: Construct, _sourceLogGroup: logs.ILogGroup): logs.LogSubscriptionDestinationConfig { + public bind(scope: Construct, _sourceLogGroup: ILogGroupRef): logs.LogSubscriptionDestinationConfig { // Following example from https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#FirehoseExample // Create a role to be assumed by CWL that can write to this stream. const id = 'CloudWatchLogsCanPutRecords'; diff --git a/packages/aws-cdk-lib/aws-logs-destinations/lib/kinesis.ts b/packages/aws-cdk-lib/aws-logs-destinations/lib/kinesis.ts index 2f9b4fdee590b..3a98eb50c0c64 100644 --- a/packages/aws-cdk-lib/aws-logs-destinations/lib/kinesis.ts +++ b/packages/aws-cdk-lib/aws-logs-destinations/lib/kinesis.ts @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as kinesis from '../../aws-kinesis'; import * as logs from '../../aws-logs'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Customize the Kinesis Logs Destination @@ -27,7 +28,7 @@ export class KinesisDestination implements logs.ILogSubscriptionDestination { constructor(private readonly stream: kinesis.IStream, private readonly props: KinesisDestinationProps = {}) { } - public bind(scope: Construct, _sourceLogGroup: logs.ILogGroup): logs.LogSubscriptionDestinationConfig { + public bind(scope: Construct, _sourceLogGroup: ILogGroupRef): logs.LogSubscriptionDestinationConfig { // Following example from https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#DestinationKinesisExample // Create a role to be assumed by CWL that can write to this stream and pass itself. const id = 'CloudWatchLogsCanPutRecords'; diff --git a/packages/aws-cdk-lib/aws-logs-destinations/lib/lambda.ts b/packages/aws-cdk-lib/aws-logs-destinations/lib/lambda.ts index 8ef4dad49d9c6..0b6dd378cd75c 100644 --- a/packages/aws-cdk-lib/aws-logs-destinations/lib/lambda.ts +++ b/packages/aws-cdk-lib/aws-logs-destinations/lib/lambda.ts @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as logs from '../../aws-logs'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Options that may be provided to LambdaDestination @@ -21,8 +22,8 @@ export class LambdaDestination implements logs.ILogSubscriptionDestination { constructor(private readonly fn: lambda.IFunction, private readonly options: LambdaDestinationOptions = {}) { } - public bind(scope: Construct, logGroup: logs.ILogGroup): logs.LogSubscriptionDestinationConfig { - const arn = logGroup.logGroupArn; + public bind(scope: Construct, logGroup: ILogGroupRef): logs.LogSubscriptionDestinationConfig { + const arn = logGroup.logGroupRef.logGroupArn; if (this.options.addPermissions !== false) { const permissionId = 'CanInvokeLambda'; this.fn.addPermission(permissionId, { diff --git a/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts b/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts index cd2db22786731..02766c63392a6 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts @@ -1,6 +1,5 @@ import { Construct } from 'constructs'; -import { ILogGroup } from './log-group'; -import { CfnDestination } from './logs.generated'; +import { CfnDestination, ILogGroupRef } from './logs.generated'; import { ILogSubscriptionDestination, LogSubscriptionDestinationConfig } from './subscription-filter'; import * as iam from '../../aws-iam'; import { ArnFormat } from '../../core'; @@ -104,7 +103,7 @@ export class CrossAccountDestination extends cdk.Resource implements ILogSubscri } @MethodMetadata() - public bind(_scope: Construct, _sourceLogGroup: ILogGroup): LogSubscriptionDestinationConfig { + public bind(_scope: Construct, _sourceLogGroup: ILogGroupRef): LogSubscriptionDestinationConfig { return { arn: this.destinationArn }; } diff --git a/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts b/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts index 731c06f49031d..b9f9fcce9596e 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; -import { ILogGroup } from './log-group'; import { IBucketRef } from '../../aws-s3'; import { Stack, UnscopedValidationError } from '../../core'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Creates a data protection policy for CloudWatch Logs log groups. @@ -27,7 +27,7 @@ export class DataProtectionPolicy { const findingsDestination: any = {}; if (this.dataProtectionPolicyProps.logGroupAuditDestination) { findingsDestination.CloudWatchLogs = { - LogGroup: this.dataProtectionPolicyProps.logGroupAuditDestination.logGroupName, + LogGroup: this.dataProtectionPolicyProps.logGroupAuditDestination.logGroupRef.logGroupName, }; } @@ -156,7 +156,7 @@ export interface DataProtectionPolicyProps { * * @default - no CloudWatch Logs audit destination */ - readonly logGroupAuditDestination?: ILogGroup; + readonly logGroupAuditDestination?: ILogGroupRef; /** * S3 bucket to send audit findings to. The bucket must already exist. diff --git a/packages/aws-cdk-lib/aws-logs/lib/log-stream.ts b/packages/aws-cdk-lib/aws-logs/lib/log-stream.ts index 947e1a245551f..ca54a31ca8cbb 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/log-stream.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/log-stream.ts @@ -1,11 +1,11 @@ import { Construct } from 'constructs'; -import { ILogGroup } from './log-group'; import { CfnLogStream } from './logs.generated'; -import { IResource, RemovalPolicy, Resource } from '../../core'; +import { IResource, RemovalPolicy, Resource, UnscopedValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogGroupRef, ILogStreamRef, LogStreamReference } from '../../interfaces/generated/aws-logs-interfaces.generated'; -export interface ILogStream extends IResource { +export interface ILogStream extends IResource, ILogStreamRef { /** * The name of this log stream * @attribute @@ -13,6 +13,23 @@ export interface ILogStream extends IResource { readonly logStreamName: string; } +/** + * Attributes for importing a LogStream + */ +export interface LogStreamAttributes { + /** + * The name of the log stream + */ + readonly logStreamName: string; + + /** + * The name of the log group + * + * @default - When not provided, logStreamRef will throw an error + */ + readonly logGroupName: string; +} + /** * Properties for a LogStream */ @@ -20,7 +37,7 @@ export interface LogStreamProps { /** * The log group to create a log stream for. */ - readonly logGroup: ILogGroup; + readonly logGroup: ILogGroupRef; /** * The name of the log stream to create. @@ -55,11 +72,37 @@ export class LogStream extends Resource implements ILogStream { public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-logs.LogStream'; /** - * Import an existing LogGroup + * Import an existing LogStream */ public static fromLogStreamName(scope: Construct, id: string, logStreamName: string): ILogStream { class Import extends Resource implements ILogStream { public readonly logStreamName = logStreamName; + + public get logStreamRef() { + return { + get logGroupName(): string { + throw new UnscopedValidationError('Cannot access logGroupName on a LogStream obtained from fromLogStreamName. Use LogStream.fromLogStreamAttributes() instead.'); + }, + logStreamName: this.logStreamName, + }; + } + } + + return new Import(scope, id); + } + + /** + * Import an existing LogStream using its attributes + */ + public static fromLogStreamAttributes(scope: Construct, id: string, attrs: LogStreamAttributes): ILogStream { + class Import extends Resource implements ILogStream { + public readonly logStreamName = attrs.logStreamName; + public get logStreamRef(): LogStreamReference { + return { + logGroupName: attrs.logGroupName, + logStreamName: this.logStreamName, + }; + } } return new Import(scope, id); @@ -70,6 +113,8 @@ export class LogStream extends Resource implements ILogStream { */ public readonly logStreamName: string; + private readonly logGroupName: string; + constructor(scope: Construct, id: string, props: LogStreamProps) { super(scope, id, { physicalName: props.logStreamName, @@ -77,12 +122,21 @@ export class LogStream extends Resource implements ILogStream { // Enhanced CDK Analytics Telemetry addConstructMetadata(this, props); + this.logGroupName = props.logGroup.logGroupRef.logGroupName; + const resource = new CfnLogStream(this, 'Resource', { - logGroupName: props.logGroup.logGroupName, + logGroupName: this.logGroupName, logStreamName: this.physicalName, }); resource.applyRemovalPolicy(props.removalPolicy); this.logStreamName = this.getResourceNameAttribute(resource.ref); } + + public get logStreamRef(): LogStreamReference { + return { + logGroupName: this.logGroupName, + logStreamName: this.logStreamName, + }; + } } diff --git a/packages/aws-cdk-lib/aws-logs/lib/metric-filter.ts b/packages/aws-cdk-lib/aws-logs/lib/metric-filter.ts index 33e7a9d2952d2..15a5fb60d5684 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/metric-filter.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/metric-filter.ts @@ -1,10 +1,11 @@ import { Construct } from 'constructs'; -import { ILogGroup, MetricFilterOptions } from './log-group'; +import { MetricFilterOptions } from './log-group'; import { CfnMetricFilter } from './logs.generated'; import { Metric, MetricOptions } from '../../aws-cloudwatch'; import { Resource, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Properties for a MetricFilter @@ -13,7 +14,7 @@ export interface MetricFilterProps extends MetricFilterOptions { /** * The log group to create the filter on. */ - readonly logGroup: ILogGroup; + readonly logGroup: ILogGroupRef; } /** @@ -50,7 +51,7 @@ export class MetricFilter extends Resource { // // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html new CfnMetricFilter(this, 'Resource', { - logGroupName: props.logGroup.logGroupName, + logGroupName: props.logGroup.logGroupRef.logGroupName, filterName: this.physicalName, filterPattern: props.filterPattern.logPatternString, metricTransformations: [{ diff --git a/packages/aws-cdk-lib/aws-logs/lib/private/ref-utils.ts b/packages/aws-cdk-lib/aws-logs/lib/private/ref-utils.ts new file mode 100644 index 0000000000000..5691ab79381ce --- /dev/null +++ b/packages/aws-cdk-lib/aws-logs/lib/private/ref-utils.ts @@ -0,0 +1,18 @@ +import { UnscopedValidationError } from '../../../core'; +import { ILogGroupRef } from '../../../interfaces/generated/aws-logs-interfaces.generated'; +import { ILogGroup } from '../log-group'; + +/** + * Convert an ILogGroupRef to ILogGroup, validating that it implements the full interface + * @internal + */ +export function toILogGroup(logGroup: ILogGroupRef): ILogGroup { + // Check for key methods that distinguish ILogGroup from ILogGroupRef + if ( + typeof (logGroup as any).addStream !== 'function' || + typeof (logGroup as any).grant !== 'function' + ) { + throw new UnscopedValidationError(`'logGroup' instance should implement ILogGroup, but doesn't: ${logGroup.constructor.name}`); + } + return logGroup as ILogGroup; +} diff --git a/packages/aws-cdk-lib/aws-logs/lib/query-definition.ts b/packages/aws-cdk-lib/aws-logs/lib/query-definition.ts index 71c276e2a59ff..537e5798df72a 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/query-definition.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/query-definition.ts @@ -1,9 +1,9 @@ import { Construct } from 'constructs'; import { CfnQueryDefinition } from '.'; -import { ILogGroup } from './log-group'; import { Annotations, Resource, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Properties for a QueryString @@ -216,7 +216,7 @@ export interface QueryDefinitionProps { * * @default - no specified log groups */ - readonly logGroups?: ILogGroup[]; + readonly logGroups?: ILogGroupRef[]; } /** @@ -252,7 +252,7 @@ export class QueryDefinition extends Resource { const queryDefinition = new CfnQueryDefinition(this, 'Resource', { name: props.queryDefinitionName, queryString: props.queryString.toString(), - logGroupNames: typeof props.logGroups === 'undefined' ? [] : props.logGroups.flatMap(logGroup => logGroup.logGroupName), + logGroupNames: typeof props.logGroups === 'undefined' ? [] : props.logGroups.flatMap(logGroup => logGroup.logGroupRef.logGroupName), }); this.queryDefinitionId = queryDefinition.attrQueryDefinitionId; diff --git a/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts b/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts index a6b2331f3e5b5..44e500b4a2913 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts @@ -1,11 +1,12 @@ import { Construct } from 'constructs'; -import { ILogGroup, SubscriptionFilterOptions } from './log-group'; +import { SubscriptionFilterOptions } from './log-group'; import { CfnSubscriptionFilter } from './logs.generated'; import * as iam from '../../aws-iam'; import { KinesisDestination } from '../../aws-logs-destinations'; import { Resource, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Interface for classes that can be the destination of a log Subscription @@ -21,7 +22,7 @@ export interface ILogSubscriptionDestination { * The destination may reconfigure its own permissions in response to this * function call. */ - bind(scope: Construct, sourceLogGroup: ILogGroup): LogSubscriptionDestinationConfig; + bind(scope: Construct, sourceLogGroup: ILogGroupRef): LogSubscriptionDestinationConfig; } /** @@ -48,7 +49,7 @@ export interface SubscriptionFilterProps extends SubscriptionFilterOptions { /** * The log group to create the subscription on. */ - readonly logGroup: ILogGroup; + readonly logGroup: ILogGroupRef; } /** @@ -78,7 +79,7 @@ export class SubscriptionFilter extends Resource { const destProps = props.destination.bind(this, props.logGroup); new CfnSubscriptionFilter(this, 'Resource', { - logGroupName: props.logGroup.logGroupName, + logGroupName: props.logGroup.logGroupRef.logGroupName, destinationArn: destProps.arn, roleArn: destProps.role?.roleArn, filterPattern: props.filterPattern.logPatternString, diff --git a/packages/aws-cdk-lib/aws-logs/lib/transformer.ts b/packages/aws-cdk-lib/aws-logs/lib/transformer.ts index 02f044cb4cbb6..c5b3c496f6412 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/transformer.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/transformer.ts @@ -18,10 +18,10 @@ import { Construct } from 'constructs'; import { CfnTransformer } from '.'; -import { ILogGroup } from './log-group'; import { Resource, Token, ValidationError, UnscopedValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; +import { ILogGroupRef } from '../../interfaces/generated/aws-logs-interfaces.generated'; /** * Valid data types for type conversion in the TypeConverter processor. @@ -806,7 +806,7 @@ export interface TransformerProps { */ readonly transformerName: string; /** Existing log group that you want to associate with this transformer. */ - readonly logGroup: ILogGroup; + readonly logGroup: ILogGroupRef; /** List of processors in a transformer */ readonly transformerConfig: Array; } @@ -1209,7 +1209,7 @@ export class Transformer extends Resource { // Map the transformer configuration to the L1 CloudFormation resource new CfnTransformer(scope, 'ResourceTransformer', { - logGroupIdentifier: props.logGroup.logGroupName, + logGroupIdentifier: props.logGroup.logGroupRef.logGroupName, transformerConfig: props.transformerConfig.map(processor => processor._render()), }); } @@ -1326,7 +1326,7 @@ export class Transformer extends Resource { * @internal Validates that the log group is in the Standard log class, as transformers can only be used with Standard log * groups. */ - private validateLogGroupClass(logGroup: ILogGroup): void { + private validateLogGroupClass(logGroup: ILogGroupRef): void { // Since logGroupClass might not be directly accessible or might be a CDK token, // we'll use a warning instead of validation error // In an ideal implementation, we would check that: logGroup.logGroupClass === 'Standard' diff --git a/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts b/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts index c88c73c8091c5..b4637f939e847 100644 --- a/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts +++ b/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts @@ -13,6 +13,7 @@ import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as logs from '../../aws-logs'; +import { toILogGroup } from '../../aws-logs/lib/private/ref-utils'; import * as route53 from '../../aws-route53'; import * as secretsmanager from '../../aws-secretsmanager'; import * as cdk from '../../core'; @@ -192,7 +193,7 @@ export interface LoggingOptions { * * @default - a new log group is created if slow search logging is enabled */ - readonly slowSearchLogGroup?: logs.ILogGroup; + readonly slowSearchLogGroup?: logs.ILogGroupRef; /** * Specify if slow index logging should be set up. @@ -208,7 +209,7 @@ export interface LoggingOptions { * * @default - a new log group is created if slow index logging is enabled */ - readonly slowIndexLogGroup?: logs.ILogGroup; + readonly slowIndexLogGroup?: logs.ILogGroupRef; /** * Specify if Amazon OpenSearch Service application logging should be set up. @@ -224,7 +225,7 @@ export interface LoggingOptions { * * @default - a new log group is created if app logging is enabled */ - readonly appLogGroup?: logs.ILogGroup; + readonly appLogGroup?: logs.ILogGroupRef; /** * Specify if Amazon OpenSearch Service audit logging should be set up. @@ -239,7 +240,7 @@ export interface LoggingOptions { * * @default - a new log group is created if audit logging is enabled */ - readonly auditLogGroup?: logs.ILogGroup; + readonly auditLogGroup?: logs.ILogGroupRef; } /** @@ -1415,34 +1416,47 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { * * @attribute */ - public readonly slowSearchLogGroup?: logs.ILogGroup; + public get slowSearchLogGroup(): logs.ILogGroup | undefined { + return this._slowSearchLogGroup ? toILogGroup(this._slowSearchLogGroup) : undefined; + } /** * Log group that slow indices are logged to. * * @attribute */ - public readonly slowIndexLogGroup?: logs.ILogGroup; + public get slowIndexLogGroup(): logs.ILogGroup | undefined { + return this._slowIndexLogGroup ? toILogGroup(this._slowIndexLogGroup) : undefined; + } /** * Log group that application logs are logged to. * * @attribute */ - public readonly appLogGroup?: logs.ILogGroup; + public get appLogGroup(): logs.ILogGroup | undefined { + return this._appLogGroup ? toILogGroup(this._appLogGroup) : undefined; + } /** * Log group that audit logs are logged to. * * @attribute */ - public readonly auditLogGroup?: logs.ILogGroup; + public get auditLogGroup(): logs.ILogGroup | undefined { + return this._auditLogGroup ? toILogGroup(this._auditLogGroup) : undefined; + } /** * Master user password if fine grained access control is configured. */ public readonly masterUserPassword?: cdk.SecretValue; + private readonly _slowSearchLogGroup?: logs.ILogGroupRef; + private readonly _slowIndexLogGroup?: logs.ILogGroupRef; + private readonly _appLogGroup?: logs.ILogGroupRef; + private readonly _auditLogGroup?: logs.ILogGroupRef; + private readonly domain: CfnDomain; private accessPolicy?: OpenSearchAccessPolicy; @@ -1804,19 +1818,19 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { } // Setup logging - const logGroups: logs.ILogGroup[] = []; + const logGroups: logs.ILogGroupRef[] = []; const logPublishing: Record = {}; if (props.logging?.slowSearchLogEnabled) { - this.slowSearchLogGroup = props.logging.slowSearchLogGroup ?? + this._slowSearchLogGroup = props.logging.slowSearchLogGroup ?? new logs.LogGroup(this, 'SlowSearchLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.slowSearchLogGroup); + logGroups.push(this._slowSearchLogGroup); logPublishing.SEARCH_SLOW_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.slowSearchLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._slowSearchLogGroup.logGroupRef.logGroupArn, }; } else if (props.logging?.slowSearchLogEnabled === false) { logPublishing.SEARCH_SLOW_LOGS = { @@ -1825,15 +1839,15 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { } if (props.logging?.slowIndexLogEnabled) { - this.slowIndexLogGroup = props.logging.slowIndexLogGroup ?? + this._slowIndexLogGroup = props.logging.slowIndexLogGroup ?? new logs.LogGroup(this, 'SlowIndexLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.slowIndexLogGroup); + logGroups.push(this._slowIndexLogGroup); logPublishing.INDEX_SLOW_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.slowIndexLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._slowIndexLogGroup.logGroupRef.logGroupArn, }; } else if (props.logging?.slowIndexLogEnabled === false) { logPublishing.INDEX_SLOW_LOGS = { @@ -1842,15 +1856,15 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { } if (props.logging?.appLogEnabled) { - this.appLogGroup = props.logging.appLogGroup ?? + this._appLogGroup = props.logging.appLogGroup ?? new logs.LogGroup(this, 'AppLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.appLogGroup); + logGroups.push(this._appLogGroup); logPublishing.ES_APPLICATION_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.appLogGroup.logGroupArn, + cloudWatchLogsLogGroupArn: this._appLogGroup.logGroupRef.logGroupArn, }; } else if (props.logging?.appLogEnabled === false) { logPublishing.ES_APPLICATION_LOGS = { @@ -1859,15 +1873,15 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { } if (props.logging?.auditLogEnabled) { - this.auditLogGroup = props.logging.auditLogGroup ?? + this._auditLogGroup = props.logging.auditLogGroup ?? new logs.LogGroup(this, 'AuditLogs', { retention: logs.RetentionDays.ONE_MONTH, }); - logGroups.push(this.auditLogGroup); + logGroups.push(this._auditLogGroup); logPublishing.AUDIT_LOGS = { enabled: true, - cloudWatchLogsLogGroupArn: this.auditLogGroup?.logGroupArn, + cloudWatchLogsLogGroupArn: this._auditLogGroup?.logGroupRef.logGroupArn, }; } else if (props.logging?.auditLogEnabled === false) { logPublishing.AUDIT_LOGS = { @@ -1880,7 +1894,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { const logPolicyStatement = new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['logs:PutLogEvents', 'logs:CreateLogStream'], - resources: logGroups.map((lg) => lg.logGroupArn), + resources: logGroups.map((lg) => lg.logGroupRef.logGroupArn), principals: [new iam.ServicePrincipal('es.amazonaws.com')], }); diff --git a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts index 54ce1ca8fb976..25d27eda4145f 100644 --- a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts @@ -145,7 +145,7 @@ export interface BucketDeploymentProps { * * @default - a default log group created by AWS Lambda */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * The amount of memory (in MiB) to allocate to the AWS Lambda function which diff --git a/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts b/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts index fc7592a567efe..6e8a30a8f76bb 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts @@ -69,7 +69,7 @@ export interface LogOptions { * * @default No log group. Required if your log level is not set to OFF. */ - readonly destination?: logs.ILogGroup; + readonly destination?: logs.ILogGroupRef; /** * Determines whether execution data is included in your log. @@ -569,7 +569,7 @@ export class StateMachine extends StateMachineBase { resources: ['*'], })); destinations = [{ - cloudWatchLogsLogGroup: { logGroupArn: logOptions.destination.logGroupArn }, + cloudWatchLogsLogGroup: { logGroupArn: logOptions.destination.logGroupRef.logGroupArn }, }]; } diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts index f6c8a37185bba..ce35800a0ab86 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts @@ -383,7 +383,7 @@ export interface AwsCustomResourceProps { * * @default - a default log group created by AWS Lambda */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * Whether to install the latest AWS SDK v3. diff --git a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts index a4105977ed1b8..2b9dfc3ce4a64 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts @@ -86,7 +86,7 @@ export interface ProviderProps { * * @default - a default log group created by AWS Lambda */ - readonly logGroup?: logs.ILogGroup; + readonly logGroup?: logs.ILogGroupRef; /** * The vpc to provision the lambda functions in. @@ -217,7 +217,7 @@ export class Provider extends Construct implements ICustomResourceProvider { private readonly entrypoint: lambda.Function; private readonly logRetention?: logs.RetentionDays; - private readonly logGroup?: logs.ILogGroup; + private readonly logGroup?: logs.ILogGroupRef; private readonly vpc?: ec2.IVpc; private readonly vpcSubnets?: ec2.SubnetSelection; private readonly securityGroups?: ec2.ISecurityGroup[]; diff --git a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/waiter-state-machine.ts b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/waiter-state-machine.ts index 531803b5c2dc8..50c621e7abec4 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/waiter-state-machine.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/waiter-state-machine.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { Grant, IGrantable, PolicyStatement, Role, ServicePrincipal } from '../../../aws-iam'; import { IFunction } from '../../../aws-lambda'; -import { ILogGroup, LogGroup } from '../../../aws-logs'; +import { ILogGroupRef, LogGroup } from '../../../aws-logs'; import { CfnStateMachine, LogLevel } from '../../../aws-stepfunctions'; import { Duration, Stack } from '../../../core'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; @@ -15,7 +15,7 @@ export interface LogOptions { * * @default - a new log group will be created */ - readonly destination?: ILogGroup; + readonly destination?: ILogGroupRef; /** * Determines whether execution data is included in your log. @@ -198,7 +198,7 @@ export class WaiterStateMachine extends Construct { return { destinations: [{ cloudWatchLogsLogGroup: { - logGroupArn: logGroup.logGroupArn, + logGroupArn: logGroup.logGroupRef.logGroupArn, }, }], includeExecutionData: logOptions?.includeExecutionData ?? false,