aws-cdk-lib#CustomResource TypeScript Examples

The following examples show how to use aws-cdk-lib#CustomResource. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: slack-app.ts    From cloudstructs with Apache License 2.0 6 votes vote down vote up
constructor(scope: Construct, id: string, props: SlackAppProps) {
    super(scope, id);

    const provider = SlackAppProvider.getOrCreate(this);
    props.configurationTokenSecret.grantRead(provider.handler);
    props.configurationTokenSecret.grantWrite(provider.handler);

    this.credentials = props.credentialsSecret ?? new secretsmanager.Secret(this, 'Credentials', {
      description: `Credentials for Slack App ${this.node.id}`,
    });
    this.credentials.grantWrite(provider.handler);

    const resource = new CustomResource(this, 'Resource', {
      serviceToken: provider.serviceToken,
      resourceType: 'Custom::SlackApp',
      properties: {
        manifest: props.manifest.render(this),
        configurationTokenSecretArn: props.configurationTokenSecret.secretArn,
        credentialsSecretArn: this.credentials.secretArn,
      },
    });

    this.appId = resource.getAttString('appId');
    this.clientId = this.credentials.secretValueFromJson('clientId').toString();
    this.clientSecret = this.credentials.secretValueFromJson('clientSecret').toString();
    this.verificationToken = this.credentials.secretValueFromJson('verificationToken').toString();
    this.signingSecret = this.credentials.secretValueFromJson('signingSecret').toString();
  }
Example #2
Source File: state-machine-cr-provider.test.ts    From cloudstructs with Apache License 2.0 6 votes vote down vote up
test('StateMachineCustomResourceProvider', () => {
  // WHEN
  const provider = new StateMachineCustomResourceProvider(stack, 'Provider', {
    stateMachine: {
      stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:my-machine',
    },
  });

  new CustomResource(stack, 'CustomResource', {
    serviceToken: provider.serviceToken,
  });

  expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});
