Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
357 changes: 357 additions & 0 deletions text/0859-organization-centralization-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
# AWS ObservabilityAdmin OrganizationCentralizationRule L2 Construct

* **Original Author(s)**: @jsicheng
* **Tracking Issue**: #859
* **API Bar Raiser**: @kumsmrit, @gjurova

The Amazon ObservabilityAdmin OrganizationCentralizationRule L2 construct simplifies centralization rule creation for an AWS Organization,
reducing the complexity of configuring organization-wide log centralization policies through sensible defaults, strong-typing and synthesis-time validation.

## Working Backwards

### CHANGELOG

```
feat(observabilityadmin): ObservabilityAdmin OrganizationCentralizationRule L2 construct
```

### README

#### AWS ObservabilityAdmin OrganizationCentralizationRule Construct Library

[Amazon ObservabilityAdmin OrganizationCentralizationRule](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogs_Centralization.html)
provides AWS Organization management accounts and optionally, delegated admin accounts, the ability to create log centralization rules for their
organization. Defined rules automatically replicate select CloudWatch Logs data from multiple source accounts and regions into a centralized
destination account and region. Centralization rules offers configuration flexibility to meet operational and security requirements, such as the ability
to configure a backup region and KMS encryption behavior.

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk)
project. It allows you to define ObservabilityAdmin organization centralization rules.

##### Basic Usage

```typescript
new OrganizationCentralizationRule(this, 'OrganizationCentralizationRule', {
ruleName: 'OrganizationCentralizationRule',
sourceScope: Scope.ALL,
sourceRegions: ['us-east-1', 'us-west-2'],
sourceLogGroupSelectionCriteria: LogGroupSelectionCriteria.ALL,
destinationAccount: '123456789012',
destinationRegion: 'us-east-1',
});
```

##### Advanced Configuration

```typescript
new OrganizationCentralizationRule(this, 'OrganizationCentralizationRule', {
ruleName: 'OrganizationCentralizationRule',
sourceScope: Scope.fromString(`AccountId = '012345678901'`),
sourceRegions: ['us-east-1', 'us-west-2'],
sourceLogGroupSelectionCriteria: LogGroupSelectionCriteria.fromString(`LogGroupName = 'ExactLogGroupName'`),
sourceEncryptedLogGroupStrategy: EncryptedLogGroupStrategy.ALLOW,
destinationAccount: '123456789012',
destinationRegion: 'us-east-1',
destinationLogEncryptionStrategy: LogEncryptionStrategy.CUSTOMER_MANAGED,
destinationLogEncryptionConflictResolutionStrategy: LogEncryptionConflictResolutionStrategy.ALLOW,
destinationLogEncryptionKmsKey: kms.Key.fromKeyArn(
this,
'DestinationKey',
'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012',
),
destinationBackupRegion: 'us-west-2',
destinationBackupKmsKey: kms.Key.fromKeyArn(
this,
'DestinationBackupKey',
'arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012',
),
});
```

Ticking the box below indicates that the public API of this RFC has been
signed-off by the API bar raiser (the `status/api-approved` label was applied to the
RFC pull request):

```
[ ] Signed-off by API Bar Raiser @xxxxx
```

## Public FAQ

### What are we launching today?

We are launching a new L2 construct in the `aws-observabilityadmin` module that provides a simplified, type-safe interface for creating
AWS ObservabilityAdmin OrganizationCentralizationRule resources. This construct abstracts the complexity of the underlying CloudFormation
resource and L1 construct while following AWS CDK design principles.

### Why should I use this feature?

The L2 construct enables the creation of organization centralization rules with minimal configurations while adhering to the AWS best practices.
The included types and defaults smooths out rough edges in configuring the resource using L1 constructs.

## Internal FAQ

### Why are we doing this?

AWS ObservabilityAdmin OrganizationCentralizationRule is a feature that helps organizations centralize their log data collection. Currently, users
must work with highly nested L1 parameters, which requires deep knowledge of the service's configuration options and can lead to misconfigurations.
An L2 construct will:

1. Provide a more intuitive API with sensible defaults
2. Include built-in validation to prevent common configuration errors
3. Offer better integration with CDK patterns and other constructs

### Why should we _not_ do this?

Potential concerns:

- The service is relatively new and APIs might still evolve
- The L1 construct already provides full functionality

However, these concerns are outweighed by the benefits of providing a better developer experience and encouraging adoption of
centralized log management practices.

### What is the technical solution (design) of this feature?

