constructor()

in blogs/awsconfig-docdb/lib/amazon-documentdb-aws-config-stack.ts [20:238]


  constructor(scope: cdk.Construct, id: string, props?: DocumentDbConfigStackProps) {
    super(scope, id, props);

    const clusterParameterGroup = props?.clusterParameterGroup || 'blogpost-param-group';
    const backupRetentionPeriod = props?.backupRetentionPeriod || 7;

    // aws managed rules
    new ManagedRule(this, 'ClusterDeletionProtectionEnabled', {
      identifier: ManagedRuleIdentifiers.RDS_CLUSTER_DELETION_PROTECTION_ENABLED,
      configRuleName: 'documentdb-cluster-deletion-protection-enabled',
      ruleScope: RuleScope.fromResources([ResourceType.RDS_DB_CLUSTER])
    });

    new ManagedRule(this, 'StorageEncrypted', {
      identifier: ManagedRuleIdentifiers.RDS_STORAGE_ENCRYPTED,
      configRuleName: 'documentdb-cluster-storage-encrypted',
      ruleScope: RuleScope.fromResources([ResourceType.RDS_DB_INSTANCE])
    });

    // custom rules
    // cluster parameter group
    const clusterParameterGroupRole = new Role(this, 'ClusterParameterGroupRole', {
      assumedBy: new ServicePrincipal('lambda.amazonaws.com')
    }); 
    clusterParameterGroupRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'));
    clusterParameterGroupRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSConfigRulesExecutionRole'));

    const clusterParameterGroupFn = new Function(this, 'ClusterParameterGroupFn', {
      runtime: Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: Code.fromAsset('./lib/functions/cluster-parameter-group-rule'),
      role: clusterParameterGroupRole
    });

    new CustomRule(this, 'ClusterParameterGroupRule', {
      lambdaFunction: clusterParameterGroupFn,
      configurationChanges: true,
      configRuleName: 'documentdb-cluster-parameter-group',
      description: 'Evaluates whether the cluster parameter group is the one provided to the rule as a parameter',
      ruleScope: RuleScope.fromResources([ResourceType.RDS_DB_CLUSTER]),
      inputParameters: {
        desiredClusterParameterGroup: clusterParameterGroup
      }
    });

    // cluster backup retention
    const clusterBackupRententionRole = new Role(this, 'ClusterBackupRetentionRole', {
      assumedBy: new ServicePrincipal('lambda.amazonaws.com')
    }); 
    clusterBackupRententionRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'));
    clusterBackupRententionRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSConfigRulesExecutionRole'));

    const clusterBackupRetentionFn = new Function(this, 'ClusterBackupRetentionFn', {
      runtime: Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: Code.fromAsset('./lib/functions/cluster-backup-retention-rule'),
      role: clusterBackupRententionRole
    });

    new CustomRule(this, 'ClusterBackupRetentionRule', {
      lambdaFunction: clusterBackupRetentionFn,
      configurationChanges: true,
      configRuleName: 'documentdb-cluster-backup-retention',
      description: 'Evaluates whether the backup retention policy has been set to a greater value than the one provided to the as parameter',
      ruleScope: RuleScope.fromResources([ResourceType.RDS_DB_CLUSTER]),
      inputParameters: {
        minBackupRetentionPeriod: backupRetentionPeriod
      }
    });

    // instances homogeneous
    const instancesHomogeneousRole = new Role(this, 'InstancesHomogeneousRole', {
      assumedBy: new ServicePrincipal('lambda.amazonaws.com')
    }); 
    instancesHomogeneousRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'));
    instancesHomogeneousRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSConfigRulesExecutionRole'));
    instancesHomogeneousRole.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: ['rds:DescribeDBInstances'],
      resources: ['*']
    }));

    const instancesHomogeneousFn = new Function(this, 'InstancesHomogeneousFn', {
      runtime: Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: Code.fromAsset('./lib/functions/instances-homogeneous-rule'),
      role: instancesHomogeneousRole
    });

    new CustomRule(this, 'InstancesHomogeneousRule', {
      lambdaFunction: instancesHomogeneousFn,
      configurationChanges: true,
      configRuleName: 'documentdb-cluster-instances-homogeneous',
      description: 'Evaluates whether all instances within an Amazon DocumentDB cluster belong to the same instance family and size.',
      ruleScope: RuleScope.fromResources([ResourceType.RDS_DB_INSTANCE])
    });

    // remediation
    // sns topic for notifications
    const key = new Key(this, 'Key');
    key.addToResourcePolicy(new PolicyStatement({
      actions: [
        'kms:GenerateDataKey',
        'kms:Decrypt'
      ],
      principals: [new ServicePrincipal('events.amazonaws.com')],
      resources: ['*'],
    }));

    const topic = new Topic(this, 'ComplianceNotificationsTopic', {
      displayName: 'Compliance Notifications',
      masterKey: key
    });

    // cloudwatch log group for debugging purposes
    const logGroup = new LogGroup(this, 'AuditLogGroup', {
      logGroupName: `/aws/events/documentdb-config-events`,
      retention: RetentionDays.ONE_WEEK
    });

    const notificationRule = new Rule(this, 'ComplianceNotificationRule', {
      eventPattern: {
        source: ['aws.config'],
        detailType: ['Config Rules Compliance Change'],
        detail: {
          messageType: ['ComplianceChangeNotification'],
          newEvaluationResult: {
            complianceType: ['NON_COMPLIANT'],
            evaluationResultIdentifier: {
              evaluationResultQualifier: {
                configRuleName: [{prefix: 'documentdb-'}]
              }
            }
          },
          resourceType: ['AWS::RDS::DBCluster', 'AWS::RDS::DBInstance']
        }
      }
    });

    notificationRule.addTarget(new CloudWatchLogGroup(logGroup));
    notificationRule.addTarget(new SnsTopic(topic));

    // parameter group remediation
    // (the IAM role below can be shared among both lambda functions that remediate
    // wrong parameter group and deletion protection disabled as they both perform the
    // same operations and thus require same IAM permissions with current implementation)
    const remediationRole = new Role(this, 'ParameterGroupRemediationRole', {
      assumedBy: new ServicePrincipal('lambda.amazonaws.com')
    }); 
    remediationRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'));
    remediationRole.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: [
        'rds:DescribeDBClusters',
        'rds:ModifyDBCluster'
      ],
      resources: ['*']
    }));

    const parameterGroupRemediationFn = new Function(this, 'ParameterGroupRemediationFn', {
      runtime: Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: Code.fromAsset('./lib/functions/cluster-parameter-group-remediation'),
      role: remediationRole,
      environment: {
        DESIRED_CLUSTER_PARAMETER_GROUP: clusterParameterGroup
      }
    });

    const parameterGroupRule = new Rule(this, 'ParameterGroupRule', {
      eventPattern: {
        source: ['aws.config'],
        detailType: ['Config Rules Compliance Change'],
        detail: {
          messageType: ['ComplianceChangeNotification'],
          newEvaluationResult: {
            evaluationResultIdentifier: {
              evaluationResultQualifier: {
                configRuleName: ['documentdb-cluster-parameter-group']
              }
            },
            complianceType: ['NON_COMPLIANT']
          },
          resourceType: ['AWS::RDS::DBCluster']
        }
      }
    });

    parameterGroupRule.addTarget(new LambdaFunction(parameterGroupRemediationFn));

    // cluster deletion protection remediation
    const deletionProtectionRemediationFn = new Function(this, 'DeletionProtectionRemediationFn', {
      runtime: Runtime.NODEJS_14_X,
      handler: 'index.handler',
      code: Code.fromAsset('./lib/functions/cluster-deletion-protection-remediation'),
      role: remediationRole
    });

    const deletionProtectionRule = new Rule(this, 'DelectionProtectionRule', {
      eventPattern: {
        source: ['aws.config'],
        detailType: ['Config Rules Compliance Change'],
        detail: {
          messageType: ['ComplianceChangeNotification'],
          newEvaluationResult: {
            evaluationResultIdentifier: {
              evaluationResultQualifier: {
                configRuleName: ['documentdb-cluster-deletion-protection-enabled']
              }
            },
            complianceType: ['NON_COMPLIANT']
          },
          resourceType: ['AWS::RDS::DBCluster']
        }
      }
    });

    deletionProtectionRule.addTarget(new LambdaFunction(deletionProtectionRemediationFn));
  }