Example #3
Source File: dns-validated-domain-identity.ts    From aws-cdk-ses-domain-identity with MIT License 5 votes vote down vote up
public constructor(scope: Construct, id: string, props: DnsValidatedDomainIdentityProps) {
    super(scope, id);

    const stack = Stack.of(this);

    const region = props.region ?? stack.region;
    const accountId = stack.account;

    this.domainName = props.domainName;
    this.dkim = props.dkim ?? false;
    this.identityArn = `arn:aws:ses:${region}:${accountId}:identity/${this.domainName}`;
    this.normalizedZoneName = props.hostedZone.zoneName;
    // Remove trailing `.` from zone name
    if (this.normalizedZoneName.endsWith(".")) {
      this.normalizedZoneName = this.normalizedZoneName.substring(0, this.normalizedZoneName.length - 1);
    }

    // Remove any `/hostedzone/` prefix from the Hosted Zone ID
    this.hostedZoneId = props.hostedZone.hostedZoneId.replace(/^\/hostedzone\//, "");

    const requestorFunction = new lambda.Function(this, "DomainIdentityRequestorFunction", {
      code: lambda.Code.fromAsset(path.resolve(__dirname, "..", "lambda-packages", "dns-validated-domain-identity-handler", "dist")),
      handler: "index.identityRequestHandler",
      runtime: lambda.Runtime.NODEJS_14_X,
      memorySize: 128,
      timeout: Duration.minutes(15),
      role: props.customResourceRole,
    });
    requestorFunction.addToRolePolicy(new iam.PolicyStatement({
      actions: [
        "ses:GetIdentityVerificationAttributes",
        "ses:GetIdentityDkimAttributes",
        "ses:SetIdentityDkimEnabled",
        "ses:VerifyDomainIdentity",
        "ses:VerifyDomainDkim",
        "ses:ListIdentities",
        "ses:DeleteIdentity",
      ],
      resources: ["*"],
    }));
    requestorFunction.addToRolePolicy(new iam.PolicyStatement({
      actions: ["route53:GetChange"],
      resources: ["*"],
    }));
    requestorFunction.addToRolePolicy(new iam.PolicyStatement({
      actions: [
          "route53:changeResourceRecordSets",
          "route53:ListResourceRecordSets",
      ],
      resources: [`arn:${Stack.of(requestorFunction).partition}:route53:::hostedzone/${this.hostedZoneId}`],
    }));

    const identity = new CustomResource(this, "IdentityRequestorResource", {
      serviceToken: requestorFunction.functionArn,
      properties: {
        DomainName: this.domainName,
        HostedZoneId: this.hostedZoneId,
        Region: region,
        DKIM: props.dkim,
      },
    });

    this.node.addValidation({
      validate: (): string[] => {
        const errors: string[] = [];
        // Ensure the zone name is a parent zone of the certificate domain name
        if (!Token.isUnresolved(this.normalizedZoneName) &&
          this.domainName !== this.normalizedZoneName &&
          !this.domainName.endsWith("." + this.normalizedZoneName)) {
          errors.push(`DNS zone ${this.normalizedZoneName} is not authoritative for SES identity domain name ${this.domainName}`);
        }

        return errors;
      },
    });
  }
Example #4
Source File: index.ts    From cdk-ssm-document with Apache License 2.0 5 votes vote down vote up
/**
   * Defines a new SSM document
   */
  constructor(scope: Construct, id: string, props: DocumentProps) {
    super(scope, id);

    this.tags = new TagManager(TagType.MAP, 'Custom::SSM-Document');
    this.tags.setTag(createdByTag, ID);

    const stack = Stack.of(this).stackName;
    this.lambda = this.ensureLambda();
    const name = this.fixDocumentName(props.name);

    if (name.length < 3 || name.length > 128) {
      Annotations.of(this).addError(
        `SSM Document name ${name} is invalid. The name must be between 3 and 128 characters.`
      );
      return;
    }

    let content = props.content;

    if (typeof content === 'string') {
      content = yaml.safeLoad(content) as DocumentContent;
    }

    const document = new CustomResource(this, `SSM-Document-${name}`, {
      serviceToken: this.lambda.functionArn,
      resourceType: resourceType,
      properties: {
        updateDefaultVersion: props.updateDefaultVersion || true,
        name: name,
        content: content,
        documentType: props.documentType || 'Command',
        targetType: props.targetType || '/',
        attachments: props.attachments,
        versionName: props.versionName,
        StackName: stack,
        tags: Lazy.any({
          produce: () => this.tags.renderTags(),
        }),
      },
      pascalCaseProperties: true,
    });

    this.name = document.getAttString('Name');
  }
Example #5
Source File: aws-image-builder.ts    From amazon-ec2-image-builder-samples with MIT No Attribution 4 votes vote down vote up
constructor(scope: Construct, id: string, props: AWSImageBuilderProps) {
    super(scope, id);

    //creates a role for Imagebuilder to build EC2 image
    const imageBuilderRole = new Role(this, `ImageBuilderRole${props.name}`, {
      assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
      path: "/executionServiceEC2Role/",
    });

    const amiTable = new CfnMapping(this, "ami-table", {
      mapping: props.parentImage,
    });

    const parentImageID: string = amiTable.findInMap(
      Stack.of(this).region,
      "amiID"
    );

    //creates a the necessary policy for Imagebuilder to build EC2 image
    imageBuilderRole.addToPolicy(
      new PolicyStatement({
        resources: ["*"],
        actions: ["s3:PutObject"],
      })
    );

    //Adds SSM  Managed policy to role
    imageBuilderRole.addManagedPolicy(
      ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore")
    );
    //Adds EC2InstanceProfileForImageBuilder policy to role
    imageBuilderRole.addManagedPolicy(
      ManagedPolicy.fromAwsManagedPolicyName(
        "EC2InstanceProfileForImageBuilder"
      )
    );
    //Builds the instance Profile to be attached to EC2 instance created during image building
    const instanceProfile = new CfnInstanceProfile(
      this,
      `imageBuilderProfile${props.name}`,
      {
        roles: [imageBuilderRole.roleName],
        instanceProfileName: `${props.instanceProfileName}-${Aws.REGION}`,
      }
    );
    const notificationTopic = new Topic(
      this,
      "ImgBuilderNotificationTopic",
      {}
    );

    //Manage Infrastructure configurations
    const cfnInfrastructureConfiguration = new CfnInfrastructureConfiguration(
      this,
      `cfnInfrastructureConfiguration${props.name}`,
      {
        name: "infraConfiguration",
        instanceProfileName: `${props.instanceProfileName}-${Aws.REGION}`,
        instanceTypes: instanceTypes,
        subnetId: props.subnetId,
        securityGroupIds: [props.imageBuilderSG.securityGroupId],
        snsTopicArn: notificationTopic.topicArn,
      }
    );

    const componentArn = props.imageBuilderComponentList.map(
      (componentList) => ({
        componentArn: new CfnComponent(this, `${componentList.name}`, {
          name: `${componentList.name}`,
          platform: os_types.LINUX,
          version: props.version,
          data: `${componentList.data}`,
        }).attrArn,
      })
    );

    const cfnImageRecipe = new CfnImageRecipe(
      this,
      `cfnImageRecipe${props.name}`,
      {
        name: props.cfnImageRecipeName,
        version: props.version,
        parentImage: parentImageID,
        components: componentArn,
      }
    );

    const cfnImageBuilderPipeline = new CfnImagePipeline(
      this,
      `imageBuilderPipeline${props.name}`,
      {
        name: `imageBuilderPipeline${props.name}`,
        infrastructureConfigurationArn: cfnInfrastructureConfiguration.attrArn,
        imageRecipeArn: cfnImageRecipe.attrArn,
      }
    );
    const imagebuilderCr = new PythonFunction(this, "imagebuilderCr", {
      entry: "lib/lambda/imagebuilder",
      runtime: Runtime.PYTHON_3_8,
      index: "app.py",
      handler: "lambda_handler",
      environment: {
        IMAGE_SSM_NAME: props.amiIdLocation.parameterName,
      },
      timeout: Duration.seconds(45),
      initialPolicy: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          actions: ["imagebuilder:StartImagePipelineExecution"],
          resources: [
            `arn:aws:imagebuilder:${Stack.of(this).region}:${
              Stack.of(this).account
            }:image/*`,
            `arn:aws:imagebuilder:${Stack.of(this).region}:${
              Stack.of(this).account
            }:image-pipeline/*`,
          ],
        }),
      ],
    });

    imagebuilderCr.node.addDependency(cfnImageBuilderPipeline);

    const pipelineTriggerCrProvider = new cr.Provider(
      this,
      "pipelineTriggerCrProvider",
      {
        onEventHandler: imagebuilderCr,
        logRetention: RetentionDays.ONE_DAY,
      }
    );

    new CustomResource(this, id, {
      serviceToken: pipelineTriggerCrProvider.serviceToken,
      properties: {
        PIIPELINE_ARN: `arn:aws:imagebuilder:${Stack.of(this).region}:${
          Stack.of(this).account
        }:image-pipeline/${cfnImageBuilderPipeline.name.toLowerCase()}`,
      },
    });
    const amiIdRecorder = new PythonFunction(this, "imageRecorder", {
      entry: "lib/lambda/recorder",
      runtime: Runtime.PYTHON_3_8,
      index: "app.py",
      handler: "lambda_handler",
      environment: {
        IMAGE_SSM_NAME: props.amiIdLocation.parameterName,
      },
    });

    props.amiIdLocation.grantRead(amiIdRecorder);
    props.amiIdLocation.grantWrite(amiIdRecorder);

    notificationTopic.addSubscription(new LambdaSubscription(amiIdRecorder));
    this.subscribeEmails(notificationTopic);

    cfnInfrastructureConfiguration.addDependsOn(instanceProfile);
    cfnImageBuilderPipeline.addDependsOn(cfnInfrastructureConfiguration);
  }
Example #6
Source File: gitlab-runner-instance.ts    From cdk-gitlab-runner with Apache License 2.0 4 votes vote down vote up
constructor(scope: Construct, id: string, props: GitlabContainerRunnerProps) {
    super(scope, id);
    const spotFleetId = id;

    const defaultProps = {
      gitlabRunnerImage: 'public.ecr.aws/gitlab/gitlab-runner:alpine',
      gitlaburl: 'https://gitlab.com/',
      ec2type: 't3.micro',
      tags: ['gitlab', 'awscdk', 'runner'],
    };
    const runnerProps = { ...defaultProps, ...props };

    const runnerBucket = new Bucket(this, 'runnerBucket', {
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });
    const shell = UserData.forLinux();
    shell.addCommands(...this.createUserData(runnerProps, runnerBucket.bucketName));

    this.runnerRole =
      runnerProps.ec2iamrole ??
      new Role(this, 'runner-role', {
        assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
        description: 'For Gitlab EC2 Runner Role',
      });
    this.validUntil = runnerProps.validUntil;
    const instanceProfile = new CfnInstanceProfile(this, 'InstanceProfile', {
      roles: [this.runnerRole.roleName],
    });
    runnerBucket.grantWrite(this.runnerRole);
    this.vpc =
      runnerProps.selfvpc ??
      new Vpc(this, 'VPC', {
        cidr: '10.0.0.0/16',
        maxAzs: 2,
        subnetConfiguration: [
          {
            cidrMask: 26,
            name: 'RunnerVPC',
            subnetType: SubnetType.PUBLIC,
          },
        ],
        natGateways: 0,
      });
    this.defaultRunnerSG = new SecurityGroup(this, 'SpotFleetSg', {
      vpc: this.vpc,
    });
    this.defaultRunnerSG.connections.allowFromAnyIpv4(Port.tcp(22));
    const spotOrOnDemand = runnerProps.spotFleet ?? false;
    if (spotOrOnDemand) {
      //throw new Error('yes new spotfleet');

      const imageId = MachineImage.latestAmazonLinux({
        generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
      }).getImage(this).imageId;
      const lt = new CfnLaunchTemplate(this, 'LaunchTemplate', {
        launchTemplateData: {
          imageId,
          instanceType: runnerProps.ec2type,
          blockDeviceMappings: [
            {
              deviceName: '/dev/xvda',
              ebs: {
                volumeSize: runnerProps.ebsSize ?? 60,
              },
            },
          ],
          userData: Fn.base64(shell.render()),
          keyName: runnerProps.keyName,
          tagSpecifications: [
            {
              resourceType: 'instance',
              tags: [
                {
                  key: 'Name',
                  value: `${Stack.of(this).stackName
                  }/spotFleetGitlabRunner/${spotFleetId}`,
                },
              ],
            },
          ],
          instanceMarketOptions: {
            marketType: 'spot',
            spotOptions: {
              blockDurationMinutes:
                runnerProps.blockDuration ?? BlockDuration.ONE_HOUR,
              instanceInterruptionBehavior:
                runnerProps.instanceInterruptionBehavior ??
                InstanceInterruptionBehavior.TERMINATE,
            },
          },
          securityGroupIds: this.defaultRunnerSG.connections.securityGroups.map(
            (m) => m.securityGroupId,
          ),
          iamInstanceProfile: {
            arn: instanceProfile.attrArn,
          },
        },
      });

      const spotFleetRole = new Role(this, 'FleetRole', {
        assumedBy: new ServicePrincipal('spotfleet.amazonaws.com'),
        managedPolicies: [
          ManagedPolicy.fromAwsManagedPolicyName(
            'service-role/AmazonEC2SpotFleetTaggingRole',
          ),
        ],
      });

      const vpcSubnetSelection = runnerProps.vpcSubnet ?? {
        subnetType: SubnetType.PUBLIC,
      };
      const subnetConfig = this.vpc
        .selectSubnets(vpcSubnetSelection)
        .subnets.map((s) => ({
          subnetId: s.subnetId,
        }));

      const cfnSpotFleet = new CfnSpotFleet(this, id, {
        spotFleetRequestConfigData: {
          launchTemplateConfigs: [
            {
              launchTemplateSpecification: {
                launchTemplateId: lt.ref,
                version: lt.attrLatestVersionNumber,
              },
              overrides: subnetConfig,
            },
          ],
          iamFleetRole: spotFleetRole.roleArn,
          targetCapacity: 1,
          validUntil: Lazy.string({ produce: () => this.validUntil }),
          terminateInstancesWithExpiration: true,
        },
      });
      const onEvent = new lambda.Function(this, 'OnEvent', {
        code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
        handler: 'index.on_event',
        runtime: lambda.Runtime.PYTHON_3_8,
        timeout: Duration.seconds(60),
      });

      const isComplete = new lambda.Function(this, 'IsComplete', {
        code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
        handler: 'index.is_complete',
        runtime: lambda.Runtime.PYTHON_3_8,
        timeout: Duration.seconds(60),
        role: onEvent.role,
      });

      const myProvider = new cr.Provider(this, 'MyProvider', {
        onEventHandler: onEvent,
        isCompleteHandler: isComplete,
        logRetention: logs.RetentionDays.ONE_DAY,
      });

      onEvent.addToRolePolicy(
        new PolicyStatement({
          actions: ['ec2:DescribeSpotFleetInstances'],
          resources: ['*'],
        }),
      );

      const fleetInstances = new CustomResource(this, 'GetInstanceId', {
        serviceToken: myProvider.serviceToken,
        properties: {
          SpotFleetRequestId: cfnSpotFleet.ref,
        },
      });

      fleetInstances.node.addDependency(cfnSpotFleet);
      this.spotFleetInstanceId = Token.asString(
        fleetInstances.getAtt('InstanceId'),
      );
      this.spotFleetRequestId = Token.asString(
        fleetInstances.getAtt('SpotInstanceRequestId'),
      );
      new CfnOutput(this, 'InstanceId', { value: this.spotFleetInstanceId });
      new CfnOutput(this, 'SpotFleetId', { value: cfnSpotFleet.ref });
    } else {
      this.runnerEc2 = new Instance(this, 'GitlabRunner', {
        instanceType: new InstanceType(runnerProps.ec2type),
        instanceName: 'Gitlab-Runner',
        vpc: this.vpc,
        vpcSubnets: runnerProps.vpcSubnet ?? {
          subnetType: SubnetType.PUBLIC,
        },
        machineImage: MachineImage.latestAmazonLinux({
          generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
        }),
        role: this.runnerRole,
        userData: shell,
        securityGroup: this.defaultRunnerSG,
        blockDevices: [
          {
            deviceName: '/dev/xvda',
            volume: BlockDeviceVolume.ebs(runnerProps.ebsSize ?? 60),
          },
        ],
      });
      new CfnOutput(this, 'Runner-Instance-ID', {
        value: this.runnerEc2.instanceId,
      });
    }

    const unregisterRunnerOnEvent = new lambda.Function(this, 'unregisterRunnerOnEvent', {
      code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
      handler: 'unregister_runner.on_event',
      runtime: lambda.Runtime.PYTHON_3_8,
      timeout: Duration.seconds(60),
    });

    const unregisterRunnerProvider = new cr.Provider(this, 'unregisterRunnerProvider', {
      onEventHandler: unregisterRunnerOnEvent,
      logRetention: logs.RetentionDays.ONE_DAY,
    });

    const unregisterRunnerCR = new CustomResource(this, 'unregisterRunnerCR', {
      resourceType: 'Custom::unregisterRunnerProvider',
      serviceToken: unregisterRunnerProvider.serviceToken,
      properties: {
        BucketName: runnerBucket.bucketName,
        GitlabUrl: runnerProps.gitlaburl,
      },
    });

    runnerBucket.grantReadWrite(unregisterRunnerOnEvent);
    unregisterRunnerCR.node.addDependency(runnerBucket);
    this.runnerRole.addManagedPolicy(
      ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'),
    );

    new CfnOutput(this, 'Runner-Role-Arn', {
      value: this.runnerRole.roleArn,
    });
  }