- Resource interfaces:

```typescript
interface ICentralizationRuleBase extends IResource, ITaggableV2 {
/**
* The name of the centralization rule.
*
* @attribute
*/
readonly ruleName: string;
/**
* The ARN of the centralization rule.
*
* @attribute
*/
readonly ruleArn: string;
}

interface IOrganizationCentralizationRule extends ICentralizationRuleBase {}
```

- Resource classes:

```typescript
abstract class CentralizationRuleBase extends Resource implements ICentralizationRuleBase {
public abstract readonly ruleName: string;
public abstract readonly ruleArn: string;
}

class OrganizationCentralizationRule extends CentralizationRuleBase implements IOrganizationCentralizationRule {
public static fromOrganizationCentralizationRuleName(scope: Construct, id: string, name: string): IOrganizationCentralizationRule;
public static fromOrganizationCentralizationRuleArn(scope: Construct, id: string, arn: string): IOrganizationCentralizationRule;
constructor(scope: Construct, id: string, props: OrganizationCentralizationRuleProps);
}
```

- Resource props:

```typescript
interface BaseCentralizationRuleProps {
/**
* The list of source regions from which telemetry data should be centralized.
*/
readonly sourceRegions: string[];
/**
* The selection criteria that specifies which source log groups to centralize. The selection criteria uses the same format as OAM link filters.
*/
readonly sourceLogGroupSelectionCriteria?: LogGroupSelectionCriteria;
/**
* A strategy determining whether to centralize source log groups that are encrypted with customer managed KMS keys (CMK).
* ALLOW will consider CMK encrypted source log groups for centralization while SKIP will skip CMK encrypted source log groups from centralization.
* @default - Skip centralizing CMK encrypted source log groups.
*/
readonly sourceEncryptedLogGroupStrategy?: EncryptedLogGroupStrategy;
/**
* The primary destination region to which telemetry data should be centralized.
*/
readonly destinationRegion: string;
/**
* Configuration that determines the encryption strategy of the destination log groups.
* CUSTOMER_MANAGED uses the configured KmsKeyArn to encrypt newly created destination log groups.
* @default - Inferred from destinationLogEncryptionKmsKeyArn.
* If destinationLogEncryptionKmsKeyArn is not provided, defaults to AWS_OWNED. Otherwise, defaults to CUSTOMER_MANAGED.
*/
readonly destinationLogEncryptionStrategy?: LogEncryptionStrategy;
/**
* Conflict resolution strategy for centralization if the encryption strategy is set to CUSTOMER_MANAGED and
* the destination log group is encrypted with an AWS_OWNED KMS Key.
* ALLOW lets centralization go through while SKIP prevents centralization into the destination log group.
* @default - Skip centralization for conflicting encryption.
*/
readonly destinationLogEncryptionConflictResolutionStrategy?: LogEncryptionConflictResolutionStrategy;
/**
* KMS IKeyRef belonging to the primary destination account and region, to encrypt newly created central log groups in the primary destination.
* @default - Log groups are encrypted with an AWS_OWNED KMS key.
*/
readonly destinationLogEncryptionKmsKey?: IKeyRef;
/**
* Logs-specific backup destination region within the primary destination account to which log data should be centralized.
* @default - no centralization backup destination region is configured.
*/
readonly destinationBackupRegion?: string;
/**
* KMS IKeyRef belonging to the primary destination account and backup region, to encrypt newly created central log groups in the backup destination.
* Only applied when destinationBackupRegion is set.
* If destinationBackupRegion is set, the backup region KMS key must be specified if destinationLogEncryptionStrategy is CUSTOMER_MANAGED.
* @default - backup destination log groups are encrypted with an AWS_OWNED KMS key.
*/
readonly destinationBackupKmsKey?: IKeyRef;
}

interface OrganizationCentralizationRuleProps extends BaseCentralizationRuleProps {
/**
* The organizational scope from which telemetry data should be centralized, specified using accounts or organizational unit ids.
*/
readonly sourceScope: Scope;
/**
* The destination account (within the organization) to which the telemetry data should be centralized.
*/
readonly destinationAccount: string;
}
```

- Helper classes:

```typescript
export class Scope {
/**
* All AWS account IDs and AWS Organization Unit IDs in the organization.
*/
public static readonly ALL = new Scope('*');
/**
* Select AWS account IDs in the organization.
*
* @param accountIds A list of AWS account IDs to include in the scope.
*/
public static fromAccountIds(accountIds: string[]): Scope {
return new Scope(`AccountId IN (${accountIds.map(id => `'${id}'`).join(', ')})`);
}
/**
* Select AWS Organization Unit IDs in the organization.
*
* @param organizationUnitIds A list of AWS Organization Unit IDs to include in the scope.
*/
public static fromOrganizationUnitIds(organizationUnitIds: string[]): Scope {
return new Scope(`OrganizationUnitId IN (${organizationUnitIds.map(id => `'${id}'`).join(', ')})`);
}
/**
* Create a scope from a string value.
*
* @param scope The scope string.
*/
public static fromString(scopeString: string): Scope {
return new Scope(scopeString);
}
protected constructor(public readonly scopeString: string) {}
}

/**
* The selection criteria that specifies which source log groups to centralize.
*/
export class LogGroupSelectionCriteria {
/**
* All log groups.
*/
public static ALL = new LogGroupSelectionCriteria('*');
/**
* Create a LogGroupSelectionCriteria from a string value.
*
* @param selectionCriteria The selection criteria string. Uses the same format as OAM link filters.
*/
public static fromString(selectionCriteria: string): LogGroupSelectionCriteria {
return new LogGroupSelectionCriteria(selectionCriteria);
}
protected constructor(public readonly selectionCriteria: string) { }
}

/**
* Strategy for determining whether to centralize source log groups that are encrypted with customer managed KMS keys (CMK).
*/
export enum EncryptedLogGroupStrategy {
/**
* Allow CMK encrypted log groups to be centralized.
*/
ALLOW = 'ALLOW',
/**
* SKIP CMK encrypted source log groups from centralization.
*/
SKIP = 'SKIP'
}

/**
* Configuration that determines the encryption strategy of the destination log groups.
*/
export enum LogEncryptionStrategy {
/**
* Use the configured KmsKeyArn to encrypt newly created destination log groups.
*/
CUSTOMER_MANAGED = 'CUSTOMER_MANAGED',
/**
* Use an AWS owned KMS key to encrypt newly created destination log groups.
*/
AWS_OWNED = 'AWS_OWNED'
}

/**
* Conflict resolution strategy for centralization if the LogEncryptionStrategy is set to CUSTOMER_MANAGED and the destination log group is
* encrypted with an AWS_OWNED KMS Key.
*/
export enum LogEncryptionConflictResolutionStrategy {
/**
* Allow source CMK encrypted log groups to be centralized despite encryption conflicts in the destination.
*/
ALLOW = 'ALLOW',
/**
* Skip CMK encrypted source log groups from centralization.
*/
SKIP = 'SKIP'
}
```

Validations:

- Required array inputs must not be empty.
- Account IDs in Scope should be 12 digit numbers.
- `destinationAccount` should be a 12 digit number.
- `sourceRegions` should not be empty.
- `sourceRegions` should contain valid AWS regions.
- `destinationRegion` should be a valid AWS region.
- `destinationBackupRegion` should be a valid AWS region.
- `destinationBackupRegion` must not be the same as the `destinationRegion`.
- If `destinationLogEncryptionStrategy` is `CUSTOMER_MANAGED`, then
- `destinationLogEncryptionKmsKey` must be provided.
- If destinationBackupRegion is set, then `destinationBackupKmsKey` must also be provided.
- `sourceLogGroupSelectionCriteria` must be defined.
- KmsKeyArn and EncryptionConflictResolutionStrategy must not be present when EncryptionStrategy is AWS_OWNED
- Both KmsKeyArn and EncryptionConflictResolutionStrategy are required when EncryptionStrategy is CUSTOMER_MANAGED

### Is this a breaking change?

No, this is a new feature that adds functionality without modifying existing APIs.

### What alternative solutions did you consider?

Keeping a simple string input for all fields without adding new types, but still keeping parameters unnested and flat.
However, many customers are likely not familiar with available options. Having classes/enums can help customers see
what options are available and avoid mistakes in configuring their rules.

### What are the drawbacks of this solution?

- **API evolution**: The underlying service is relatively new and may soon introduce new resources and fields.

### What is the high-level project plan?

- [ ] Kick off and gather feedback for RFC
- [ ] Bar raiser to sign off on RFC
- [ ] Implement L2 Construct, iterate and respond to feedback
- [ ] Merge new construct

### Are there any open issues that need to be addressed later?

- **Forward compatibility**: L2 construct must support future data sources. Some fields will be intentionally left as optional to support this.
Loading