

Amazon CodeCatalyst is no longer open to new customers. Existing customers can continue to use the service as normal. For more information, see [How to migrate from CodeCatalyst](migration.md).

# Developing a custom blueprint to meet project requirements
<a name="develop-bp"></a>

Before publishing a custom blueprint, you can develop the blueprint to meet specific requirements. You can develop your custom blueprint and test the blueprint by creating a project when previewing. You can develop the custom blueprint to include project components, such as specific source code, account connections, workflows, issues, or any other component that can be created in CodeCatalyst.

**Important**  
If you want to use blueprint packages from external sources, consider the risks that may come with those packages. You're responsible for the custom blueprints that you add to your space and the code they generate.

**Important**  
To develop a custom blueprint in your CodeCatalyst space, you must be signed in with an account that has the **Space administrator** or **Power user** role in the space.

**To develop or update a custom blueprint**

1. Resume your Dev Environment. For more information, see [Resuming a Dev Environment](devenvironment-resume.md).

   If you don't have a Dev Environment, you must first create one. For more information, see [Creating a Dev Environment](devenvironment-create.md).

1. Open a working terminal in your Dev Environment.

1. If you opted in for a release workflow when creating your blueprint, the latest blueprint version is automatically published. Pull the changes to make sure the `package.json` file has the incremented version. Use the following command:

   ```
   git pull
   ```

