Skip to content

schubergphilis/terraform-aws-mcaf-service-quotas-manager

Repository files navigation

Service Quotas Manager

tests

Solution Architecture

This Service Quotas Manager can be installed as part as an AWS Organization or individual account to manage service quotas and become (more) demonstrably in control of them. It currently supports the following:

  1. Collection of service quotas and metrics per configured account, storing usage metrics centrally. Usage metrics can be derived from CloudWatch or AWS Config. The latter obviously requires AWS Config to be enabled in your target account.

  2. Automated discovery of used services by querying AWS Cost Explorer.

  3. Management of alarms on your service quotas with configurable thresholds per quota. Alarms can be disabled by omitting the alerting config.

  4. Optionally an SNS topic can be provided as alarm action.

  5. Automated requesting of service quota increases by configurable steps and motivations for support case updates (requires at least AWS Business Support and alarms to be enabled).

Please see supported quotas for an overview of supported services and service quotas.

Prerequisites & considerations

  • To support collection of usage metrics from AWS Config, AWS Config needs to be enabled in every target account in your organization. This is usually the case in enterprise organizations.

  • To support automated quota increase requests at least AWS Business Support is required.

  • This service quota manager relies on custom CloudWatch metrics ($0.30/metric/month) and CloudWatch alarms ($0.10/alarm/month). Services to monitor are configurable; more services monitored means increased cost.

  • Most quotas are applied per region. This Service Quota Manager operates in a single region. Install the Service Quota Manager in more regions in order to monitor quotas in more regions.

Remarks on usage collection via AWS Config

AWS Service Quotas by default only works with AWS CloudWatch. A limited set of Service Quotas have a reference to a CloudWatch metric that is collected by default or as soon as one starts using a service. A lot of service quotas however do not have metrics available. There is - for example - no metric for the number of ENI's assigned to a Lambda function, but there is a service quota for it. This tool leverages AWS config - if enabled - to collect that information; because you rather know upfront if you can request a quota increase or should re-architect your solution.

In order to collect usage from AWS Config, this tool uses the 'advanced query' functionality in AWS Config. The queries return a list of serialized JSON objects as a resultset and a JMESPath expression is used to convert that resultset to a re-usable number. Extending the queries - and thus the number of supported service quotas - is relatively easy and can be done by extending the custom collection queries file.

As an example, in custom_collection_queries.json:

{
  // The service code as defined by AWS Service Quotas. The service code can be derived from the quota ARN in the AWS console.
  "acm": {
     // The quota code as defined by AWS Service Quotas. The quota code can be derived from the quota ARN in the AWS console.
    "L-D2CB7DE9": {
      "parameters": {
        // The expression to use as advanced query. This is fed to the select_resource_config API call. It's easy to test expressions in the AWS console.
        "expression": "SELECT resourceId WHERE resourceType = 'AWS::ACM::Certificate' AND configuration.type = 'IMPORTED'",
        // The JMESPath expression to execute on the expression result. JMESPath expressions are also used by the --query flag when using the AWS CLI.
        "jmespath": "length([])"
      },
      // The type of custom collection query, to enable future support for more services.
      "type": "config"
    }
  }
}

Setup

General steps to install:

  1. Install the service quotas manager in a central account.
  2. Setup the assumable roles in all target accounts that require monitoring.

Central Account

Tip

Consider using a services account or audit account to deploy the service quotas manager in.

A minimal setup can be done like this:

module "service_quotas_manager" {
  source  = "schubergphilis/mcaf-service-quotas-manager/aws"
  version = ">= 0.1.0"

  quotas_manager_configuration = [
    {
      account_id = "123456789000"
      alerting_config = {
        default_threshold_perc = 75
        notification_topic_arn = "arn:aws:sns:eu-west-1:123456789000:service-quotas-manager-notifications"
      }
    }
  ]
}

See the examples for more examples on how to configure thresholds and auto-increase rules.

Target Accounts

Roles

This manager works by assuming roles in your target accounts from a single central management account to collect applied service quotas and usage metrics. Every account you want to be managed requires a role that can be assumed by the service quota manager. Setting up these roles is not part of this solution but could be part of - for example - your account baseline. It's already part of the MCAF account baseline.

Each role requires the following trust policy:

{
	"Version": "2012-10-17",
	"Statement": [
	  {
			"Sid": "AllowServiceQuotasManager",
			"Effect": "Allow",
			"Principal": {
				"AWS": "arn:aws:iam::<manager_account_id>:role/ServiceQuotasManagerExecutionRole-<region_name>"
			},
			"Action": "sts:AssumeRole"
		}
	]
}

Each role requires the following policies attached:

  1. AWS Managed policy ServiceQuotasReadOnlyAccess.
  2. A custom or inline policy with the following permissions:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowConfigReadAccess",
      "Effect": "Allow",
      "Action": "config:SelectResourceConfig",
      "Resource": "*"
    },
    {
      "Sid": "AllowSupportAccess",
      "Effect": "Allow",
      "Action": [
        "support:DescribeSeverityLevels",
        "support:AddCommunicationToCase"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowOptionalCeAccessForServiceAutoDiscovery",
      "Effect": "Allow",
      "Action": [
        "ce:GetCostAndUsage"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowServiceQuotaAccess",
      "Effect": "Allow",
      "Action": [
          "servicequotas:RequestServiceQuotaIncrease"
      ],
      "Resource": "*"
    }
  ]
}

Tip

We do not pin modules to versions in our examples. We highly recommend that in your code you pin the version to the exact version you are using so that your infrastructure remains stable.

Requirements

Name Version
terraform >= 1.6.0
archive >= 2.4.0
aws >= 5.25.0

Providers

Name Version
archive >= 2.4.0
aws >= 5.25.0

Modules

Name Source Version
service_quotas_manager_bucket schubergphilis/mcaf-s3/aws ~> 0.12.1
service_quotas_manager_lambda schubergphilis/mcaf-lambda/aws ~> 1.1.2

Resources

Name Type
aws_cloudwatch_event_rule.trigger_service_quotas_manager_on_alarm resource
aws_cloudwatch_event_target.trigger_service_quotas_manager_on_alarm resource
aws_iam_policy.service_quotas_manager_schedules resource
aws_iam_role.service_quotas_manager_execution_role resource
aws_iam_role.service_quotas_manager_schedules resource
aws_iam_role_policy.service_quotas_manager_execution_policy resource
aws_iam_role_policy_attachment.service_quotas_manager_schedules resource
aws_lambda_permission.trigger_service_quotas_manager_on_alarm resource
aws_s3_object.service_quotas_manager_config resource
aws_scheduler_schedule.sqm_collect_service_quotas resource
aws_scheduler_schedule_group.service_quotas_manager resource
archive_file.service_quotas_manager_source data source
aws_caller_identity.current data source
aws_region.current data source

Inputs

Name Description Type Default Required
kms_key_arn The ARN of the KMS key to use with the configuration S3 bucket and scheduler string n/a yes
quotas_manager_configuration The configuration for the service quotas manager
list(object({
account_id = string
role_name = optional(string, "ServiceQuotasManagerRole")
role_path = optional(string, "/")
selected_services = optional(list(string), [])

alerting_config = optional(object({
default_threshold_perc = number
notification_topic_arn = optional(string, "")
rules = optional(
map(
map(
object({
threshold_perc = optional(number, null)
ignore = optional(bool, false)
})
)
), {}
)
}), {
default_threshold_perc = 75
notification_topic_arn = ""
rules = {}
})
quota_increase_config = optional(map(map(object({
step = optional(number)
factor = optional(number)
motivation = string
cc_mail_addresses = list(string)
}))), {})
}))
n/a yes
bucket_name The optional name for the service quotas manager configuration bucket, overrides bucket_prefix. string null no
bucket_prefix The prefix for the service quotas manager configuration bucket. string "service-quotas-manager" no
execution_role Configuration of the IAM role to assume to execute the service quotas manager lambda
object({
name_prefix = optional(string, "ServiceQuotasManagerExecutionRole")
path = optional(string, "/")
permissions_boundary = optional(string, null)
})
{} no
schedule_timezone The timezone to schedule service quota metric collection in string "Europe/Amsterdam" no
tags Tags to assign to resources created by this module map(string) {} no

Outputs

Name Description
lambda_service_quotas_manager_role_arn ARN of the service quotas manager lambda execution role

Licensing

100% Open Source and licensed under the Apache License Version 2.0. See LICENSE for full details.