Backstage に AWS Proton プラグインを導入し、テンプレートからサービスをデプロイしてみた

# はじめに

近年 **Platform Engineering** への注目が高まり、「開発者が **セルフサービス** でインフラやツールを使える環境づくり」が各社のテーマになっています。
そこで本記事では、開発者ポータル **Backstage** と **AWS Proton** を組み合わせ、

* Backstage からソフトウェアテンプレートを選択
* 即座にECS Fargateの環境を作成＆デプロイ

できるまでの手順を解説します。
まず、動くPlatform Engineeringの最小実装を試したい方の参考になれば幸いです。



# 前提知識

今回は、Backstage・AWS Protonを主に使用します。これらについて全く知らない人は以下の記事を見ていただけると良いかと思います。
### **Backstage** 
社内のソフトウェア資産(マイクロサービス、ライブラリ、データパイプライン、Webサイト、MLモデルなど)の作成・管理を一元化できるツールです。

https://qiita.com/tsutorm/items/1120293943418e28de0e
### **AWS Proton** 
コンテナおよびサーバーレスアプリケーションのための初の完全マネージド型アプリケーションデプロイサービスです。AWS Protonを用いることで、インフラストラクチャのプロビジョニング、コードデプロイ、モニタリング等ができます。

https://dev.classmethod.jp/articles/introduction-2024-proton/

# 今回作成するもの

少しわかりづらいですが、下図のようなアーキテクチャを作成していきます。
BackstageとAWS ProtonやGitHubリポジトリをプラグインで接続することで
  **アプリ開発用リポジトリ作成 → IaC デプロイ → サービス稼働** 　までBackstage上で操作が完結します。

```mermaid
graph TB
    %% スタイル定義
    classDef backstageComponent fill:#68dec4,stroke:#007058,stroke-width:2px
    classDef githubComponent fill:#f9f9f9,stroke:#24292e,stroke-width:2px
    classDef awsComponent fill:#FF9900,stroke:#232F3E,stroke-width:2px
    
    %% Backstageプラットフォーム
    subgraph Backstage[Backstageプラットフォーム]
        BackstageApp[Backstageフロントエンド]:::backstageComponent
        subgraph Backend[バックエンド]
          ProtonPlugin[AWS Protonプラグイン]:::backstageComponent
            SoftwareTemplates[Scaffolder
            ソフトウェアテンプレート]:::backstageComponent
        end
    end

    %% GitHubリポジトリ
    subgraph GitHub[GitHubリポジトリ]
        TemplateRepo[テンプレートリポジトリ]:::githubComponent
        AppRepo[アプリケーション
        リポジトリ]:::githubComponent
    end
    
    %% AWS Proton
    subgraph AWS[AWSサービス]
        AWSProton[AWS Proton]:::awsComponent
        AWSResources[AWSリソース
        （Fargateなど）]:::awsComponent
    end
    
    %% Backstage内部の関係
    BackstageApp -->|テンプレート選択| SoftwareTemplates
    SoftwareTemplates -->|プラグイン呼び出し| ProtonPlugin
    
    %% Backstageと外部システムの連携
    ProtonPlugin -->|API呼び出し| AWSProton
    SoftwareTemplates -.->|テンプレート読み込み| TemplateRepo
    SoftwareTemplates -->|新規リポジトリ作成| AppRepo
    AppRepo -->|AWSリソースへデプロイ| AWSResources
    
    %% AWS Protonの処理
    AWSProton -->|リソースプロビジョニング| AWSResources
    
    %% スタイル適用
    style Backstage fill:#b1fcec,stroke:#007058,stroke-width:4px
    style Backend fill:#b1fcec,stroke:#007058,stroke-width:2px
    style GitHub fill:#f9f9f9,stroke:#24292e,stroke-width:4px
    style AWS fill:#FFEBCC,stroke:#FF9900,stroke-width:4px


```

# 前提環境

この記事では、以下の前提条件に基づいて実装手順を説明します。
参考記事をもとに以下３つを構築してください。

| # | 項目                                     | 概要                                                    | 参考記事                                                                                                                             |
| - | -------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| 1 | **Backstage プロジェクトの作成**                | `npx @backstage/create-app` などで作成済み                   | [Backstage Getting Started](https://backstage.io/docs/getting-started/)                                                        |
| 2 | **AWS Proton テンプレートと環境の作成**          | 環境テンプレート + サービステンプレートを作成し、環境をプロビジョニング済み | [AWS Proton 入門 (DevelopersIO)](https://dev.classmethod.jp/articles/introduction-2024-proton/)                                  |
| 3 | **Backstage から AWS Proton へアクセスする権限を付与** | 私の場合は、EC2にサーバーを立ててローカルのvscode上で開発しており、EC2にIAMロールを追加しました。           | [IAM Permissions Guide](https://github.com/awslabs/aws-proton-plugins-for-backstage/blob/main/docs/install.md#iam-permissions) |

1でBackstageプロジェクトを作成すると、以下のようなディレクトリ構造になっていると想定しています。

```text
test-backstage/
├── packages/
│   ├── app/
│   │   └── src/
│   │       └── components/
│   │           └── catalog/
│   │               └── EntityPage.tsx
│   └── backend/
│       └── src/
│           ├── index.ts
│           └── plugins/
├── app-config.yaml
└── package.json
```

# 実装手順

基本的に、以下の記事を参考に実装を進めています。

https://github.com/awslabs/aws-proton-plugins-for-backstage/blob/main/docs/tutorial.md

## 1. `template.yaml` の作成

Backstage上のテンプレートを選択した際に、  
- どんな入力項目を設定し  
- どの Scaffolder Action を呼び出すか  

を定義するファイルを作成します。  
ここでは、下記にて提供されているテンプレートを少し修正していきます。

https://github.com/awslabs/aws-proton-plugins-for-backstage/blob/main/docs/tutorial-assets/fargate-nginx-template/template.yaml

### 1.1 テンプレートリポジトリをクローン

```bash
# （任意の場所）
git clone git@github.com:<your-org>/aws-proton-plugins-for-backstage.git
````

### 1.2 `template.yaml` を微修正
以下の`XXX`部分について適切な値に変更してください。その他はデフォルトの設定で良いです。
修正後、自身のGitHubリポジトリにpushしてください。

```yaml
steps:

  - id: template
    name: Fetch Skeleton + Template
    action: fetch:template
    input:
      url: ./skeleton
      values:
        #Protonで環境テンプレートから作成した環境の名前を入れてください。
         aws_proton_dev_environment_name: "XXX"
         aws_proton_prod_environment_name: "XXX"
        #自身のAWSアカウントIDを入れてください。
         aws_account_id: "XXX"


  - id: create-proton-service
    name: Create AWS Proton Service
    action: aws:proton:create-service
    input:
        #Protonで作成したサービステンプレートの名前を入れてください。
         templateName: XXX
        #Protonでテンプレート作成時に設定したCodeStar ConnectionsのARNを設定してください。
         repositoryConnectionArn: 'arn:aws:codeconnections:us-east-1:XXX:connection/XXX'
```

## 2. Backstage 設定ファイルの更新

### 2.1 `app-config.yaml`の設定

既存の`app-config.yaml`ファイルに以下を追加してください。
> **ポイント**
>
> * **`aws`セクション**：リージョンの追加
> * **`integration`セクション**：GitHub認証情報とAWSのリージョンを追加
> * **`proxy` セクション**：Backstage → Proton API 用のリバースプロキシ
> * **`catalog.locations`セクション**：[1. template.yaml の作成](#1-templateyaml-の作成)でfork した `template.yaml` への URL を登録

```yaml
# AWS
aws:
  region: us-east-1

# GitHub / AWS 統合
integrations:
  github:
    - host: github.com
      apps:
        - $include: github-credentials.yaml
  aws:
    - region: us-east-1

# Proxy
proxy:
  endpoints:
    "/api/aws-proton":
      target: "http://localhost:7007/api/aws-proton"
      changeOrigin: true
    "/api/aws-proton-backend":
      target: "http://localhost:7007/api/aws-proton-backend"
      changeOrigin: true

# テンプレート登録
catalog:
  locations:
    - type: url
      target: https://github.com/<your-org>/aws-proton-plugins-for-backstage/blob/main/docs/tutorial-assets/fargate-nginx-template/template.yaml
      rules:
        - allow: [Template]
```

### 2.2 `github-credentials.yaml`の設定
`github-credentials.yaml`には、GitHubで作成したGitHub Appsの情報を格納しています。
`app-config.yaml`ファイルと同じディレクトリに作成してください。

```yaml
appId: 111111
clientId: abcdefghi123
clientSecret: xxxxx
webhookSecret: yyyyy
privateKey: |
  -----BEGIN RSA PRIVATE KEY-----
  (省略)
  -----END RSA PRIVATE KEY-----
```

### 2.3 ここまでの動作確認
Backstage 設定ファイルの更新まで完了したら、以下のテンプレートがBackstage上に表示されるか確認してみましょう。（テンプレートの名前や色は違うかもしれません。）
うまく表示されていれば、次に進みましょう。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4096150/cb23f74f-17c4-4ffe-b5fb-01f764619a07.png)


## 3. AWS Proton プラグインをインストール

AWS Proton用のBackstageプラグインをインストールします。
```bash
yarn add @aws/aws-proton-plugin-for-backstage           # frontend
yarn add @aws/aws-proton-backend-plugin-for-backstage   # backend
```

## 4. Backstage バックエンド の実装

ここでは、
- 2ファイル（`awsProton.ts`・`scaffolderActions.ts`）の新規作成
- 1ファイル（`index.ts`）の修正

を行います。

### 4.1 `awsProton.ts`ファイルの作成

`packages/backend/src/plugins/awsProton.ts`ファイルを新規作成し、AWS Protonサービスとの通信を行うAPIエンドポイントを実装しています。
これにより、既存のAWS Protonリソースの情報を Backstage上で取得・表示することができます。

```ts
import { createBackendPlugin, coreServices } from '@backstage/backend-plugin-api';
import express from 'express';
import Router from 'express-promise-router';
import {
  ProtonClient,
  ListEnvironmentsCommand,
} from '@aws-sdk/client-proton';

export default createBackendPlugin({
  pluginId: 'aws-proton-backend',
  register(env) {
    env.registerInit({
      deps: {
        config: coreServices.rootConfig,
        httpRouter: coreServices.httpRouter,
      },
      async init({ config, httpRouter }) {
        const region = config.getString('aws.region');
        const client = new ProtonClient({ region });
        const router = Router();

        router.get('/environments', async (_, res) => {
          const output = await client.send(new ListEnvironmentsCommand({}));
          res.json(output.environments ?? []);
        });

        httpRouter.use('/api/aws-proton', router);
      },
    });
  },
});
```

### 4.2 `scaffolderActions.ts`ファイルの作成

`packages/backend/src/plugins/scaffolderActions.ts`を新規作成し、BackstageのテンプレートからAWS Protonサービスを作成するためのアクションを登録しています。

```ts
import { createBackendModule, coreServices } from '@backstage/backend-plugin-api';
import { createAwsProtonServiceAction } from '@aws/aws-proton-backend-plugin-for-backstage';
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';

export default createBackendModule({
  pluginId: 'scaffolder',
  moduleId: 'aws-proton-actions',
  register(env) {
    env.registerInit({
      deps: {
        actions: scaffolderActionsExtensionPoint,
        config: coreServices.rootConfig,
      },
      async init({ actions, config }) {
        actions.addActions(createAwsProtonServiceAction({ config }));
      },
    });
  },
});
```

### 4.3  `index.ts`ファイルの修正

`packages/backend/src/index.ts`ファイルに、作成したプラグインを登録します。

```ts
import { createBackend } from '@backstage/backend-defaults';
import awsProtonBackendModule from './plugins/awsProton';
import scaffolderActionsModule from './plugins/scaffolderActions';
// 他のモジュールも追加
// ...

const backend = createBackend();

backend.add(awsProtonBackendModule);
backend.add(scaffolderActionsModule);

// 他のプラグインも追加
// ...

backend.start();
```

### 4.4 ここまでの動作確認
以下の動作が正常に実行できたら、次に進みましょう。

#### 4.4.1 テンプレート選択後
![image (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4096150/4755c966-b288-4714-849c-4e639afbf823.png)

#### 4.4.2 必要項目入力＆Createクリック後
![image (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4096150/6916ad33-d83f-4ee4-8084-e03878ec563b.png)


## 5. Backstage フロントエンド の実装

### 5.1 フロントエンド への Proton カード追加
`packages/app/src/components/catalog/EntityPage.tsx` にカードを差し込む例です。

```tsx
import {
  EntityAWSProtonServiceOverviewCard,
  isAWSProtonServiceAvailable,
} from '@aws/aws-proton-plugin-for-backstage';

const overviewContent = (
  <Grid container spacing={3}>
    <EntitySwitch>
      <EntitySwitch.Case if={isAWSProtonServiceAvailable}>
        <Grid item md={6}>
          <EntityAWSProtonServiceOverviewCard />
        </Grid>
      </EntitySwitch.Case>
    </EntitySwitch>
  </Grid>
);
```

### 5.2 ここまでの動作確認
AWS Proton Serviceカードが以下のように正しく表示されれば、連携は完了です 🎉
![image (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4096150/93e414df-3206-42a5-9acd-21eb16d01d36.png)


# まとめ
今回、プラグインを用いてBackstage と AWS Proton をつなげることで、**テンプレート選択 → Fargate デプロイ** が簡単にできるようになりました。

Backstageには他にもあらゆるプラグインが存在しており、AWS Codepipelineと連携しCI/CD状況の可視化やGrafanaと連携しダッシュボードの表示などが簡単にできます。
時間があれば、他にもBackstageに関わる記事を書きたいと思っています！



## 参考資料

* [**AWS Proton plugins for Backstage – README**](https://github.com/awslabs/aws-proton-plugins-for-backstage)

* [**Backstage Getting Started**](https://backstage.io/docs/getting-started/)

* [**AWS 入門ブログリレー 2024 〜 AWS Proton 編**（DevelopersIO）](https://dev.classmethod.jp/articles/introduction-2024-proton/)