1. In the `src/blueprint.ts` file, edit the options of your custom blueprint. The `Options` interface is interpreted by the CodeCatalyst wizard dynamically to generate a selection user interface (UI). You can develop your custom blueprint by adding components and supported tags. For more information, see [Modifying blueprint features with a front-end wizard](wizard-bp.md), [Adding environment components to a blueprint](comp-env-bp.md), [Adding region components to a blueprint](region-comp-bp.md), [Adding repository and source code components to a blueprint](comp-repo-source-bp.md), [Adding workflow components to a blueprint](comp-workflow-bp.md), and [Adding Dev Environments components to a blueprint](comp-dev-env-bp.md).

   You can also view the blueprints SDK and sample blueprints for additional support when developing your custom blueprint. For more information, see the [open-source GitHub repository](https://github.com/aws/codecatalyst-blueprints).

Custom blueprints provide preview bundles as a result of a successful synthesis. The project bundle represents the source code, configuration, and resources in a project, and it's used by CodeCatalyst deployment API operations to deploy into a project. If you want to continue developing your custom blueprint, rerun the blueprint synthesis process. For more information, see [Custom blueprints concepts](custom-bp-concepts.md).

# Modifying blueprint features with a front-end wizard
<a name="wizard-bp"></a>

A blueprint selection wizard on CodeCatalyst is auto-generated by the `Options` interface in the `blueprint.ts` file. The front-end wizard supports modifications and features of a blueprint's `Options` using [JSDOC style comments and tags](https://jsdoc.app/about-getting-started.html). You can use JSDOC style comments and tags to perform tasks. For example, you can select the text displayed above an option, enable features such as input validation, or make an option collapsible. The wizard works by interpreting an abstract syntax tree (AST) generated from the TypeScript type from the `Options` interface. The wizard configures itself automatically to the type described as best as it can. Not all types are supported. Other supported types include the region selector and environment selector.

The following is an example of a wizard that uses JSDOC comments and tags with blueprint's `Options`:

```
export interface Options {
  /**
   * What do you want to call your new blueprint?
   * @validationRegex /^[a-zA-Z0-9_]+$/
   * @validationMessage Must contain only upper and lowercase letters, numbers and underscores
   */
  blueprintName: string;

  /**
   * Add a description for your new blueprint.
   */
   description?: string;

   /**
    * Tags for your Blueprint:
    * @collapsed true
    */
  tags?: string[];
}
```

The display name of each option of the `Options` interface appears in `camelCase` by default. Plain text in the JSDOC style comment is displayed as text above the option in the wizard.

**Topics**
+ [Supported tags](#supported-tags-bp)
+ [Supported TypeScript types](#supported-typescript-bp)
+ [Communicating to the user during synthesis](#communication-mid-synthesis)

## Supported tags
<a name="supported-tags-bp"></a>

The following JSDOC tags are supported by a custom blueprint's `Options` in the front-end wizard.

### @inlinePolicy ./path/to/policy/file.json
<a name="inline-policy-tag"></a>
+ **Requires** - Option to be a type `Role`.
+ **Usage** - Allows you to communicate the inline policies a role needs. The `policy.json` path is expected to be under source code. Use this tag when you need a custom policy for a role.
+ **Dependencies** - `blueprint-cli 0.1.12` and above
+ **Example** - `@inlinePolicy ./deployment-policy.json`

```
environment: EnvironmentDefinition{
    awsAccountConnection: AccountConnection{
      /**
       * @inlinePolicy ./path/to/deployment-policy.json
       */
      cdkRole: Role[];
    };
   };
```

### @trustPolicy ./path/to/policy/file.json
<a name="trust-policy-tag"></a>
+ **Requires** - Option to be a type `Role`.
+ **Usage** - Allows you to communicate the trust policies a role needs. The `policy.json` path is expected to be under source code. Use this tag when you need a custom policy for a role.
+ **Dependencies** - `blueprint-cli 0.1.12` and above
+ **Example** - `@trustPolicy ./trust-policy.json`

```
environment: EnvironmentDefinition{
    awsAccountConnection: AccountConnection{
      /**
       * @trustPolicy ./path/to/trust-policy.json
       */
      cdkRole: Role[];
    };
   };
```

### @validationRegex Regex expression
<a name="validation-regex-tag"></a>
+ **Requires** - Option to be a string.
+ **Usage** - Performs input validation on the option by using the given regex expression and displays `@validationMessage`.
+ **Example** - `@validationRegex /^[a-zA-Z0-9_]+$/`
+ **Recommendation** - Use with `@validationMessage`. Validation message is empty by default.

### @validationMessage string
<a name="validation-message-tag"></a>
+ **Requires** - `@validationRegex` or other errors to review usage.
+ **Usage** - Displays validation message on `@validation*` failure.
+ **Example** - `@validationMessage Must contain only upper and lowercase letters, numbers, and underscores`.
+ **Recommendation** - Use with `@validationMessage`. Validation message is empty by default.

### @collapsed boolean (optional)
<a name="collapsed-boolean-tag"></a>
+ **Requires** - N/A
+ **Usage** - Boolean that allows a suboption to be collapsible. If the collapsed annotation is present, its default value is true. Setting the value to `@collapsed false` creates a collapsible section that is initially open.
+ **Example** - `@collapsed true`

### @displayName string
<a name="display-name-tag"></a>
+ **Requires** - N/A
+ **Usage** - Changes option display name. Allows formats other than camelCase for the display name.
+ **Example** - `@displayName Blueprint Name`

### @displayName string
<a name="display-name-tag"></a>
+ **Requires** - N/A
+ **Usage** - Changes option display name. Allows formats other than [camelCase](https://en.wikipedia.org/wiki/Camel_case) for the display name.
+ **Example** - `@displayName Blueprint Name`

### @defaultEntropy number
<a name="default-entropy-tag"></a>
+ **Requires** - Option to be a string.
+ **Usage** - Appends a randomized alphanumeric string of a specified length to the option.
+ **Example** - `@defaultEntropy 5`

### @placeholder string (optional)
<a name="placeholder-tag"></a>
+ **Requires** - N/A
+ **Usage** - Changes default text field placeholder.
+ **Example** - `@placeholder type project name here`

### @textArea number (optional)
<a name="text-area-tag"></a>
+ **Requires** - N/A
+ **Usage** - Converts string input into a text area component for larger sections of text. Adding a number defines the number of rows. The default is five rows.
+ **Example** - `@textArea 10`

### @hidden boolean (optional)
<a name="hidden-tag"></a>
+ **Requires** - N/A
+ **Usage** - Hides file from user unless validation check fails. Default value is true.
+ **Example** - `@hidden`

### @button boolean (optional)
<a name="button-tag"></a>
+ **Requires** - N/A
+ **Usage** - Annotation must be on a Boolean property. Adds a button that will synthesize as true when chosen. Not a toggle.
+ **Example** - `buttonExample: boolean;`

  ```
  /**
    * @button
    */
  buttonExample: boolean;
  ```

### @showName boolean (optional)
<a name="show-name-tag"></a>
+ **Requires** - N/A
+ **Usage** - Can only be used on an account connection type. Shows hidden name input. Defaults to `default_environment`.
+ **Example** - `@showName true`

  ```
  /**
    * @showName true
    */
  accountConnection: AccountConnection<{
      ...
  }>;
  ```

### @showEnvironmentType boolean (optional)
<a name="show-environment-tag"></a>
+ **Requires** - N/A
+ **Usage** - Can only be used on an account connection type. Shows hidden environment type dropdown menu. All connections default to `production`. Options are **Non-production** or **Production**.
+ **Example** - `@showEnvironmentType true`

  ```
  /**
    * @showEnvironmentType true
    */
  accountConnection: AccountConnection<{
      ...
  }>;
  ```

### @forceDefault boolean (optional)
<a name="force-default-tag"></a>
+ **Requires** - N/A
+ **Usage** - Uses the default value provided by the blueprint author instead of the value that was used previously by the user.
+ **Example** - `forceDeafultExample: any;`

  ```
  /**
    * @forceDefault
    */
  forceDeafultExample: any;
  ```

### @requires blueprintName
<a name="requires-tag"></a>
+ **Requires** - Annotates the `Options` interface.
+ **Usage** - Warns user to add specified `blueprintName` to project as a requirement for the current blueprint.
+ **Example** - `@requires '@amazon-codecatalyst/blueprints.blueprint-builder'`

  ```
  /*
   * @requires '@amazon-codecatalyst/blueprints.blueprint-builder'
   */
  export interface Options extends ParentOptions {
  ...
  ```

### @filter regex
<a name="filter-regex-tag"></a>
+ **Requires** - Annotates the `Selector` or `MultiSelect` interface.
+ **Usage** - Filters dropdown in wizard to options matching the specified regex.
+ **Example** - `@filter /blueprintPackageName/`

  ```
   /**
       * @filter /myPackageName/
       */
      blueprintInstantiation?: Selector<BlueprintInstantiation>;
  ...
  ```

## Supported TypeScript types
<a name="supported-typescript-bp"></a>

The following TypeScript types are supported by a custom blueprint's `Options` in the front-end wizard.

### Number
<a name="number-ts-tag"></a>
+ **Requires** - Option to be a type `number`.
+ **Usage** - Generate a number input field.
+ **Example** - `age: number`

```
{
  age: number
  ...
}
```

### String
<a name="string-ts-tag"></a>
+ **Requires** - Option to be a type `string`.
+ **Usage** - Generate a string input field.
+ **Example** - `name: string`

```
{
  age: string
  ...
}
```

### String list
<a name="string-list-ts-tag"></a>
+ **Requires** - Option to be an array of type `string`.
+ **Usage** - Generate a string list input.
+ **Example** - `isProduction: boolean`

```
{
  isProduction: boolean
  ...
}
```

### Checkbox
<a name="checkbox-ts-tag"></a>
+ **Requires** - Option to be a `boolean`.
+ **Usage** - Generate a checkbox.
+ **Example** - `isProduction: boolean`

```
{
  isProduction: boolean
  ...
}
```

### Radio
<a name="radio-ts-tag"></a>
+ **Requires** - Option to be a union of three or fewer strings.
+ **Usage** - Generate a radio selected.
**Note**  
When there are four or more items, this type renders as a dropdown.
+ **Example** - `color: 'red' | 'blue' | 'green'`

```
{
  color: 'red' | 'blue' | 'green'
  ...
}
```

### Dropdown
<a name="dropdown-ts-tag"></a>
+ **Requires** - Option to be a union of four or more strings.
+ **Usage** - Generate a dropdown.
+ **Example** - `runtimes: 'nodejs' | 'python' | 'java' | 'dotnetcore' | 'ruby'`

```
{
  runtimes: 'nodejs' | 'python' | 'java' | 'dotnetcore' | 'ruby'
  ...
}
```

### Expandable section
<a name="expandable-ts-tag"></a>
+ **Requires** - Option to be an object.
+ **Usage** - Generate an expandable section. Options in the object will be nested inside the expandable section in the wizard.
+ **Example** - 

```
{
     expandableSectionTitle: {
         nestedString: string;
         nestedNumber: number;
     }
}
```

### Tuple
<a name="tuple-ts-tag"></a>
+ **Requires** - Option to be of type `Tuple`.
+ **Usage** - Generate a key-value paid input.
+ **Example** - `tuple: Tuple[string, string]>`

```
{
    tuple: Tuple[string, string]>;
    ...
}
```

### Tuple list
<a name="tuple-list-ts-tag"></a>
+ **Requires** - Option to be an array of type `Tuple`.
+ **Usage** - Generate a tuple list input.
+ **Example** - `tupleList: Tuple[string, string]>[]`

```
{
  tupleList: Tuple[string, string]>[];
  ...
}
```

### Selector
<a name="selector-ts-tag"></a>
+ **Requires** - Option to be of type `Selector`.
+ **Usage** - Generate a dropdown of source repositories or blueprints applied to a project.
+ **Example** - `sourceRepo: Selector<SourceRepository>`

```
{
    sourceRepo: Selector<SourceRepository>;
    sourceRepoOrAdd: Selector<SourceRepository | string>;
    blueprintInstantiation: Selector<BlueprintInstantiation>;
  ...
}
```

### Multiselect
<a name="multiselect-ts-tag"></a>
+ **Requires** - Option to be of type `Selector`.
+ **Usage** - Generate a multiselect input.
+ **Example** - `multiselect: MultiSelect['A' | 'B' | 'C' | 'D' | 'E']>`

```
{
  multiselect: MultiSelect['A' | 'B' | 'C' | 'D' | 'E']>;
  ...
}
```

## Communicating to the user during synthesis
<a name="communication-mid-synthesis"></a>

As a blueprint author, you can communicate back to users beyond only validation messages. For example, a space member might view a combination of options that produces a blueprint that isn't clear. Custom blueprints supports the ability to communicate error messages back to users by invoking the synthesis. The base blueprint implements a `throwSynthesisError(...)` function that expects a clear error message. You can invoke the message by using the following:

```
//blueprint.ts
this.throwSynthesisError({
   name: BlueprintSynthesisErrorTypes.BlueprintSynthesisError,
   message: 'hello from the blueprint! This is a custom error communicated to the user.'
})
```

# Generating inputs and rerendering front-end wizard elements
<a name="comp-dynamic-input-bp"></a>

You can generate wizard inputs with the DynamicKVInput and dynamically create front-end wizard elements for your custom blueprint.

**Topics**
+ [Creating development environments](#dynamickvinput-bp)
+ [Dynamically creating wizard elements](#create-wizard-elements-bp)

## Creating development environments
<a name="dynamickvinput-bp"></a>

The DynamicKVInput type can be used to generate front-end wizard inputs using your custom bluerpint's defaults. To view the most up-to-date schema, see the [DynamicKVInput definition](https://github.com/aws/codecatalyst-blueprints/blob/main/packages/blueprints/blueprint/src/ui-selectors/dynamic-kv-input.ts).

The following example shows how you can use `Options` to shape an object:

```
import { DynamicKVInput } from '@amazon-codecatalyst/blueprints.blueprint';

export interface Options extends ParentOptions {

  parameters: DynamicKVInput[];

}
```

The following example shows how you canset default parameters with several properties:

```
{ 
"parameters": [
        {
            "key": "AWS_REGION",
            "value": "us-west-2",
            "displayType": "region",
            "possibleValues": [
                "us-west-1",
                "us-west-2",
                "us-east-1",
                "us-east-2"
            ],
            "displayName": "AWS Region",
            "description": "AWS Region to deploy the solution to."
        },
        {
            "key": "SchedulingActive",
            "value": "Yes",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Scheduling Active",
            "description": "Activate or deactivate scheduling."
        },
        {
            "key": "ScheduledServices",
            "value": "Both",
            "displayType": "dropdown",
            "possibleValues": [
                "EC2",
                "RDS",
                "Both"
            ],
            "displayName": "Scheduled Services",
            "description": "Services to schedule."
        },
        {
            "key": "ScheduleRdsClusters",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Schedule RDS Clusters",
            "description": "Enable scheduling of Aurora clusters for RDS service."
        },
        {
            "key": "CreateRdsSnapshot",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Create RDS Snapshot",
            "description": "Create snapshot before stopping RDS instances (does not apply to Aurora Clusters)."
        },
        {
            "key": "MemorySize",
            "value": "128",
            "displayType": "dropdown",
            "possibleValues": [
                "128",
                "384",
                "512",
                "640",
                "768",
                "896",
                "1024",
                "1152",
                "1280",
                "1408",
                "1536"
            ],
            "displayName": "Memory Size",
            "description": "Size of the Lambda function running the scheduler, increase size when processing large numbers of instances."
        },
        {
            "key": "UseCloudWatchMetrics",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Use CloudWatch Metrics",
            "description": "Collect instance scheduling data using CloudWatch metrics."
        },
        {
            "key": "LogRetentionDays",
            "value": "30",
            "displayType": "dropdown",
            "possibleValues": [
                "1",
                "3",
                "5",
                "7",
                "14",
                "30",
                "60",
                "90",
                "120",
                "150",
                "180",
                "365",
                "400",
                "545",
                "731",
                "1827",
                "3653"
            ],
            "displayName": "Log Retention Days",
            "description": "Retention days for scheduler logs."
        },
        {
            "key": "Trace",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Trace",
            "description": "Enable debug-level logging in CloudWatch logs."
        },
        {
            "key": "EnableSSMMaintenanceWindows",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Enable SSM Maintenance Windows",
            "description": "Enable the solution to load SSM Maintenance Windows, so that they can be used for EC2 instance Scheduling."
        },
        {
            "key": "DefaultTimezone",
            "value": "UTC",
            "displayType": "string",
            "displayName": "Default Timezone",
            "description": "Default timezone to use for scheduling."
        },
        {
            "key": "Regions",
            "value": "us-west-2",
            "displayType": "string",
            "displayName": "Regions",
            "description": "List of regions in which instances should be scheduled, leave blank for current region only."
        },
        {
            "key": "UsingAWSOrganizations",
            "value": "No",
            "displayType": "dropdown",
            "possibleValues": [
                "Yes",
                "No"
            ],
            "displayName": "Using AWS Organizations",
            "description": "Use AWS Organizations to automate spoke account registration."
        },
        {
            "key": "Principals",
            "displayType": "string",
			      "optional": false,
            "displayName": "Principals",
            "description": "(Required) If using AWS Organizations, provide the Organization ID. Eg. o-xxxxyyy. Else, provide a comma separated list of spoke account ids to schedule. Eg.: 1111111111, 2222222222 or {param: ssm-param-name}"
        },
        {
            "key": "Namespace",
            "value": "Default",
            "displayType": "string",
            "displayName": "Namespace",
            "description": "Provide unique identifier to differentiate between multiple solution deployments (No Spaces). Example: Dev"
        },
        {
            "key": "SchedulerFrequency",
            "value": 5,
            "displayType": "number",
            "displayName": "Scheduler Frequency",
            "description": "Scheduler running frequency in minutes."
        }
    ]
}
```

## Dynamically creating wizard elements
<a name="create-wizard-elements-bp"></a>

The same schema as creating wizard inputs can be used to dynamically rerender a wizard during synthesis. That can be used to address follow-up questions by a user when necessary.

```
//blueprint.ts

export interface Options extends ParentOptions {
...
 dynamicOptions: OptionsSchemaDefinition<'optionsIdentifier', KVSchema>;
}
```

The wizard can then be set during synthesis period by using an `Options` component.

```
import {
  OptionsSchemaDefinition,
  OptionsSchema,
} from '@amazon-codecatalyst/blueprints.blueprint';

...

  // dynamically renders a number in the place where 'optionsIdentifier' was set in the original options type.
  new OptionsSchema<KVSchema>(this, 'optionsIdentifier', [
    {
            "key": "SchedulerFrequency",
            "value": 5,
            "displayType": "number",
            "displayName": "Scheduler Frequency",
            "description": "Scheduler running frequency in minutes."
    }
   ]);
```

# Adding environment components to a blueprint
<a name="comp-env-bp"></a>

The custom blueprint wizard is dynamically generated from the `Options` interface exposed through the wizard. Blueprints support generating user-interface (UI) components from exposed types.

**To import Amazon CodeCatalyst blueprints environment components**

In your `blueprint.ts` file, add the following:

```
import {...} from '@amazon-codecatalyst/codecatalyst-environments'
```

**Topics**
+ [Creating development environments](#create-dev-env-bp)
+ [List of environments](#list-env-bp)
+ [Mock interface examples](#examples-comp-env-bp)

## Creating development environments
<a name="create-dev-env-bp"></a>

The following example shows how to deploy your application to the cloud:

```
export interface Options extends ParentOptions {
        ...
        myNewEnvironment:  EnvironmentDefinition{
            thisIsMyFirstAccountConnection: AccountConnection{
                thisIsARole: Role['lambda', 's3', 'dynamo'];
             };
        };
    }
```

The interface generates a UI component that asks for a new environment (`myNewEnvironment`) with a single account connection (`thisIsMyFirstAccountConnection`. A role on the account connection (`thisIsARole`) is also generated with `['lambda', 's3', 'dynamo']` as the minimum required role capabilities. Not all users have account connections, so you should check for the case where a user doesn't connect an account or doesn't connect an account with a role. Roles can also be annotated with `@inlinePolicies`. For more information, see [@inlinePolicy ./path/to/policy/file.json](wizard-bp.md#inline-policy-tag).

The environment component requires a `name` and `environmentType`. The following code is the minimum required default shape:

```
{
  ...
  "myNewEnvironment": {
    "name": "myProductionEnvironment",
    "environmentType": "PRODUCTION"
  },
}
```

The UI component then prompts you for various fields. As you fill in the fields, the blueprint gets a fully expanded shape. It can be helpful for you to include the full mock in the `defaults.json` file for testing and development purposes.

## List of environments
<a name="list-env-bp"></a>

Specifying an array of type `EnvironmentDefinition` will generate a list of environments in the wizard UI.

```
export interface Options extends ParentOptions {
    ...
   /**
     @showName readOnly
   */
    myEnvironments:  EnvironmentDefinition<{
        thisIsMyFirstAccountConnection: AccountConnection<{
            thisIsARole: Role<['lambda', 's3', 'dynamo']>;
        }>;
    }>[];

}
```

The following example shows the defaults for an environment list:

```
{
  ...
  "myEnvironments": [
  {
    "name": "myProductionEnvironment",
    "environmentType": "PRODUCTION"
  },
  {
    "name": "myDevelopmentEnvironment",
    "environmentType": "DEVELOPMENT"
  },
  ]
}
```

## Mock interface examples
<a name="examples-comp-env-bp"></a>

### Simple mock interface
<a name="simple-comp-env-bp"></a>

```
{
    ...
    "thisIsMyEnvironment": {
        "name": "myProductionEnvironment",
        "environmentType": "PRODUCTION",
        "thisIsMySecondAccountConnection": {
            "id": "12345678910",
            "name": "my-account-connection-name",
            "secondAdminRole": {
                "arn": "arn:aws:iam::12345678910:role/ConnectedQuokkaRole",
                "name": "ConnectedQuokkaRole",
                "capabilities": [
                    "lambda",
                    "s3",
                    "dynamo"
                ]
            }
        }
    }
}
```

### Complex mock interface
<a name="complex-comp-env-bp"></a>

```
export interface Options extends ParentOptions {
  /**
   * The name of an environment
   * @displayName This is a Environment Name
   * @collapsed
   */
  thisIsMyEnvironment: EnvironmentDefinition{
    /**
     * comments about the account that is being deployed into
     * @displayName This account connection has an overriden name
     * @collapsed
     */
    thisIsMyFirstAccountConnection: AccountConnection{
      /**
       * Blah blah some information about the role that I expect
       * e.g. here's a copy-pastable policy: [to a link]
       * @displayName This role has an overriden name
       */
      adminRole: Role['admin', 'lambda', 's3', 'cloudfront'];
      /**
       * Blah blah some information about the second role that I expect
       * e.g. here's a copy-pastable policy: [to a link]
       */
      lambdaRole: Role['lambda', 's3'];
    };
    /**
     * comments about the account that is being deployed into
     */
    thisIsMySecondAccountConnection: AccountConnection{
      /**
         * Blah blah some information about the role that I expect
         * e.g. here's a copy-pastable policy: [to a link]
         */
      secondAdminRole: Role['admin', 'lambda', 's3', 'cloudfront'];
      /**
         * Blah blah some information about the second role that I expect
         * e.g. here's a copy-pastable policy: [to a link]
         */
      secondLambdaRole: Role['lambda', 's3'];
    };
  };
}
```

### Complete mock interface
<a name="complete-comp-env-bp"></a>

```
{
  ...
  "thisIsMyEnvironment": {
    "name": "my-production-environment",
    "environmentType": "PRODUCTION",
    "thisIsMySecondAccountConnection": {
      "id": "12345678910",
      "name": "my-connected-account",
      "secondAdminRole": {
        "name": "LambdaQuokkaRole",
        "arn": "arn:aws:iam::12345678910:role/LambdaQuokkaRole",
        "capabilities": [
          "admin",
          "lambda",
          "s3",
          "cloudfront"
        ]
      },
      "secondLambdaRole": {
        "name": "LambdaQuokkaRole",
        "arn": "arn:aws:iam::12345678910:role/LambdaQuokkaRole",
        "capabilities": [
          "lambda",
          "s3"
        ]
      }
    },
    "thisIsMyFirstAccountConnection": {
      "id": "12345678910",
      "name": "my-connected-account",
      "adminRole": {
        "name": "LambdaQuokkaRole",
        "arn": "arn:aws:iam::12345678910:role/LambdaQuokkaRole",
        "capabilities": [
          "admin",
          "lambda",
          "s3",
          "cloudfront"
        ]
      },
      "lambdaRole": {
        "name": "LambdaQuokkaRole",
        "arn": "arn:aws:iam::12345678910:role/LambdaQuokkaRole",
        "capabilities": [
          "lambda",
          "s3"
        ]
      }
    }
  },
}
```

# Adding secrets components to a blueprint
<a name="secrets-comp-bp"></a>

Secrets can be used in CodeCatalyst to store sensitive data that can be referenced in workflows. You can add a secret to your custom blueprint and reference it in your workflow. For more information, see [Masking data using secrets](workflows-secrets.md).

**To import Amazon CodeCatalyst blueprints region type**

In your `blueprint.ts` file, add the following:

```
import { Secret, SecretDefinition } from '@amazon-codecatalyst/blueprint-component.secrets'
```

**Topics**
+ [Creating a secret](#comp-create-secrets-bp)
+ [Referencing a secret in a workflow](#comp-reference-secrets-bp)

## Creating a secret
<a name="comp-create-secrets-bp"></a>

The following example creates a UI component that prompts the user to enter a secret value and optional description:

```
export interface Options extends ParentOptions {
    ...
    mySecret: SecretDefinition;
}


export class Blueprint extends ParentBlueprint {
  constructor(options_: Options) {
    new Secret(this, options.secret);
}
```

The secret component requires a `name`. The following code is the minimum required default shape:

```
{
    ...
    "secret": {
        "name": "secretName"
    },

}
```

## Referencing a secret in a workflow
<a name="comp-reference-secrets-bp"></a>

The following example blueprint creates a secret and a workflow that references the secret value. For more information, see [Referencing a secret in a workflow](workflows-secrets.using.md#workflows-using-secrets.using-identifier).

```
export interface Options extends ParentOptions {
    ...
/**
*
* @validationRegex /^\w+$/
*/
  username: string;


  password: SecretDefinition;
}


export class Blueprint extends ParentBlueprint {
  constructor(options_: Options) {
    const password = new Secret(this, options_.password);

    const workflowBuilder = new WorkflowBuilder(this, {
      Name: 'my_workflow',
    });


    workflowBuilder.addBuildAction({
      actionName: 'download_files',
      input: {
        Sources: ['WorkflowSource'],
      },
      output: {
        Artifacts: [{ Name: 'download', Files: ['file1'] }],
      },
      steps: [
        `curl -u ${options_.username}:${password.reference} https://example.com`,
      ],
    });

    new Workflow(
      this,
      repo,
      workflowBuilder.getDefinition(),
    );

}
```

To learn more about using secrets in CodeCatalyst, see [Masking data using secrets](workflows-secrets.md).

# Adding region components to a blueprint
<a name="region-comp-bp"></a>

The region type can be added to your custom blueprint's `Options` interface to generate a component in the blueprint wizard you can input one or more AWS gions. The gion type can be imported from your base blueprint in your `blueprint.ts` file. For more information, see [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/).

**To import Amazon CodeCatalyst blueprints region type**

In your `blueprint.ts` file, add the following:

```
import { Region } from '@amazon-codecatalyst/blueprints.blueprint'
```

The region type parameter is an array of AWS region codes to choose from, or you can use `*` to include all supported AWS regions.

**Topics**
+ [Annotations](#region-annotations-bp)
+ [Region components examples](#region-components-examples)

## Annotations
<a name="region-annotations-bp"></a>

JSDoc tags can be added to each field in the `Options` interface to customize how a field appears and behaves in the wizard. For the region type, the following tags are supported:
+ The `@displayName` annotation can be used to change the field's label in the wizard.

  Example: `@displayName AWS Region`
+ The `@placeholder` annotation can be used to change the select/multiselect component's placeholder.

  Example: `@placeholder Choose AWS Region`

## Region components examples
<a name="region-components-examples"></a>

### Choosing a region from a specified list
<a name="region-specified-list-bp"></a>

```
export interface Options extends ParentOptions {
    ...
  /**
   * @displayName Region
   */
  region: Region<['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2']>;
}
```

### Choosing one or more regions from a specified list
<a name="region-specified-list-bp"></a>

```
export interface Options extends ParentOptions {
    ...
  /**
   * @displayName Regions
   */
  multiRegion: Region<['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2']>[];
}
```

### Choosing one AWS egion
<a name="region-one-bp"></a>

```
export interface Options extends ParentOptions {
    ...
  /**
   * @displayName Region
   */
  region: Region<['*']>;
}
```

### Choosing one or more regions from a specified list
<a name="region-specified-list-bp"></a>

```
export interface Options extends ParentOptions {
    ...
  /**
   * @displayName Regions
   */
  multiRegion: Region<['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2']>[];
}
```

# Adding repository and source code components to a blueprint
<a name="comp-repo-source-bp"></a>

A repository is used by Amazon CodeCatalyst to store code. The repository takes a name as an input. Most components are stored in a repository, such as source code files, workflows, and other components like managed development environments (MDE). The source repository component also exports components used for managing files and static assets. Repositories have name constraints. For more information, see [Store and collaborate on code with source repositories in CodeCatalystStore and collaborate on code with source repositories](source.md).

```
const repository = new SourceRepository(this, {
  title: 'my-new-repository-title',
});
```

**To import Amazon CodeCatalyst blueprints repository and source code components**

In your `blueprint.ts` file, add the following:

```
import {...} from '@caws-blueprint-component/caws-source-repositories'
```

**Topics**
+ [Adding a file](#repo-add-file-bp)
+ [Adding a generic file](#repo-add-generic-file-bp)
+ [Copying files](#repo-copy-file-bp)
+ [Targeting multiple files](#target-multiple-files-bp)
+ [Creating a new repository and adding files](#repo-code-examples-bp)

## Adding a file
<a name="repo-add-file-bp"></a>

You can write a text file to a repository with the `SourceFile` construct. The operation is one of the most common use cases and takes a repository, a filepath, and text contents. If the file path doesn't exist within a repository, the component creates all the required folders.

```
new SourceFile(repository, `path/to/my/file/in/repo/file.txt`, 'my file contents');
```

**Note**  
If you write two files to the same location within the same repository, the most recent implementation overwrites the previous one. You can use the feature to layer generated code, and it's especially useful for extending over the code that the custom blueprints may have generated.

## Adding a generic file
<a name="repo-add-generic-file-bp"></a>

You can write arbitrary bits to your repository. You can read from a buffer and use the `File` construct.

```
new File(repository, `path/to/my/file/in/repo/file.img`, new Buffer(...));

new File(repository, `path/to/my/file/in/repo/new-img.img`, new StaticAsset('path/to/image.png').content());
```

## Copying files
<a name="repo-copy-file-bp"></a>

You can get started with generated code by copying and pasting starter code and then generating more code on top of that base. Place the code inside the `static-assets` directory, and then target that code with the `StaticAsset` construct. The path in this case always begins at the root of the `static-assets` directory.

```
const starterCode = new StaticAsset('path/to/file/file.txt')
const starterCodeText = new StaticAsset('path/to/file/file.txt').toString()
const starterCodeRawContent = new StaticAsset('path/to/image/hello.png').content()

const starterCodePath = new StaticAsset('path/to/image/hello.png').path()
// starterCodePath is equal to 'path/to/image/hello.png'
```

A subclass of `StaticAsset` is `SubstitutionAsset`. The subclass functions exactly the same, but instead you can run a mustache substitution over the file instead. It can be useful for performing copy-and-replace style generation.

Static asset substitution uses a mustache templating engine to render the static files that are seeded into the generated source repository. Mustache templating rules are applied during the rendering, which means that all values are HTML-encoded by default. To render unescaped HTML, use the triple mustache syntax `{{{name}}}`. For more information, see the [mustache templating rules](https://github.com/janl/mustache.js?tab=readme-ov-file#variables).

**Note**  
Running a substitute over files that aren't text-interpretable can produce errors.

```
const starterCodeText = new SubstitionAsset('path/to/file/file.txt').subsitite({
  'my_variable': 'subbed value1',
  'another_variable': 'subbed value2'
})
```

## Targeting multiple files
<a name="target-multiple-files-bp"></a>

Static assets support glob targeting through a static function on `StaticAsset` and its subclasses called `findAll(...)`, which returns a list of static assets preloaded with their paths, contents, and more. You can chain the list with `File` constructions to copy and paste contents in the `static-assets` directory.

```
new File(repository, `path/to/my/file/in/repo/file.img`, new Buffer(...));

new File(repository, `path/to/my/file/in/repo/new-img.img`, new StaticAsset('path/to/image.png').content());
```

## Creating a new repository and adding files
<a name="repo-code-examples-bp"></a>

You can use a repository component to create a new repository in a generated project. You can then add files or workflows to the created repository.

```
import { SourceRepository } from '@amazon-codecatalyst/codecatalyst-source-repositories';
...
const repository = new SourceRepository(this, { title: 'myRepo' });
```

The following example shows how to add files and workflows to an existing repository:

```
import { SourceFile } from '@amazon-codecatalyst/codecatalyst-source-repositories';
import { Workflow } from '@amazon-codecatalyst/codecatalyst-workflows';
...
new SourceFile(repository, 'README.md', 'This is the content of my readme');
new Workflow(this, repository, {/**...workflowDefinition...**/});
```

Combining the two pieces of code generates a single repository named `myRepo` with a source file `README.md` and a CodeCatalyst workflow at the root.

# Adding workflow components to a blueprint
<a name="comp-workflow-bp"></a>

A workflow is used by Amazon CodeCatalyst projects to run actions based on triggers. You can use workflow components to build and put together workflow YAML files. For more information, see [Workflow YAML definition](workflow-reference.md).

**To import Amazon CodeCatalyst blueprints workflows components**

In your `blueprint.ts` file, add the following:

```
import { WorkflowBuilder, Workflow } from '@amazon-codecatalyst/codecatalyst-workflows'
```

**Topics**
+ [Workflow components examples](#comp-workflows-examples-bp)
+ [Connecting to an environment](#comp-workflows-connect-env-bp)

## Workflow components examples
<a name="comp-workflows-examples-bp"></a>

### WorkflowBuilder component
<a name="comp-workflows-workflowbuilder-bp"></a>

You can use a class to build a workflow definition. The definition can be given to a workflow component for rendering in a repository.

```
import { WorkflowBuilder } from '@amazon-codecatalyst/codecatalyst-workflows'

const workflowBuilder = new WorkflowBuilder({} as Blueprint, {
  Name: 'my_workflow',
});

// trigger the workflow on pushes to branch 'main'
workflowBuilder.addBranchTrigger(['main']);

// add a build action
workflowBuilder.addBuildAction({
  // give the action a name
  actionName: 'build_and_do_some_other_stuff',

  // the action pulls from source code
  input: {
    Sources: ['WorkflowSource'],
  },

  // the output attempts to autodiscover test reports, but not in the node modules
  output: {
    AutoDiscoverReports: {
      Enabled: true,
      ReportNamePrefix: AutoDiscovered,
      IncludePaths: ['**/*'],
      ExcludePaths: ['*/node_modules/**/*'],
    },
  },
  // execute some arbitrary steps
  steps: [
    'npm install',
    'npm run myscript',
    'echo hello-world',
  ],
  // add an account connection to the workflow
  environment: convertToWorkflowEnvironment(myEnv),
});
```

### Workflow Projen component
<a name="comp-workflows-projen-bp"></a>

The following example shows how a Projen component can be used to write a workflow YAML to a repository:

```
import { Workflow } from '@amazon-codecatalyst/codecatalyst-workflows'

...

const repo = new SourceRepository
const blueprint = this;
const workflowDef = workflowBuilder.getDefinition()

// creates a workflow.yaml at .aws/workflows/${workflowDef.name}.yaml
new Workflow(blueprint, repo, workflowDef);

// can also pass in any object and have it rendered as a yaml. This is unsafe and may not produce a valid workflow
new Workflow(blueprint, repo, {... some object ...});
```

## Connecting to an environment
<a name="comp-workflows-connect-env-bp"></a>

Many workflows need to run in an AWS account connection. Workflows handle this by allowing actions to connect to environments with account and role name specifications.

```
import { convertToWorkflowEnvironment } from '@amazon-codecatalyst/codecatalyst-workflows'


const myEnv = new Environment(...);

// can be passed into a workflow constructor
const workflowEnvironment = convertToWorkflowEnvironment(myEnv);


// add a build action
workflowBuilder.addBuildAction({
  ...
  // add an account connection to the workflow
  environment: convertToWorkflowEnvironment(myEnv),
});
```

# Adding Dev Environments components to a blueprint
<a name="comp-dev-env-bp"></a>

Managed development environments (MDE) are used to create and stand up MDE Workspaces in CodeCatalyst. The component generates a `devfile.yaml` file. For more information, see [Introduction to Devfile](https://redhat-developer.github.io/devfile/) and [Editing a repository devfile for a Dev Environment](devenvironment-devfile-moving.md).

```
new Workspace(this, repository, SampleWorkspaces.default);
```

**To import Amazon CodeCatalyst blueprints workspaces components**

In your `blueprint.ts` file, add the following:

```
import {...} from '@amazon-codecatalyst/codecatalyst-workspaces'
```

# Adding issues components to a blueprint
<a name="comp-issues-bp"></a>

In CodeCatalyst, you can monitor features, tasks, bugs, and any other work involved in your project. Each piece of work is kept in a distinct recordcalled an issue. Each issue can have a description, assignee, status, and other properties, which you can search for, group and filter on. You can view your issues using the default views, or you can create your own views with custom filtering, sorting, or grouping. For more information about concepts related to issues, see [Issues concepts](issues-concepts.md) and [Quotas for issues in CodeCatalyst](issues-quotas.md).

The issue component generates a JSON representation of an issue. The component takes in an ID field and issue definition as input.

**To import Amazon CodeCatalyst blueprints issues components**

In your `blueprint.ts` file, add the following:

```
import {...} from '@amazon-codecatalyst/blueprint-component.issues'
```

**Topics**
+ [Issues components examples](#comp-issues-examples-bp)

## Issues components examples
<a name="comp-issues-examples-bp"></a>

### Creating an issue
<a name="comp-issues-create-bp"></a>

```
import { Issue } from '@amazon-codecatalyst/blueprint-component.issues';
...
new Issue(this, 'myFirstIssue', {
  title: 'myFirstIssue',
  content: 'This is an example issue.',
});
```

### Creating a high-priority issue
<a name="comp-issues-high-priority-bp"></a>

```
import { Workflow } from '@amazon-codecatalyst/codecatalyst-workflows'
...
const repo = new SourceRepository
const blueprint = this;
const workflowDef = workflowBuilder.getDefinition()

// Creates a workflow.yaml at .aws/workflows/${workflowDef.name}.yaml
new Workflow(blueprint, repo, workflowDef);

// Can also pass in any object and have it rendered as a yaml. This is unsafe and may not produce a valid workflow
new Workflow(blueprint, repo, {... some object ...});
```

### Creating a low-priority issue with labels
<a name="comp-issues-low-priority-bp"></a>

```
import { Issue } from '@amazon-codecatalyst/blueprint-component.issues';
...
new Issue(this, 'myThirdIssue', {
  title: 'myThirdIssue',
  content: 'This is an example of a low priority issue with a label.',
  priority: 'LOW',
  labels: ['exampleLabel'],
});
```

# Working with blueprint tooling and CLI
<a name="bp-cli"></a>

The [blueprint CLI](https://www.npmjs.com/package/@amazon-codecatalyst/blueprint-util.cli) provides tooling to manage and work with your custom blueprints.

**Topics**
+ [Working with blueprint tooling](#working-with-bp-cli)
+ [Image upload tool](#image-upload-tool)

## Working with blueprint tooling
<a name="working-with-bp-cli"></a>

**To work with the blueprint tools**

1. Open the CodeCatalyst console at [https://codecatalyst.aws/](https://codecatalyst.aws/).

1. Resume your Dev Environment. For more information, see [Resuming a Dev Environment](devenvironment-resume.md).

   If you don't have a Dev Environment, you must first create one. For more information, see [Creating a Dev Environment](devenvironment-create.md).

1. In a working terminal, run the following the command to install the blueprint CLI:

   ```
   npm install -g @amazon-codecatalyst/blueprint-util.cli
   ```

1. In the `blueprint.ts` file, import the tools you want to use in the following format:

   ```
   import { <tooling-function-name> } from '@amazon-codecatalyst/blueprint-util.cli/lib/<tooling-folder-name>/<tooling-file-name>;
   ```
**Tip**  
You can to the [https://github.com/aws/codecatalyst-blueprints/tree/main/packages/utils/blueprint-cli](https://github.com/aws/codecatalyst-blueprints/tree/main/packages/utils/blueprint-cli) to find the name of the tooling you want to use.

   **If you want to use the image uploading tool, add the following to your script:**

   ```
   import { uploadImagePublicly } from '@amazon-codecatalyst/blueprint-util.cli/lib/image-upload-tool/upload-image-to-aws';
   ```

   **Examples**
   + **If you want to use the publishing function, add the following to your script:**

     ```
     import { publish } from '@amazon-codecatalyst/blueprint-util.cli/lib/publish/publish';
     ```
   + **If you want to use the image uploading tool, add the following to your script:**

     ```
     import { uploadImagePublicly } from '@amazon-codecatalyst/blueprint-util.cli/lib/image-upload-tool/upload-image-to-aws';
     ```

1. Call the function.

   **Examples:**
   + **If you want to use the publishing function, add the following to your script:**

     ```
     await publish(logger, config.publishEndpoint, {<your publishing options>});
     ```
   + **If you want to use the image uploading tool, add the following to your script:**

     ```
     const {imageUrl, imageName} = await uploadImagePublicly(logger, 'path/to/image'));
     ```

## Image upload tool
<a name="image-upload-tool"></a>

The image upload tool provides you with the ability to upload your own image to an S3 bucket in your AWS account and then distribute that image publicly behind CloudFront. The tool takes an image path in the local storage (and optional bucket name) as input, and returns the URL to the image that is publicly available. For more information, see [What is Amazon CloudFront?](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html) and [What is Amazon S3?](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)

**To work with the image upload tool**

1. Clone the [open-source blueprints GitHub repository](https://github.com/aws/codecatalyst-blueprints) that provides access to the blueprints SDK and sample blueprints. In a working terminal, run the following command:

   ```
   git clone https://github.com/aws/codecatalyst-blueprints.git
   ```

1. Run the following command to navigate to the blueprints GitHub repository:

   ```
   cd codecatalyst-blueprints
   ```

1. Run the following command to install dependencies:

   ```
   yarn && yarn build
   ```

1. Run the following command to make sure the latest blueprint CLI version is installed:

   ```
   yarn upgrade @amazon-codecatalyst/blueprint-util.cli
   ```

1. Log in to the AWS account with the S3 bucket you want to upload your image to. For more information, see [Configure the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html), and [Sign in through the AWS Command Line Interface](https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html).

1. Run the following command from the root of your CodeCatalyst repository to navigate to the directory with the blueprint CLI:

   ```
   cd packages/utils/blueprint-cli
   ```

1. Run the following command to upload your image to an S3 bucket:

   ```
   yarn blueprint upload-image-public <./path/to/your/image> 
         <optional:optional-bucket-name>
   ```

A URL to your image is generated. The URL won’t be available immediately since it requires some time for the CloudFront distribution to be deployed. Check the distribution status to get the latest deployment status. For more information, see [Working with distributions](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html).

# Assessing interface changes with snapshot testing
<a name="testing-bp"></a>

Generated snapshot tests across multiple configurations of your blueprint are supported.

Blueprints support [snapshot testing](https://jestjs.io/docs/snapshot-testing) on configurations provided by you as a blueprint author. The configurations are partial overrides that are merged on top of the defaults.json file at the root of a blueprint. When snapshot testing is enabled and configured, the build and test process synthesizes the given configurations and verifies that the synthesized outputs haven’t changed from the reference snapshot. To view the snapshot testing code, see the [CodeCatalyst blueprints GitHub repository](https://github.com/aws/codecatalyst-blueprints/blob/main/packages/utils/projen-blueprint/src/test-snapshot.ts#L12).

**To enable snapshot testing**

1. In the `.projenrc.ts` file, update the input object to ProjenBlueprint with the files you want to snapshot. For example:

   ```
   {
     ....
     blueprintSnapshotConfiguration: {
       snapshotGlobs: ['**', '!environments/**', '!aws-account-to-environment/**'],
     },
   }
   ```

1. Resynthesize the blueprint to create TypeScript files in your blueprint project. Don’t edit the source files since they’re maintained and regenerated by Projen. Use the following command:

   ```
   yarn projen
   ```

1. Navigate to the `src/snapshot-configurations` directory to view the `default-config.json` file with an empty object. Update or replace the file with one or more of your own test configurations. Each test configuration is then merged with the project’s `defaults.json` file, synthesized, and compared to snapshots when testing. Use the following command to test:

   ```
   yarn test
   ```

   The first time you use a test command, the following message is displayed: `Snapshot Summary › NN snapshots written from 1 test suite`. Subsequent test runs verify that the synthesized output hasn’t changed from the snapshots and display the following message: `Snapshots: NN passed, NN total`.

   If you intentionally change your blueprint to produce a different output, then run the following command to update the reference snapshots:

   ```
   yarn test:update
   ```

Snapshots expect synthesized outputs to be constant between each run. If your blueprint generates files that vary, you must exclude those files from the snapshot testing. Update the `blueprintSnapshotConfiguration` object of your `ProjenBluerpint` input object to add the `snapshotGlobs` property. The `snapshotGlobs` property is an array of [globs](https://github.com/isaacs/node-glob#glob-primer) that determines which files are included or excluded from snapshotting.

**Note**  
There is a default list of globs. If you specify your own list, you may need to explicitly bring back the default entries.