or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-log-management.mdindex.mdlog-forwarding.mdmetric-extraction.mdpattern-matching.mdquery-definitions.mdresource-policies.md
tile.json

resource-policies.mddocs/

Resource Policies and Access Control

Manage CloudWatch Logs resource policies for fine-grained access control, enabling other AWS services and external accounts to interact with log groups.

Capabilities

ResourcePolicy

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*'],
  })
);

LogGroup Resource Policy Integration

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');

Common Access Control Patterns

Service-Specific Access

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,
    },
  },
}));

Cross-Account Access

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',
    },
  },
}));

Role-Based Access

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,
    },
  },
}));

Conditional Access

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
    },
  },
}));

Service Integration Patterns

// 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,
    },
  },
}));

Multi-Service Resource Policy

// 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:*:*:*'],
  })
);