packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts (576 lines of code) (raw):
import { Construct } from 'constructs';
import { ApplicationListener, BaseApplicationListenerProps } from './application-listener';
import { ListenerAction } from './application-listener-action';
import * as cloudwatch from '../../../aws-cloudwatch';
import * as ec2 from '../../../aws-ec2';
import { PolicyStatement } from '../../../aws-iam/lib/policy-statement';
import { ServicePrincipal } from '../../../aws-iam/lib/principals';
import * as s3 from '../../../aws-s3';
import * as cxschema from '../../../cloud-assembly-schema';
import { CfnResource, Duration, Lazy, Names, Resource, Stack, Token } from '../../../core';
import { ValidationError } from '../../../core/lib/errors';
import { addConstructMetadata, MethodMetadata } from '../../../core/lib/metadata-resource';
import * as cxapi from '../../../cx-api';
import { ApplicationELBMetrics } from '../elasticloadbalancingv2-canned-metrics.generated';
import { BaseLoadBalancer, BaseLoadBalancerLookupOptions, BaseLoadBalancerProps, ILoadBalancerV2 } from '../shared/base-load-balancer';
import { IpAddressType, ApplicationProtocol, DesyncMitigationMode } from '../shared/enums';
import { parseLoadBalancerFullName } from '../shared/util';
/**
* Properties for defining an Application Load Balancer
*
* @see https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#load-balancer-attributes
*/
export interface ApplicationLoadBalancerProps extends BaseLoadBalancerProps {
/**
* Security group to associate with this load balancer
*
* @default A security group is created
*/
readonly securityGroup?: ec2.ISecurityGroup;
/**
* The type of IP addresses to use
*
* @default IpAddressType.IPV4
*/
readonly ipAddressType?: IpAddressType;
/**
* Indicates whether HTTP/2 is enabled.
*
* @default true
*/
readonly http2Enabled?: boolean;
/**
* The load balancer idle timeout, in seconds
*
* @default 60
*/
readonly idleTimeout?: Duration;
/**
* Indicates whether HTTP headers with invalid header fields are removed
* by the load balancer (true) or routed to targets (false)
*
* @default false
*/
readonly dropInvalidHeaderFields?: boolean;
/**
* Determines how the load balancer handles requests that
* might pose a security risk to your application
*
* @default DesyncMitigationMode.DEFENSIVE
*/
readonly desyncMitigationMode?: DesyncMitigationMode;
/**
* The client keep alive duration. The valid range is 60 to 604800 seconds (1 minute to 7 days).
*
* @default - Duration.seconds(3600)
*/
readonly clientKeepAlive?: Duration;
/**
* Indicates whether the Application Load Balancer should preserve the host header in the HTTP request
* and send it to the target without any change.
*
* @default false
*/
readonly preserveHostHeader?: boolean;
/**
* Indicates whether the two headers (x-amzn-tls-version and x-amzn-tls-cipher-suite),
* which contain information about the negotiated TLS version and cipher suite,
* are added to the client request before sending it to the target.
*
* The x-amzn-tls-version header has information about the TLS protocol version negotiated with the client,
* and the x-amzn-tls-cipher-suite header has information about the cipher suite negotiated with the client.
*
* Both headers are in OpenSSL format.
*
* @default false
*/
readonly xAmznTlsVersionAndCipherSuiteHeaders?: boolean;
/**
* Indicates whether the X-Forwarded-For header should preserve the source port
* that the client used to connect to the load balancer.
*
* @default false
*/
readonly preserveXffClientPort?: boolean;
/**
* Enables you to modify, preserve, or remove the X-Forwarded-For header in the HTTP request
* before the Application Load Balancer sends the request to the target.
*
* @default XffHeaderProcessingMode.APPEND
*/
readonly xffHeaderProcessingMode?: XffHeaderProcessingMode;
/**
* Indicates whether to allow a WAF-enabled load balancer to route requests to targets
* if it is unable to forward the request to AWS WAF.
*
* @default false
*/
readonly wafFailOpen?: boolean;
}
/**
* Processing mode of the X-Forwarded-For header in the HTTP request
* before the Application Load Balancer sends the request to the target.
*/
export enum XffHeaderProcessingMode {
/**
* Application Load Balancer adds the client IP address (of the last hop) to the X-Forwarded-For header
* in the HTTP request before it sends it to targets.
*/
APPEND = 'append',
/**
* Application Load Balancer preserves the X-Forwarded-For header in the HTTP request,
* and sends it to targets without any change.
*/
PRESERVE = 'preserve',
/**
* Application Load Balancer removes the X-Forwarded-For header
* in the HTTP request before it sends it to targets.
*/
REMOVE = 'remove',
}
/**
* Options for looking up an ApplicationLoadBalancer
*/
export interface ApplicationLoadBalancerLookupOptions extends BaseLoadBalancerLookupOptions {
}
/**
* Define an Application Load Balancer
*
* @resource AWS::ElasticLoadBalancingV2::LoadBalancer
*/
export class ApplicationLoadBalancer extends BaseLoadBalancer implements IApplicationLoadBalancer {
/**
* Look up an application load balancer.
*/
public static fromLookup(scope: Construct, id: string, options: ApplicationLoadBalancerLookupOptions): IApplicationLoadBalancer {
const props = BaseLoadBalancer._queryContextProvider(scope, {
userOptions: options,
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
});
return new LookedUpApplicationLoadBalancer(scope, id, props);
}
/**
* Import an existing Application Load Balancer
*/
public static fromApplicationLoadBalancerAttributes(
scope: Construct, id: string, attrs: ApplicationLoadBalancerAttributes): IApplicationLoadBalancer {
return new ImportedApplicationLoadBalancer(scope, id, attrs);
}
public readonly connections: ec2.Connections;
public readonly ipAddressType?: IpAddressType;
public readonly listeners: ApplicationListener[];
public readonly metrics: IApplicationLoadBalancerMetrics;
constructor(scope: Construct, id: string, props: ApplicationLoadBalancerProps) {
super(scope, id, props, {
type: 'application',
securityGroups: Lazy.list({ produce: () => this.connections.securityGroups.map(sg => sg.securityGroupId) }),
ipAddressType: props.ipAddressType,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
const minimumCapacityUnit = props.minimumCapacityUnit;
if (
minimumCapacityUnit &&
!Token.isUnresolved(minimumCapacityUnit) &&
(!Number.isInteger(minimumCapacityUnit) || minimumCapacityUnit < 100 || minimumCapacityUnit > 1500)
) {
throw new ValidationError(`'minimumCapacityUnit' must be a positive integer between 100 and 1500 for Application Load Balancer, got: ${minimumCapacityUnit}.`, this);
}
this.ipAddressType = props.ipAddressType ?? IpAddressType.IPV4;
if (props.ipAddressType === IpAddressType.DUAL_STACK_WITHOUT_PUBLIC_IPV4 && !props.internetFacing) {
throw new ValidationError('dual-stack without public IPv4 address can only be used with internet-facing scheme.', this);
}
const securityGroups = [props.securityGroup || new ec2.SecurityGroup(this, 'SecurityGroup', {
vpc: props.vpc,
description: `Automatically created Security Group for ELB ${Names.uniqueId(this)}`,
allowAllOutbound: false,
})];
this.connections = new ec2.Connections({ securityGroups });
this.listeners = [];
this.metrics = new ApplicationLoadBalancerMetrics(this, this.loadBalancerFullName);
if (props.http2Enabled !== undefined) { this.setAttribute('routing.http2.enabled', props.http2Enabled ? 'true' : 'false'); }
if (props.idleTimeout !== undefined) { this.setAttribute('idle_timeout.timeout_seconds', props.idleTimeout.toSeconds().toString()); }
if (props.dropInvalidHeaderFields) { this.setAttribute('routing.http.drop_invalid_header_fields.enabled', 'true'); }
if (props.desyncMitigationMode !== undefined) { this.setAttribute('routing.http.desync_mitigation_mode', props.desyncMitigationMode); }
if (props.preserveHostHeader) { this.setAttribute('routing.http.preserve_host_header.enabled', 'true'); }
if (props.xAmznTlsVersionAndCipherSuiteHeaders) { this.setAttribute('routing.http.x_amzn_tls_version_and_cipher_suite.enabled', 'true'); }
if (props.preserveXffClientPort) { this.setAttribute('routing.http.xff_client_port.enabled', 'true'); }
if (props.xffHeaderProcessingMode !== undefined) { this.setAttribute('routing.http.xff_header_processing.mode', props.xffHeaderProcessingMode); }
if (props.wafFailOpen) { this.setAttribute('waf.fail_open.enabled', 'true'); }
if (props.clientKeepAlive !== undefined) {
const clientKeepAliveInMillis = props.clientKeepAlive.toMilliseconds();
if (clientKeepAliveInMillis < 1000) {
throw new ValidationError(`\'clientKeepAlive\' must be between 60 and 604800 seconds. Got: ${clientKeepAliveInMillis} milliseconds`, this);
}
const clientKeepAliveInSeconds = props.clientKeepAlive.toSeconds();
if (clientKeepAliveInSeconds < 60 || clientKeepAliveInSeconds > 604800) {
throw new ValidationError(`\'clientKeepAlive\' must be between 60 and 604800 seconds. Got: ${clientKeepAliveInSeconds} seconds`, this);
}
this.setAttribute('client_keep_alive.seconds', clientKeepAliveInSeconds.toString());
}
if (props.crossZoneEnabled === false) {
throw new ValidationError('crossZoneEnabled cannot be false with Application Load Balancers.', this);
}
}
/**
* Add a new listener to this load balancer
*/
@MethodMetadata()
public addListener(id: string, props: BaseApplicationListenerProps): ApplicationListener {
const listener = new ApplicationListener(this, id, {
loadBalancer: this,
...props,
});
this.listeners.push(listener);
return listener;
}
/**
* Add a redirection listener to this load balancer
*/
@MethodMetadata()
public addRedirect(props: ApplicationLoadBalancerRedirectConfig = {}): ApplicationListener {
const sourcePort = props.sourcePort ?? 80;
const targetPort = (props.targetPort ?? 443).toString();
return this.addListener(`Redirect${sourcePort}To${targetPort}`, {
protocol: props.sourceProtocol ?? ApplicationProtocol.HTTP,
port: sourcePort,
open: props.open ?? true,
defaultAction: ListenerAction.redirect({
port: targetPort,
protocol: props.targetProtocol ?? ApplicationProtocol.HTTPS,
permanent: true,
}),
});
}
/**
* Enable access logging for this load balancer.
*
* A region must be specified on the stack containing the load balancer; you cannot enable logging on
* environment-agnostic stacks. See https://docs.aws.amazon.com/cdk/latest/guide/environments.html
*/
@MethodMetadata()
public logAccessLogs(bucket: s3.IBucket, prefix?: string) {
/**
* KMS key encryption is not supported on Access Log bucket for ALB, the bucket must use Amazon S3-managed keys (SSE-S3).
* See https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-access-logging.html#bucket-permissions-troubleshooting
*/
if (bucket.encryptionKey) {
throw new ValidationError('Encryption key detected. Bucket encryption using KMS keys is unsupported', this);
}
prefix = prefix || '';
this.setAttribute('access_logs.s3.enabled', 'true');
this.setAttribute('access_logs.s3.bucket', bucket.bucketName.toString());
this.setAttribute('access_logs.s3.prefix', prefix);
const logsDeliveryServicePrincipal = new ServicePrincipal('delivery.logs.amazonaws.com');
bucket.addToResourcePolicy(new PolicyStatement({
actions: ['s3:PutObject'],
principals: [this.resourcePolicyPrincipal()],
resources: [
bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${Stack.of(this).account}/*`),
],
}));
bucket.addToResourcePolicy(
new PolicyStatement({
actions: ['s3:PutObject'],
principals: [logsDeliveryServicePrincipal],
resources: [
bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${this.env.account}/*`),
],
conditions: {
StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' },
},
}),
);
bucket.addToResourcePolicy(
new PolicyStatement({
actions: ['s3:GetBucketAcl'],
principals: [logsDeliveryServicePrincipal],
resources: [bucket.bucketArn],
}),
);
// make sure the bucket's policy is created before the ALB (see https://github.com/aws/aws-cdk/issues/1633)
// at the L1 level to avoid creating a circular dependency (see https://github.com/aws/aws-cdk/issues/27528
// and https://github.com/aws/aws-cdk/issues/27928)
const lb = this.node.defaultChild;
const bucketPolicy = bucket.policy?.node.defaultChild;
if (lb && bucketPolicy && CfnResource.isCfnResource(lb) && CfnResource.isCfnResource(bucketPolicy)) {
lb.addDependency(bucketPolicy);
}
}
/**
* Enable connection logging for this load balancer.
*
* A region must be specified on the stack containing the load balancer; you cannot enable logging on
* environment-agnostic stacks.
*
* @see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
*/
@MethodMetadata()
public logConnectionLogs(bucket: s3.IBucket, prefix?: string) {
/**
* KMS key encryption is not supported on Connection Log bucket for ALB, the bucket must use Amazon S3-managed keys (SSE-S3).
* See https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-connection-logging.html#bucket-permissions-troubleshooting-connection
*/
if (bucket.encryptionKey) {
throw new ValidationError('Encryption key detected. Bucket encryption using KMS keys is unsupported', this);
}
prefix = prefix || '';
this.setAttribute('connection_logs.s3.enabled', 'true');
this.setAttribute('connection_logs.s3.bucket', bucket.bucketName.toString());
this.setAttribute('connection_logs.s3.prefix', prefix);
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-connection-logging.html
const logsDeliveryServicePrincipal = new ServicePrincipal('delivery.logs.amazonaws.com');
bucket.addToResourcePolicy(new PolicyStatement({
actions: ['s3:PutObject'],
principals: [this.resourcePolicyPrincipal()],
resources: [
bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${Stack.of(this).account}/*`),
],
}));
// We still need this policy for the bucket using the ACL
bucket.addToResourcePolicy(
new PolicyStatement({
actions: ['s3:PutObject'],
principals: [logsDeliveryServicePrincipal],
resources: [
bucket.arnForObjects(`${prefix ? prefix + '/' : ''}AWSLogs/${Stack.of(this).account}/*`),
],
conditions: {
StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' },
},
}),
);
bucket.addToResourcePolicy(
new PolicyStatement({
actions: ['s3:GetBucketAcl'],
principals: [logsDeliveryServicePrincipal],
resources: [bucket.bucketArn],
}),
);
// make sure the bucket's policy is created before the ALB (see https://github.com/aws/aws-cdk/issues/1633)
// at the L1 level to avoid creating a circular dependency (see https://github.com/aws/aws-cdk/issues/27528
// and https://github.com/aws/aws-cdk/issues/27928)
const lb = this.node.defaultChild;
const bucketPolicy = bucket.policy?.node.defaultChild;
if (lb && bucketPolicy && CfnResource.isCfnResource(lb) && CfnResource.isCfnResource(bucketPolicy)) {
lb.addDependency(bucketPolicy);
}
}
/**
* Add a security group to this load balancer
*/
@MethodMetadata()
public addSecurityGroup(securityGroup: ec2.ISecurityGroup) {
this.connections.addSecurityGroup(securityGroup);
}
/**
* Return the given named metric for this Application Load Balancer
*
* @default Average over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.custom`` instead
*/
@MethodMetadata()
public metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric {
return this.metrics.custom(metricName, props);
}
/**
* The total number of concurrent TCP connections active from clients to the
* load balancer and from the load balancer to targets.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.activeConnectionCount`` instead
*/
@MethodMetadata()
public metricActiveConnectionCount(props?: cloudwatch.MetricOptions) {
return this.metrics.activeConnectionCount(props);
}
/**
* The number of TLS connections initiated by the client that did not
* establish a session with the load balancer. Possible causes include a
* mismatch of ciphers or protocols.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.clientTlsNegotiationErrorCount`` instead
*/
@MethodMetadata()
public metricClientTlsNegotiationErrorCount(props?: cloudwatch.MetricOptions) {
return this.metrics.clientTlsNegotiationErrorCount(props);
}
/**
* The number of load balancer capacity units (LCU) used by your load balancer.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.consumedLCUs`` instead
*/
@MethodMetadata()
public metricConsumedLCUs(props?: cloudwatch.MetricOptions) {
return this.metrics.consumedLCUs(props);
}
/**
* The number of fixed-response actions that were successful.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.httpFixedResponseCount`` instead
*/
@MethodMetadata()
public metricHttpFixedResponseCount(props?: cloudwatch.MetricOptions) {
return this.metrics.httpFixedResponseCount(props);
}
/**
* The number of redirect actions that were successful.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.httpRedirectCount`` instead
*/
@MethodMetadata()
public metricHttpRedirectCount(props?: cloudwatch.MetricOptions) {
return this.metrics.httpRedirectCount(props);
}
/**
* The number of redirect actions that couldn't be completed because the URL
* in the response location header is larger than 8K.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.httpRedirectUrlLimitExceededCount`` instead
*/
@MethodMetadata()
public metricHttpRedirectUrlLimitExceededCount(props?: cloudwatch.MetricOptions) {
return this.metrics.httpRedirectUrlLimitExceededCount(props);
}
/**
* The number of HTTP 3xx/4xx/5xx codes that originate from the load balancer.
*
* This does not include any response codes generated by the targets.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.httpCodeElb`` instead
*/
@MethodMetadata()
public metricHttpCodeElb(code: HttpCodeElb, props?: cloudwatch.MetricOptions) {
return this.metrics.httpCodeElb(code, props);
}
/**
* The number of HTTP 2xx/3xx/4xx/5xx response codes generated by all targets
* in the load balancer.
*
* This does not include any response codes generated by the load balancer.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.httpCodeTarget`` instead
*/
@MethodMetadata()
public metricHttpCodeTarget(code: HttpCodeTarget, props?: cloudwatch.MetricOptions) {
return this.metrics.httpCodeTarget(code, props);
}
/**
* The total number of bytes processed by the load balancer over IPv6.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.ipv6ProcessedBytes`` instead
*/
@MethodMetadata()
public metricIpv6ProcessedBytes(props?: cloudwatch.MetricOptions) {
return this.metrics.ipv6ProcessedBytes(props);
}
/**
* The number of IPv6 requests received by the load balancer.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.ipv6RequestCount`` instead
*/
@MethodMetadata()
public metricIpv6RequestCount(props?: cloudwatch.MetricOptions) {
return this.metrics.ipv6RequestCount(props);
}
/**
* The total number of new TCP connections established from clients to the
* load balancer and from the load balancer to targets.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.newConnectionCount`` instead
*/
@MethodMetadata()
public metricNewConnectionCount(props?: cloudwatch.MetricOptions) {
return this.metrics.newConnectionCount(props);
}
/**
* The total number of bytes processed by the load balancer over IPv4 and IPv6.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.processedBytes`` instead
*/
@MethodMetadata()
public metricProcessedBytes(props?: cloudwatch.MetricOptions) {
return this.metrics.processedBytes(props);
}
/**
* The number of connections that were rejected because the load balancer had
* reached its maximum number of connections.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.rejectedConnectionCount`` instead
*/
@MethodMetadata()
public metricRejectedConnectionCount(props?: cloudwatch.MetricOptions) {
return this.metrics.rejectedConnectionCount(props);
}
/**
* The number of requests processed over IPv4 and IPv6.
*
* This count includes only the requests with a response generated by a target of the load balancer.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.requestCount`` instead
*/
@MethodMetadata()
public metricRequestCount(props?: cloudwatch.MetricOptions) {
return this.metrics.requestCount(props);
}
/**
* The number of rules processed by the load balancer given a request rate averaged over an hour.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.ruleEvaluations`` instead
*/
@MethodMetadata()
public metricRuleEvaluations(props?: cloudwatch.MetricOptions) {
return this.metrics.ruleEvaluations(props);
}
/**
* The number of connections that were not successfully established between the load balancer and target.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.targetConnectionErrorCount`` instead
*/
@MethodMetadata()
public metricTargetConnectionErrorCount(props?: cloudwatch.MetricOptions) {
return this.metrics.targetConnectionErrorCount(props);
}
/**
* The time elapsed, in seconds, after the request leaves the load balancer until a response from the target is received.
*
* @default Average over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.targetResponseTime`` instead
*/
@MethodMetadata()
public metricTargetResponseTime(props?: cloudwatch.MetricOptions) {
return this.metrics.targetResponseTime(props);
}
/**
* The number of TLS connections initiated by the load balancer that did not establish a session with the target.
*
* Possible causes include a mismatch of ciphers or protocols.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.targetTLSNegotiationErrorCount`` instead
*/
@MethodMetadata()
public metricTargetTLSNegotiationErrorCount(props?: cloudwatch.MetricOptions) {
return this.metrics.targetTLSNegotiationErrorCount(props);
}
/**
* The number of user authentications that could not be completed
*
* Because an authenticate action was misconfigured, the load balancer
* couldn't establish a connection with the IdP, or the load balancer
* couldn't complete the authentication flow due to an internal error.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.elbAuthError`` instead
*/
@MethodMetadata()
public metricElbAuthError(props?: cloudwatch.MetricOptions) {
return this.metrics.elbAuthError(props);
}
/**
* The number of user authentications that could not be completed because the
* IdP denied access to the user or an authorization code was used more than
* once.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.elbAuthFailure`` instead
*/
@MethodMetadata()
public metricElbAuthFailure(props?: cloudwatch.MetricOptions) {
return this.metrics.elbAuthFailure(props);
}
/**
* The time elapsed, in milliseconds, to query the IdP for the ID token and user info.
*
* If one or more of these operations fail, this is the time to failure.
*
* @default Average over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.elbAuthLatency`` instead
*/
@MethodMetadata()
public metricElbAuthLatency(props?: cloudwatch.MetricOptions) {
return this.metrics.elbAuthLatency(props);
}
/**
* The number of authenticate actions that were successful.
*
* This metric is incremented at the end of the authentication workflow,
* after the load balancer has retrieved the user claims from the IdP.
*
* @default Sum over 5 minutes
* @deprecated Use ``ApplicationLoadBalancer.metrics.elbAuthSuccess`` instead
*
*/
@MethodMetadata()
public metricElbAuthSuccess(props?: cloudwatch.MetricOptions) {
return this.metrics.elbAuthSuccess(props);
}
}
class ApplicationLoadBalancerMetrics implements IApplicationLoadBalancerMetrics {
private readonly scope: Construct;
private readonly loadBalancerFullName: string;
constructor(scope: Construct, loadBalancerFullName: string) {
this.scope = scope;
this.loadBalancerFullName = loadBalancerFullName;
}
/**
* Return the given named metric for this Application Load Balancer
*
* @default Average over 5 minutes
*/
public custom(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric {
return new cloudwatch.Metric({
namespace: 'AWS/ApplicationELB',
metricName,
dimensionsMap: { LoadBalancer: this.loadBalancerFullName },
...props,
});
}
public activeConnectionCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.activeConnectionCountSum, props);
}
public clientTlsNegotiationErrorCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.clientTlsNegotiationErrorCountSum, props);
}
public consumedLCUs(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.consumedLcUsAverage, {
statistic: 'sum',
...props,
});
}
public httpFixedResponseCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.httpFixedResponseCountSum, props);
}
public httpRedirectCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.httpRedirectCountSum, props);
}
public httpRedirectUrlLimitExceededCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.httpRedirectUrlLimitExceededCountSum, props);
}
public httpCodeElb(code: HttpCodeElb, props?: cloudwatch.MetricOptions) {
return this.custom(code, {
statistic: 'Sum',
...props,
});
}
public httpCodeTarget(code: HttpCodeTarget, props?: cloudwatch.MetricOptions) {
return this.custom(code, {
statistic: 'Sum',
...props,
});
}
public ipv6ProcessedBytes(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.iPv6ProcessedBytesSum, props);
}
public ipv6RequestCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.iPv6RequestCountSum, props);
}
public newConnectionCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.newConnectionCountSum, props);
}
public processedBytes(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.processedBytesSum, props);
}
public rejectedConnectionCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.rejectedConnectionCountSum, props);
}
public requestCount(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.requestCountSum, props);
}
public ruleEvaluations(props?: cloudwatch.MetricOptions) {
return this.cannedMetric(ApplicationELBMetrics.ruleEvaluationsSum, props);
}
public targetConnectionErrorCount(props?: cloudwatch.MetricOptions) {
return this.custom('TargetConnectionErrorCount', {
statistic: 'Sum',
...props,
});
}
public targetResponseTime(props?: cloudwatch.MetricOptions) {
return this.custom('TargetResponseTime', {
statistic: 'Average',
...props,
});
}
public targetTLSNegotiationErrorCount(props?: cloudwatch.MetricOptions) {
return this.custom('TargetTLSNegotiationErrorCount', {
statistic: 'Sum',
...props,
});
}
public elbAuthError(props?: cloudwatch.MetricOptions) {
return this.custom('ELBAuthError', {
statistic: 'Sum',
...props,
});
}
public elbAuthFailure(props?: cloudwatch.MetricOptions) {
return this.custom('ELBAuthFailure', {
statistic: 'Sum',
...props,
});
}
public elbAuthLatency(props?: cloudwatch.MetricOptions) {
return this.custom('ELBAuthLatency', {
statistic: 'Average',
...props,
});
}
public elbAuthSuccess(props?: cloudwatch.MetricOptions) {
return this.custom('ELBAuthSuccess', {
statistic: 'Sum',
...props,
});
}
private cannedMetric(
fn: (dims: { LoadBalancer: string }) => cloudwatch.MetricProps,
props?: cloudwatch.MetricOptions,
): cloudwatch.Metric {
return new cloudwatch.Metric({
...fn({ LoadBalancer: this.loadBalancerFullName }),
...props,
}).attachTo(this.scope);
}
}
/**
* Count of HTTP status originating from the load balancer
*
* This count does not include any response codes generated by the targets.
*/
export enum HttpCodeElb {
/**
* The number of HTTP 3XX redirection codes that originate from the load balancer.
*/
ELB_3XX_COUNT = 'HTTPCode_ELB_3XX_Count',
/**
* The number of HTTP 4XX client error codes that originate from the load balancer.
*
* Client errors are generated when requests are malformed or incomplete.
* These requests have not been received by the target. This count does not
* include any response codes generated by the targets.
*/
ELB_4XX_COUNT = 'HTTPCode_ELB_4XX_Count',
/**
* The number of HTTP 5XX server error codes that originate from the load balancer.
*/
ELB_5XX_COUNT = 'HTTPCode_ELB_5XX_Count',
/**
* The number of HTTP 500 server error codes that originate from the load balancer.
*/
ELB_500_COUNT = 'HTTPCode_ELB_500_Count',
/**
* The number of HTTP 502 server error codes that originate from the load balancer.
*/
ELB_502_COUNT = 'HTTPCode_ELB_502_Count',
/**
* The number of HTTP 503 server error codes that originate from the load balancer.
*/
ELB_503_COUNT = 'HTTPCode_ELB_503_Count',
/**
* The number of HTTP 504 server error codes that originate from the load balancer.
*/
ELB_504_COUNT = 'HTTPCode_ELB_504_Count',
}
/**
* Count of HTTP status originating from the targets
*/
export enum HttpCodeTarget {
/**
* The number of 2xx response codes from targets
*/
TARGET_2XX_COUNT = 'HTTPCode_Target_2XX_Count',
/**
* The number of 3xx response codes from targets
*/
TARGET_3XX_COUNT = 'HTTPCode_Target_3XX_Count',
/**
* The number of 4xx response codes from targets
*/
TARGET_4XX_COUNT = 'HTTPCode_Target_4XX_Count',
/**
* The number of 5xx response codes from targets
*/
TARGET_5XX_COUNT = 'HTTPCode_Target_5XX_Count',
}
/**
* Contains all metrics for an Application Load Balancer.
*/
export interface IApplicationLoadBalancerMetrics {
/**
* Return the given named metric for this Application Load Balancer
*
* @default Average over 5 minutes
*/
custom(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The total number of concurrent TCP connections active from clients to the
* load balancer and from the load balancer to targets.
*
* @default Sum over 5 minutes
*/
activeConnectionCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of TLS connections initiated by the client that did not
* establish a session with the load balancer. Possible causes include a
* mismatch of ciphers or protocols.
*
* @default Sum over 5 minutes
*/
clientTlsNegotiationErrorCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of load balancer capacity units (LCU) used by your load balancer.
*
* @default Sum over 5 minutes
*/
consumedLCUs(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of fixed-response actions that were successful.
*
* @default Sum over 5 minutes
*/
httpFixedResponseCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of redirect actions that were successful.
*
* @default Sum over 5 minutes
*/
httpRedirectCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of redirect actions that couldn't be completed because the URL
* in the response location header is larger than 8K.
*
* @default Sum over 5 minutes
*/
httpRedirectUrlLimitExceededCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of HTTP 3xx/4xx/5xx codes that originate from the load balancer.
*
* This does not include any response codes generated by the targets.
*
* @default Sum over 5 minutes
*/
httpCodeElb(code: HttpCodeElb, props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of HTTP 2xx/3xx/4xx/5xx response codes generated by all targets
* in the load balancer.
*
* This does not include any response codes generated by the load balancer.
*
* @default Sum over 5 minutes
*/
httpCodeTarget(code: HttpCodeTarget, props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The total number of bytes processed by the load balancer over IPv6.
*
* @default Sum over 5 minutes
*/
ipv6ProcessedBytes(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of IPv6 requests received by the load balancer.
*
* @default Sum over 5 minutes
*/
ipv6RequestCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The total number of new TCP connections established from clients to the
* load balancer and from the load balancer to targets.
*
* @default Sum over 5 minutes
*/
newConnectionCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The total number of bytes processed by the load balancer over IPv4 and IPv6.
*
* @default Sum over 5 minutes
*/
processedBytes(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of connections that were rejected because the load balancer had
* reached its maximum number of connections.
*
* @default Sum over 5 minutes
*/
rejectedConnectionCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of requests processed over IPv4 and IPv6.
*
* This count includes only the requests with a response generated by a target of the load balancer.
*
* @default Sum over 5 minutes
*/
requestCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of rules processed by the load balancer given a request rate averaged over an hour.
*
* @default Sum over 5 minutes
*/
ruleEvaluations(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of connections that were not successfully established between the load balancer and target.
*
* @default Sum over 5 minutes
*/
targetConnectionErrorCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The time elapsed, in seconds, after the request leaves the load balancer until a response from the target is received.
*
* @default Average over 5 minutes
*/
targetResponseTime(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of TLS connections initiated by the load balancer that did not establish a session with the target.
*
* Possible causes include a mismatch of ciphers or protocols.
*
* @default Sum over 5 minutes
*/
targetTLSNegotiationErrorCount(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of user authentications that could not be completed
*
* Because an authenticate action was misconfigured, the load balancer
* couldn't establish a connection with the IdP, or the load balancer
* couldn't complete the authentication flow due to an internal error.
*
* @default Sum over 5 minutes
*/
elbAuthError(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of user authentications that could not be completed because the
* IdP denied access to the user or an authorization code was used more than
* once.
*
* @default Sum over 5 minutes
*/
elbAuthFailure(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The time elapsed, in milliseconds, to query the IdP for the ID token and user info.
*
* If one or more of these operations fail, this is the time to failure.
*
* @default Average over 5 minutes
*/
elbAuthLatency(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
/**
* The number of authenticate actions that were successful.
*
* This metric is incremented at the end of the authentication workflow,
* after the load balancer has retrieved the user claims from the IdP.
*
* @default Sum over 5 minutes
*/
elbAuthSuccess(props?: cloudwatch.MetricOptions): cloudwatch.Metric;
}
/**
* An application load balancer
*/
export interface IApplicationLoadBalancer extends ILoadBalancerV2, ec2.IConnectable {
/**
* The ARN of this load balancer
*/
readonly loadBalancerArn: string;
/**
* The VPC this load balancer has been created in (if available).
* If this interface is the result of an import call to fromApplicationLoadBalancerAttributes,
* the vpc attribute will be undefined unless specified in the optional properties of that method.
*/
readonly vpc?: ec2.IVpc;
/**
* The IP Address Type for this load balancer
*
* If the `@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault`
* feature flag is set (the default for new projects), and `addListener()` is called with `open: true`,
* the load balancer's security group will automatically include both IPv4 and IPv6 ingress rules
* when using `IpAddressType.DUAL_STACK_WITHOUT_PUBLIC_IPV4`.
*
* For existing projects that only have IPv4 rules, you can opt-in to IPv6 ingress rules
* by enabling the feature flag in your cdk.json file. Note that enabling this feature flag
* will modify existing security group rules.
*
* @default IpAddressType.IPV4
*/
readonly ipAddressType?: IpAddressType;
/**
* A list of listeners that have been added to the load balancer.
* This list is only valid for owned constructs.
*/
readonly listeners: ApplicationListener[];
/**
* All metrics available for this load balancer
*/
readonly metrics: IApplicationLoadBalancerMetrics;
/**
* Add a new listener to this load balancer
*/
addListener(id: string, props: BaseApplicationListenerProps): ApplicationListener;
}
/**
* Properties to reference an existing load balancer
*/
export interface ApplicationLoadBalancerAttributes {
/**
* ARN of the load balancer
*/
readonly loadBalancerArn: string;
/**
* ID of the load balancer's security group
*/
readonly securityGroupId: string;
/**
* The canonical hosted zone ID of this load balancer
*
* @default - When not provided, LB cannot be used as Route53 Alias target.
*/
readonly loadBalancerCanonicalHostedZoneId?: string;
/**
* The DNS name of this load balancer
*
* @default - When not provided, LB cannot be used as Route53 Alias target.
*/
readonly loadBalancerDnsName?: string;
/**
* Whether the security group allows all outbound traffic or not
*
* Unless set to `false`, no egress rules will be added to the security group.
*
* @default true
*/
readonly securityGroupAllowsAllOutbound?: boolean;
/**
* The VPC this load balancer has been created in, if available
*
* @default - If the Load Balancer was imported and a VPC was not specified,
* the VPC is not available.
*/
readonly vpc?: ec2.IVpc;
}
/**
* An ApplicationLoadBalancer that has been defined elsewhere
*/
class ImportedApplicationLoadBalancer extends Resource implements IApplicationLoadBalancer {
/**
* Manage connections for this load balancer
*/
public readonly connections: ec2.Connections;
/**
* ARN of the load balancer
*/
public readonly loadBalancerArn: string;
public get listeners(): ApplicationListener[] {
throw Error('.listeners can only be accessed if the class was constructed as an owned, not imported, load balancer');
}
/**
* VPC of the load balancer
*
* Undefined if optional vpc is not specified.
*/
public readonly vpc?: ec2.IVpc;
public readonly metrics: IApplicationLoadBalancerMetrics;
constructor(scope: Construct, id: string, private readonly props: ApplicationLoadBalancerAttributes) {
super(scope, id, {
environmentFromArn: props.loadBalancerArn,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
this.vpc = props.vpc;
this.loadBalancerArn = props.loadBalancerArn;
this.connections = new ec2.Connections({
securityGroups: [ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroup', props.securityGroupId, {
allowAllOutbound: props.securityGroupAllowsAllOutbound,
})],
});
this.metrics = new ApplicationLoadBalancerMetrics(this, parseLoadBalancerFullName(props.loadBalancerArn));
}
@MethodMetadata()
public addListener(id: string, props: BaseApplicationListenerProps): ApplicationListener {
return new ApplicationListener(this, id, {
loadBalancer: this,
...props,
});
}
public get loadBalancerCanonicalHostedZoneId(): string {
if (this.props.loadBalancerCanonicalHostedZoneId) { return this.props.loadBalancerCanonicalHostedZoneId; }
// eslint-disable-next-line max-len
throw new ValidationError(`'loadBalancerCanonicalHostedZoneId' was not provided when constructing Application Load Balancer ${this.node.path} from attributes`, this);
}
public get loadBalancerDnsName(): string {
if (this.props.loadBalancerDnsName) { return this.props.loadBalancerDnsName; }
// eslint-disable-next-line max-len
throw new ValidationError(`'loadBalancerDnsName' was not provided when constructing Application Load Balancer ${this.node.path} from attributes`, this);
}
}
class LookedUpApplicationLoadBalancer extends Resource implements IApplicationLoadBalancer {
public readonly loadBalancerArn: string;
public readonly loadBalancerCanonicalHostedZoneId: string;
public readonly loadBalancerDnsName: string;
public readonly ipAddressType?: IpAddressType;
public readonly connections: ec2.Connections;
public readonly vpc?: ec2.IVpc;
public readonly metrics: IApplicationLoadBalancerMetrics;
public get listeners(): ApplicationListener[] {
throw Error('.listeners can only be accessed if the class was constructed as an owned, not looked up, load balancer');
}
constructor(scope: Construct, id: string, props: cxapi.LoadBalancerContextResponse) {
super(scope, id, {
environmentFromArn: props.loadBalancerArn,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
this.loadBalancerArn = props.loadBalancerArn;
this.loadBalancerCanonicalHostedZoneId = props.loadBalancerCanonicalHostedZoneId;
this.loadBalancerDnsName = props.loadBalancerDnsName;
if (props.ipAddressType === cxapi.LoadBalancerIpAddressType.IPV4) {
this.ipAddressType = IpAddressType.IPV4;
} else if (props.ipAddressType === cxapi.LoadBalancerIpAddressType.DUAL_STACK) {
this.ipAddressType = IpAddressType.DUAL_STACK;
} else if (props.ipAddressType === cxapi.LoadBalancerIpAddressType.DUAL_STACK_WITHOUT_PUBLIC_IPV4) {
this.ipAddressType = IpAddressType.DUAL_STACK_WITHOUT_PUBLIC_IPV4;
}
this.vpc = ec2.Vpc.fromLookup(this, 'Vpc', {
vpcId: props.vpcId,
});
this.connections = new ec2.Connections();
for (const securityGroupId of props.securityGroupIds) {
const securityGroup = ec2.SecurityGroup.fromLookupById(this, `SecurityGroup-${securityGroupId}`, securityGroupId);
this.connections.addSecurityGroup(securityGroup);
}
this.metrics = new ApplicationLoadBalancerMetrics(this, parseLoadBalancerFullName(this.loadBalancerArn));
}
@MethodMetadata()
public addListener(id: string, props: BaseApplicationListenerProps): ApplicationListener {
return new ApplicationListener(this, id, {
...props,
loadBalancer: this,
});
}
}
/**
* Properties for a redirection config
*/
export interface ApplicationLoadBalancerRedirectConfig {
/**
* The protocol of the listener being created
*
* @default HTTP
*/
readonly sourceProtocol?: ApplicationProtocol;
/**
* The port number to listen to
*
* @default 80
*/
readonly sourcePort?: number;
/**
* The protocol of the redirection target
*
* @default HTTPS
*/
readonly targetProtocol?: ApplicationProtocol;
/**
* The port number to redirect to
*
* @default 443
*/
readonly targetPort?: number;
/**
* Allow anyone to connect to this listener
*
* If this is specified, the listener will be opened up to anyone who can reach it.
* For internal load balancers this is anyone in the same VPC. For public load
* balancers, this is anyone on the internet.
*
* If you want to be more selective about who can access this load
* balancer, set this to `false` and use the listener's `connections`
* object to selectively grant access to the listener.
*
* @default true
*/
readonly open?: boolean;
}