Skip to content
Snippets Groups Projects
update-proxy.ts 2.60 KiB
import {
  aws_ssm as ssm,
  CfnDeletionPolicy,
  CfnResource,
  custom_resources as cr,
  CustomResource,
  RemovalPolicy,
  Stack,
} from 'aws-cdk-lib';
import {Construct} from 'constructs';
import * as fs from 'fs';
import * as path from 'path';

/**
 * SNS message contents.
 */
export interface Message {
  /** Configuration files */
  files?: ProxyFile[];
  /** Stack name */
  stackName?: string;
}

/**
 * Reverse proxy file info.
 */
export interface ProxyFile {
  /** File content */
  content: string;
  /** Filename */
  filename: string;
}

/**
 * Update proxy properties.
 */
export interface UpdateProxyProps {
  /** Resource id */
  id: string;
  /** Path to configuration files */
  paths: string[];
  /** Current scope */
  scope: Construct;
  /** SSM parameter name for reverse proxy topic arn */
  topicArnSsmName: string;
}

/**
 * Read in reverse proxy configuration files from given path and publish SNS
 * message with contents.
 *
 * @param props Properties
 */
export function updateProxy(props: UpdateProxyProps): cr.AwsCustomResource {
  const files: ProxyFile[] = props.paths.map(filePath => {
    const content = fs.readFileSync(filePath, {
      encoding: 'utf-8',
    });

    return {
      filename: path.basename(filePath),
      content,
    };
  });

  const topicArn = ssm.StringParameter.valueFromLookup(
    props.scope,
    props.topicArnSsmName
  );
  const {stackName} = Stack.of(props.scope);

  const message: Message = {
    files,
    stackName,
  };

  const sdkCall: cr.AwsSdkCall = {
    action: 'publish',
    service: 'SNS',
    parameters: {
      Message: JSON.stringify(message),
      TopicArn: topicArn,
    },
    physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString()),
  };

  const onDeleteMessage: Message = {
    stackName,
  };

  const onDeleteSdkCall: cr.AwsSdkCall = {
    ...sdkCall,
    parameters: {
      ...sdkCall.parameters,
      Message: JSON.stringify(onDeleteMessage),
    },
  };

  const customResource = new cr.AwsCustomResource(
    props.scope,
    `${props.id}SnsReverseProxy`,
    {
      onCreate: sdkCall,
      onDelete: onDeleteSdkCall,
      onUpdate: sdkCall,
      removalPolicy: RemovalPolicy.RETAIN,
      policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
        resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
      }),
    }
  );

  const cfnResource = (
    customResource.node.defaultChild as CustomResource | undefined
  )?.node.defaultChild as CfnResource | undefined;

  // Update deletion policy
  if (cfnResource instanceof CfnResource) {
    cfnResource.addOverride('DeletionPolicy', CfnDeletionPolicy.DELETE);
  }

  return customResource;
}