Skip to content

Instantly share code, notes, and snippets.

@tolicodes
Created October 20, 2021 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tolicodes/1f00b63e90162bb1e690eab3acfe29be to your computer and use it in GitHub Desktop.
Save tolicodes/1f00b63e90162bb1e690eab3acfe29be to your computer and use it in GitHub Desktop.
Pulumi Components
import * as aws from '@pulumi/aws';
import * as awsx from '@pulumi/awsx';
import * as pulumi from '@pulumi/pulumi';
export interface IAutoScaleFargateServiceArgs {
iamRole: aws.iam.Role;
service: awsx.ecs.FargateService;
cluster: awsx.ecs.Cluster;
serviceName: string;
maxCount: number;
minCount: number;
CPUThreshold: number;
memoryThreshold: number;
}
export default class AutoScaleFargateService extends pulumi.ComponentResource {
public readonly /*out*/ autoScaleTarget: pulumi.Output<aws.appautoscaling.Target>;
public readonly /*out*/ cpuScalingPolicy: pulumi.Output<aws.appautoscaling.Policy>;
public readonly /*out*/ memoryScalingPolicy: pulumi.Output<aws.appautoscaling.Policy>;
constructor(
name: string,
{
iamRole,
service,
cluster,
serviceName,
maxCount,
minCount,
CPUThreshold,
memoryThreshold,
}: IAutoScaleFargateServiceArgs,
opts?: pulumi.ResourceOptions,
) {
super('AutoScaleFargateService', name, {}, opts);
const stack = pulumi.getStack();
this.autoScaleTarget = pulumi.output(
new aws.appautoscaling.Target(
`appautoscaling-target-${serviceName}-${stack}`,
{
// max/min task instances
maxCapacity: maxCount,
minCapacity: minCount,
roleArn: iamRole.arn,
resourceId: pulumi.interpolate`service/${cluster.cluster.name}/${service.service.name}`,
scalableDimension: 'ecs:service:DesiredCount',
serviceNamespace: 'ecs',
},
),
);
const policyOpts = {
policyType: 'TargetTrackingScaling',
resourceId: this.autoScaleTarget.resourceId,
scalableDimension: this.autoScaleTarget.scalableDimension,
serviceNamespace: this.autoScaleTarget.serviceNamespace,
};
this.cpuScalingPolicy = pulumi.output(
new aws.appautoscaling.Policy(
`appautoscaling-policy-cpu-${serviceName}-${stack}`,
{
...policyOpts,
targetTrackingScalingPolicyConfiguration: {
predefinedMetricSpecification: {
predefinedMetricType: 'ECSServiceAverageCPUUtilization',
},
// scale so that we use max x% CPU
targetValue: CPUThreshold,
},
},
),
);
this.memoryScalingPolicy = pulumi.output(
new aws.appautoscaling.Policy(
`appautoscaling-policy-memory-${serviceName}-${stack}`,
{
...policyOpts,
targetTrackingScalingPolicyConfiguration: {
predefinedMetricSpecification: {
predefinedMetricType: 'ECSServiceAverageMemoryUtilization',
},
// scale so that we use max x% Memory
targetValue: memoryThreshold,
},
},
),
);
super.registerOutputs({
cpuScalingPolicy: this.cpuScalingPolicy,
memoryScalingPolicy: this.memoryScalingPolicy,
autoScaleTarget: this.autoScaleTarget,
});
}
}
import * as aws from '@pulumi/aws';
import * as awsx from '@pulumi/awsx';
import * as pulumi from '@pulumi/pulumi';
import ValidatedCertificate from './validated-certificate';
const DEFAULT_CLIENT_CIDR_BLOCK = '11.0.0.0/22';
interface IValidatedCertificateOpts {
hostedZoneId: pulumi.Input<string>;
domainName: pulumi.Input<string>;
clientCidrBlock?: pulumi.Input<string>;
samlProviderArn: pulumi.Input<string>;
vpc: awsx.ec2.Vpc;
}
export default class ClientVPNEndpoint extends pulumi.ComponentResource {
public readonly /*out*/ endpoint: pulumi.Output<aws.ec2clientvpn.Endpoint>;
public readonly /*out*/ networkAssociation: pulumi.Output<aws.ec2clientvpn.NetworkAssociation>;
public readonly /*out*/ authorizationRule: pulumi.Output<aws.ec2clientvpn.AuthorizationRule>;
constructor(
name: string,
{
hostedZoneId,
domainName,
clientCidrBlock = DEFAULT_CLIENT_CIDR_BLOCK,
samlProviderArn,
vpc,
}: IValidatedCertificateOpts,
opts?: pulumi.ResourceOptions,
) {
super('ClientVPNEndpoint', name, {}, opts);
const validatedCertificate = new ValidatedCertificate(
`${name}-validated-certificate`,
{
hostedZoneId,
domainName,
},
);
const endpointName = `${name}-client-vpn-endpoint`;
this.endpoint = pulumi.output(new aws.ec2clientvpn.Endpoint(endpointName, {
tags: {
Name: endpointName,
},
serverCertificateArn: validatedCertificate.certificateArn,
clientCidrBlock,
splitTunnel: true,
authenticationOptions: [
{
type: 'federated-authentication',
samlProviderArn,
},
],
connectionLogOptions: {
enabled: false,
},
}));
// TODO: what if there are multiple private subnets? will this be a problem?
// what is a scenario where we need to access multiple subnets?
const defaultPrivateSubnetId = pulumi.output(vpc.privateSubnetIds)[0];
const defaultPrivateSubnetCidrBlock = pulumi
.output(vpc.privateSubnets)[0].subnet.cidrBlock;
// allows the endpoint to access these subnets
this.networkAssociation = pulumi.output(new aws.ec2clientvpn.NetworkAssociation(
`${name}-network-association`,
{
clientVpnEndpointId: this.endpoint.id,
subnetId: defaultPrivateSubnetId,
},
));
// allows the following subnet groups to access these subnets
this.authorizationRule = pulumi.output(new aws.ec2clientvpn.AuthorizationRule(
`${name}-authorization-rule`,
{
clientVpnEndpointId: this.endpoint.id,
targetNetworkCidr: defaultPrivateSubnetCidrBlock,
authorizeAllGroups: true,
},
));
super.registerOutputs({
endpoint: this.endpoint,
networkAssociation: this.networkAssociation,
authorizationRule: this.authorizationRule,
});
}
}
import * as aws from '@pulumi/aws';
import * as pulumi from '@pulumi/pulumi';
interface IValidatedCertificateOpts {
hostedZoneId: pulumi.Input<string>;
domainName: pulumi.Input<string>;
}
export default class ValidatedCertificate extends pulumi.ComponentResource {
public readonly /*out*/ certificateArn: pulumi.Output<string>;
public readonly /*out*/ certificate: pulumi.Output<aws.acm.Certificate>;
constructor(
name: string,
{ hostedZoneId, domainName }: IValidatedCertificateOpts,
opts?: pulumi.ResourceOptions,
) {
super('ValidatedCertificate', name, {}, opts);
this.certificate = pulumi.output(
new aws.acm.Certificate(`${name}-certificate`, {
domainName,
validationMethod: 'DNS',
}),
);
const { resourceRecordName, resourceRecordType, resourceRecordValue } =
this.certificate.domainValidationOptions[0];
// Create a DNS record to prove that we _own_ the domain we're requesting a certificate for.
const certificateValidationDomain = new aws.route53.Record(
`${name}-certificate-domain-validation`,
{
name: resourceRecordName,
zoneId: hostedZoneId,
type: resourceRecordType,
records: [resourceRecordValue],
ttl: 600,
},
);
/**
* This is a _special_ resource that waits for ACM to complete validation via the DNS record
* checking for a status of 'ISSUED' on the certificate itself. No actual resources are
* created (or updated or deleted).
*
* See https://www.terraform.io/docs/providers/aws/r/acm_certificate_validation.html for slightly more detail
* and https://github.com/terraform-providers/terraform-provider-aws/blob/master/aws/resource_aws_acm_certificate_validation.go
* for the actual implementation.
*/
const certificateValidation = new aws.acm.CertificateValidation(
`${name}-certificate-validation`,
{
certificateArn: this.certificate.arn,
validationRecordFqdns: [certificateValidationDomain.fqdn],
},
);
this.certificateArn = certificateValidation.certificateArn;
super.registerOutputs({
certificate: this.certificate,
certificateArn: this.certificateArn,
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment