Manage CloudWatch Logs resource policies for fine-grained access control, enabling other AWS services and external accounts to interact with log groups.
Create and manage CloudWatch Logs resource policies that control which AWS services, accounts, or IAM principals can access log groups and perform logging operations.
/**
* CloudWatch Logs resource policy for controlling access to log groups
*/
class ResourcePolicy extends Construct {
/**
* Create a CloudWatch Logs resource policy
* @param scope - Construct scope
* @param id - Construct ID
* @param props - ResourcePolicy configuration properties
*/
constructor(scope: Construct, id: string, props?: ResourcePolicyProps);
/** IAM policy document containing access rules */
readonly document: iam.PolicyDocument;
}
/**
* Properties for ResourcePolicy
*/
interface ResourcePolicyProps {
/** Name of the resource policy */
readonly resourcePolicyName?: string;
/** Initial policy statements to include */
readonly policyStatements?: iam.PolicyStatement[];
}Usage Examples:
import * as logs from '@aws-cdk/aws-logs';
import * as iam from '@aws-cdk/aws-iam';
// Create resource policy allowing services to write logs
const logsPolicy = new logs.ResourcePolicy(this, 'LogsResourcePolicy', {
resourcePolicyName: 'AllowServiceLogs',
policyStatements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [
new iam.ServicePrincipal('apigateway.amazonaws.com'),
new iam.ServicePrincipal('lambda.amazonaws.com'),
],
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents',
],
resources: ['arn:aws:logs:*:*:*'],
}),
],
});
// Add additional statements to existing policy
logsPolicy.document.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com')],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: ['arn:aws:logs:*:*:log-group:/aws/vpc/flowlogs*'],
})
);CloudWatch Logs resource policies are automatically created when using addToResourcePolicy() on log groups, providing convenient access control management.
/**
* Add a policy statement to the log group's resource policy (from LogGroup class)
* @param statement - IAM policy statement to add
*/
addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult;
/**
* Grant write permissions to this log group (from LogGroup class)
* @param grantee - Principal to grant permissions to
* @returns Grant object
*/
grantWrite(grantee: iam.IGrantable): iam.Grant;
/**
* Grant specific permissions to this log group (from LogGroup class)
* @param grantee - Principal to grant permissions to
* @param actions - Specific actions to grant
* @returns Grant object
*/
grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant;Usage Examples:
import * as logs from '@aws-cdk/aws-logs';
import * as iam from '@aws-cdk/aws-iam';
const logGroup = new logs.LogGroup(this, 'ApplicationLogs');
// Grant write access to specific service
logGroup.grantWrite(new iam.ServicePrincipal('elasticsearch.amazonaws.com'));
// Add custom policy statement
logGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com')],
actions: ['logs:CreateLogStream', 'logs:PutLogEvents', 'logs:DescribeLogStreams'],
resources: [logGroup.logGroupArn],
}));
// Grant specific actions to cross-account role
const crossAccountRole = iam.Role.fromRoleArn(
this,
'CrossAccountRole',
'arn:aws:iam::123456789012:role/LogReaderRole'
);
logGroup.grant(crossAccountRole, 'logs:DescribeLogStreams', 'logs:GetLogEvents');const apiLogGroup = new logs.LogGroup(this, 'ApiGatewayLogs');
const lambdaLogGroup = new logs.LogGroup(this, 'LambdaLogs');
const vpcLogGroup = new logs.LogGroup(this, 'VpcFlowLogs');
// API Gateway access
apiLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('apigateway.amazonaws.com')],
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:DescribeLogGroups',
'logs:DescribeLogStreams',
'logs:PutLogEvents',
'logs:GetLogEvents',
'logs:FilterLogEvents',
],
resources: [apiLogGroup.logGroupArn],
}));
// Lambda function access
lambdaLogGroup.grantWrite(new iam.ServicePrincipal('lambda.amazonaws.com'));
// VPC Flow Logs access
vpcLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com')],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [vpcLogGroup.logGroupArn],
conditions: {
StringEquals: {
'aws:SourceAccount': cdk.Stack.of(this).account,
},
},
}));const sharedLogGroup = new logs.LogGroup(this, 'SharedLogs');
// Allow specific accounts to read logs
sharedLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [
new iam.AccountPrincipal('123456789012'),
new iam.AccountPrincipal('210987654321'),
],
actions: [
'logs:DescribeLogStreams',
'logs:GetLogEvents',
'logs:FilterLogEvents',
],
resources: [sharedLogGroup.logGroupArn],
}));
// Allow organization accounts to write logs
sharedLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AnyPrincipal()],
actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [sharedLogGroup.logGroupArn],
conditions: {
StringEquals: {
'aws:PrincipalOrgID': 'o-example12345',
},
},
}));const auditLogGroup = new logs.LogGroup(this, 'AuditLogs');
// Create roles with different access levels
const logReaderRole = new iam.Role(this, 'LogReaderRole', {
assumedBy: new iam.AccountRootPrincipal(),
description: 'Role for reading audit logs',
});
const logWriterRole = new iam.Role(this, 'LogWriterRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
description: 'Role for writing audit logs',
});
const logAdminRole = new iam.Role(this, 'LogAdminRole', {
assumedBy: new iam.AccountRootPrincipal(),
description: 'Role for managing audit logs',
});
// Grant different permissions to each role
auditLogGroup.grant(logReaderRole, 'logs:DescribeLogStreams', 'logs:GetLogEvents');
auditLogGroup.grantWrite(logWriterRole);
auditLogGroup.grant(
logAdminRole,
'logs:*', // Full access for admin role
);
// Add fine-grained policy for compliance
auditLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.DENY,
principals: [new iam.AnyPrincipal()],
actions: ['logs:DeleteLogGroup', 'logs:DeleteLogStream'],
resources: [auditLogGroup.logGroupArn],
conditions: {
StringNotEquals: {
'aws:PrincipalArn': logAdminRole.roleArn,
},
},
}));const productionLogGroup = new logs.LogGroup(this, 'ProductionLogs');
// Time-based access restriction
productionLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AccountRootPrincipal()],
actions: ['logs:GetLogEvents', 'logs:FilterLogEvents'],
resources: [productionLogGroup.logGroupArn],
conditions: {
DateGreaterThan: {
'aws:CurrentTime': '2023-01-01T00:00:00Z',
},
DateLessThan: {
'aws:CurrentTime': '2024-12-31T23:59:59Z',
},
},
}));
// IP-based access restriction
productionLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AccountRootPrincipal()],
actions: ['logs:DescribeLogStreams', 'logs:GetLogEvents'],
resources: [productionLogGroup.logGroupArn],
conditions: {
IpAddress: {
'aws:SourceIp': ['203.0.113.0/24', '192.0.2.0/24'],
},
},
}));
// MFA requirement for sensitive operations
productionLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AccountRootPrincipal()],
actions: ['logs:DeleteLogGroup', 'logs:PutRetentionPolicy'],
resources: [productionLogGroup.logGroupArn],
conditions: {
Bool: {
'aws:MultiFactorAuthPresent': 'true',
},
NumericLessThan: {
'aws:MultiFactorAuthAge': '3600', // 1 hour
},
},
}));// Elasticsearch Service integration
const esLogGroup = new logs.LogGroup(this, 'ElasticsearchLogs');
esLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('es.amazonaws.com')],
actions: [
'logs:PutLogEvents',
'logs:CreateLogGroup',
'logs:CreateLogStream',
],
resources: [esLogGroup.logGroupArn],
}));
// Route 53 resolver query logging
const route53LogGroup = new logs.LogGroup(this, 'Route53QueryLogs');
route53LogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('route53resolver.amazonaws.com')],
actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [route53LogGroup.logGroupArn],
conditions: {
StringEquals: {
'aws:SourceAccount': cdk.Stack.of(this).account,
},
},
}));
// AWS Config integration
const configLogGroup = new logs.LogGroup(this, 'ConfigLogs');
configLogGroup.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('config.amazonaws.com')],
actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
resources: [configLogGroup.logGroupArn],
conditions: {
StringEquals: {
'aws:SourceAccount': cdk.Stack.of(this).account,
},
},
}));// Central resource policy for multiple services
const centralPolicy = new logs.ResourcePolicy(this, 'CentralLogsPolicy', {
resourcePolicyName: 'MultiServiceLogsPolicy',
});
// Add statements for different services
centralPolicy.document.addStatements(
// API Gateway
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('apigateway.amazonaws.com')],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: ['arn:aws:logs:*:*:log-group:/aws/apigateway/*'],
}),
// Lambda
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('lambda.amazonaws.com')],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: ['arn:aws:logs:*:*:log-group:/aws/lambda/*'],
}),
// VPC Flow Logs
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com')],
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
resources: ['arn:aws:logs:*:*:log-group:/aws/vpc/flowlogs*'],
conditions: {
StringEquals: {
'aws:SourceAccount': cdk.Stack.of(this).account,
},
},
}),
// Cross-account access for monitoring
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.AccountPrincipal('123456789012')], // Monitoring account
actions: ['logs:DescribeLogGroups', 'logs:DescribeLogStreams', 'logs:GetLogEvents'],
resources: ['arn:aws:logs:*:*:*'],
})
);