Example #7
Source File: index.ts    From cdk-ec2-key-pair with Apache License 2.0 4 votes vote down vote up
/**
   * Defines a new EC2 Key Pair. The private key will be stored in AWS Secrets Manager
   */
  constructor(scope: Construct, id: string, props: KeyPairProps) {
    super(scope, id);

    if (
      props.removeKeySecretsAfterDays &&
      (props.removeKeySecretsAfterDays < 0 ||
        (props.removeKeySecretsAfterDays > 0 &&
          props.removeKeySecretsAfterDays < 7) ||
        props.removeKeySecretsAfterDays > 30)
    ) {
      Annotations.of(this).addError(
        `Parameter removeKeySecretsAfterDays must be 0 or between 7 and 30. Got ${props.removeKeySecretsAfterDays}`
      );
    }

    if (
      props.publicKey?.length &&
      props.publicKeyFormat !== undefined &&
      props.publicKeyFormat !== PublicKeyFormat.OPENSSH
    ) {
      Annotations.of(this).addError(
        'When importing a key, the format has to be of type OpenSSH'
      );
    }

    const stack = Stack.of(this).stackName;
    this.prefix = props.resourcePrefix || stack;
    if (this.prefix.length + cleanID.length > 62)
      // Cloudformation limits names to 63 characters.
      Annotations.of(this).addError(
        `Cloudformation limits names to 63 characters.
         Prefix ${this.prefix} is too long to be used as a prefix for your roleName. Define parameter resourcePrefix?:`
      );
    this.lambda = this.ensureLambda();

    this.tags = new TagManager(TagType.MAP, 'Custom::EC2-Key-Pair');
    this.tags.setTag(createdByTag, ID);

    const kmsPrivate = props.kmsPrivateKey || props.kms;
    const kmsPublic = props.kmsPublicKey || props.kms;

    const key = new CustomResource(this, `EC2-Key-Pair-${props.name}`, {
      serviceToken: this.lambda.functionArn,
      resourceType: resourceType,
      properties: {
        Name: props.name,
        Description: props.description || '',
        KmsPrivate: kmsPrivate?.keyArn || 'alias/aws/secretsmanager',
        KmsPublic: kmsPublic?.keyArn || 'alias/aws/secretsmanager',
        PublicKey: props.publicKey || '',
        StorePublicKey: props.storePublicKey || false,
        ExposePublicKey: props.exposePublicKey || false,
        PublicKeyFormat: props.publicKeyFormat || PublicKeyFormat.OPENSSH,
        RemoveKeySecretsAfterDays: props.removeKeySecretsAfterDays || 0,
        SecretPrefix: props.secretPrefix || 'ec2-ssh-key/',
        StackName: stack,
        Tags: Lazy.any({
          produce: () => this.tags.renderTags(),
        }),
      },
    });

    if (typeof props.kms !== 'undefined') {
      props.kms.grantEncryptDecrypt(this.lambda.role!);
      key.node.addDependency(props.kms);
      key.node.addDependency(this.lambda.role!);
    }

    if (typeof props.kmsPrivateKey !== 'undefined') {
      props.kmsPrivateKey.grantEncryptDecrypt(this.lambda.role!);
      key.node.addDependency(props.kmsPrivateKey);
      key.node.addDependency(this.lambda.role!);
    }

    if (typeof props.kmsPublicKey !== 'undefined') {
      props.kmsPublicKey.grantEncryptDecrypt(this.lambda.role!);
      key.node.addDependency(props.kmsPublicKey);
      key.node.addDependency(this.lambda.role!);
    }

    this.privateKeyArn = key.getAttString('PrivateKeyARN');
    this.publicKeyArn = key.getAttString('PublicKeyARN');
    this.publicKeyValue = key.getAttString('PublicKeyValue');
    this.keyPairName = key.getAttString('KeyPairName');
    this.keyPairID = key.getAttString('KeyPairID');
  }