VPC Private subnet 上の Ubuntu 24.04 EC2 デスクトップ環境に Amazon DCV でセキュアに接続する(前編)

本記事は 夏休みクラウド自由研究2026 8/25付の記事です

この記事は前編・中編・後編の3部構成です。

– 前編 (この記事) : VPC Private subnet 上の Ubuntu 24.04 EC2 デスクトップ環境に Amazon DCV でセキュアに接続する(前編)
– 中編 (作成中) : VPC Private subnet 上の Ubuntu 24.04 EC2 デスクトップ環境に Amazon DCV でセキュアに接続する(中編)
– 後編 (作成中) : VPC Private subnet 上の Ubuntu 24.04 EC2 デスクトップ環境に Amazon DCV でセキュアに接続する(後編)

こんにちは、SCSKで テクニカルエスコートサービス を担当してる兒玉です。

Linux 開発環境はAWS には Cloud 9 があったのですが残念ながら終了してしまい、後継と目していた Amazon CodeCatalyst も東京リージョンに来る前に新規受付終了とのことで、AWS 上での Linux 開発環境を利用していた人たちは皆迷子になっていると思います。
 

Linux の開発環境を用意したいのですが、会社より支給される PC は Windows が標準で、しかもストレージなどが手狭で Linux の開発には向かないなぁ、と思っている方はいらっしゃいませんか?
そんなあなたにピッタリの企画です。

同じ悩みを抱いている人たちはたくさんいて、お二方の記事も大変参考になります。
むしろ用途によってはそちらのほうが向いているかもしれませんので、併せてご参考ください。
私(兒玉)のコンセプトとしては
  •  EC2 を VPC Private subnet 上に作成したい
    • リモートデスクトップのような口を外に開けるのはちょっと怖いので
  • Linuxのデスクトップ環境も使用したい
    • コンソールや Kiro / VSCode などからのSSH接続だけでなく、デスクトップ環境も使用したい

です

構成概要

本記事の全体像は下記のようになります。


構成する要素がたくさんあるように見えますが、最終目的は中央下の EC2 になります。
右下のWindows PC から AWS Systems Manager Sessions Manager 経由で Amazon DCV を使って EC2 上のLinux デスクトップにセキュアにアクセスします。

まず、前編では、EC2 のイメージを作成するところから始めます。

イメージパイプラインの構築

イメージビルダーは苦戦の原因でもあり、苦戦からたどり着いた結論でもあります。
当初全てEC2 起動時に動作する ユーザーデータ(User Data) に必要なインストールコマンドをすべて記述して実行しようとしていたのですが、インストールにかかる時間がとにかく長い!
ユーザーデータを修正して、CloudFormationでデプロイして、EC2が起動した後接続して各種ログを見たり動作確認をしたりしてエラーや原因を探す… などするとワントライ30分以上かかってしまい、イライラする原因でした。
既にユーザーデータの実行スクリプト中の実績のある箇所は EC2 Image Builder で構築して 中間の AMI を作成し、そこからスタートして ユーザーデータの検証を行うことができないかと考え、EC2 Image Builder のパイプライン作成に舵を切りました。
EC2 Image Builder は以下のような構成になっています。
EC2 Image Builder Pipeline
  1. EventBridge または 手動でイメージレシピを元にイメージパイプラインを実行。以降はすべて EC2 Image Builder が実行してくれます。
  2. ベースイメージ AMI から EC2 インスタンスを作成して VPC にデプロイ
  3. レシピを元にコンポーネントや設定をインストール
  4. インストール完了後、出力イメージAMI を作成
  5. Inspector を使用して 脆弱性検査を実施
  6. テストのために出力イメージをVPCに再デプロイ
  7. テストを実行
  8. 完了したら Amazon SNS から結果を知らせる

Amazon SNS を利用した結果通知ですが、パイプラインの実行は長時間かかるため、パイプライン実行中は別のこととしていて、実行完了時にメールなど結果を知らせてもらうような使い方や、定期的にゴールデンイメージを作成するようなケースでは FAILED となった場合には対応が必要なのでメールで通知するような運用ケースが考えられます。

パイプライン作成方針

パイプラインの CloudFormation 作成方針としては以下を考慮しました。

    • EC2 Image Builder のパイプラインはモジュールの追加や、エラー対応などのトライアンドエラーを行うので CloudFormation で修正を繰り返すことになる(前提条件)
      • CloudFormationスタックの updateや create-change-set もやってみたのですが、うまく適用できず結局削除となるケースが多々あった
      • パイプラインの実行環境である VPC は EC2 Image Builder の CloudFormation テンプレートで一緒に作ってしまうと毎回作成/削除時に余計な時間がかかってしまう
      • イメージ作成の際に、インターネットからのダウンロードが必要なので、外向きの出口が必要で、NAT Gateway などを作成する必要がある
        • イメージ作成時しか使わないので何本もパイプラインを作成する運用を考えるとそのパイプライン専用のAWSリソースとして作成するのは費用的にもったいない
    • CloudFormationテンプレートにVPC、サブネット、NAT Gatewayなどの情報も記載すると煩雑になる
    • SNS トピックについては、作成後にサブスクリプションが必要で、何度も作り直すとなると毎回サブスクリプションの承認が必要になるのは面倒

という点から、VPC 、Subnet、セキュリティグループ、SNS トピックなどのリソースは作成済みのIDをパラメータで渡すことにしました。

Ubuntu 24.04 EC2 Image Builder pipeline の構築

準備その1 : 必要なファイルの準備

それでは構築を始めましょう。

必要なファイルをまとめた zip ファイルをダウンロードして、CloudShellにアップロードします。

ubuntu24-pipeline.zip

SHA256: a20bb1d5bfbf44121497e757a6a4ab82524b1dc568b02c66cb240972c582611a

CloudShellに必要ファイルをアップロード

zip ファイルの中身を展開してフォルダを移動します。

~ $ unzip ubuntu24-pipeline.zip -d ./ubuntu24-pipeline
Archive: ubuntu24-pipeline.zip
inflating: ./ubuntu24-pipeline/ubuntu2404-pipeline.yaml 
inflating: ./ubuntu24-pipeline/shared-resources.yaml 
inflating: ./ubuntu24-pipeline/deploy-imagebuilder-pipelines.sh 
inflating: ./ubuntu24-pipeline/ubuntu2404-pipeline-deployment-guide.md 
inflating: ./ubuntu24-pipeline/vpc-infrastructure.yaml 
inflating: ./ubuntu24-pipeline/deploy-vpc-resources.sh 
~ $ cd ubuntu24-pipeline
ubuntu24-pipeline $

デプロイ用の環境変数( PROJECT_NAME と COST_TAG_VALUE ) を設定します。
PROJECT_NAME は作成するリソースの prefix(接頭語) となります。英小文字、数字、ハイフン、11文字まで 例: yourproject

英小文字、数字、記号はハイフンのみとしているのは この PROJECT_NAME が S3バケットの prefix として使用される場合にS3のバケットの命名規則により作成できなくなるからです。 

汎用バケットの命名規則 - Amazon Simple Storage Service
Amazon S3 バケットの命名規則について説明します。

COST_TAG_VALUE は コスト配分タグとして利用するタグ( タグ名 “Cost” ) 例: yourdivision

ubuntu24-pipeline $ export PROJECT_NAME=kodama-th
ubuntu24-pipeline $ export COST_TAG_VALUE=j.kodama
ubuntu24-pipeline $

準備その2 : VPC、Private Subnet、Security Group、NAT Gateway、VPC Endpoint  (SSM、SSM Messages、EC2 Messages)  の作成

EC2をデプロイするために、VPC、Private Subnet、Security Group が必要になります。
EC2は Private Subnet に配置します。 しかし、EC2へのインストールに必要なファイル群はインターネットからダウンロードしてくるので、インターネットへのアクセスのために Public Subnet に NAT Gateway の配置と適切なルーティングテーブル設定が必要です。

このNAT Gateway を経由して Private Subnet の Image Builder が使用する ビルド用EC2インスタンス がインターネットに出られるようにしておきます。

VPC Endpoint(SSM、SSM Messages、EC2 Messages)  3つは EC2 が Systems Manager とプライベートに通信するのに必要です。

既に作成済みのリソースを割り当てる場合は準備その3 に進んでください。

deploy-vpc-resources.sh を実行して VPCなどのリソースをデプロイします。

ubuntu24-pipeline $ ./deploy-vpc-resources.sh 
=== Configuration ===
PROJECT_NAME: kodama-th
COST_TAG_VALUE: j.kodama
CREATE_DATE: 2026/01/28
STACK_NAME: kodama-th-vpc-infrastructure
TEMPLATE_FILE: vpc-infrastructure.yaml

=== Deploying VPC Infrastructure Stack ===
Stack does not exist, creating new stack...
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/kodama-th-vpc-infrastructure/abcd1234-de56-ab12-cd34-abdce12345",
    "OperationId": "1234bcde-ab01-cd12-ef34-abcdef012345"
}
Waiting for stack creation to complete...
VPC infrastructure created successfully

=== Deployment completed ===

Stack Outputs:
-------------------------------------------------
|                DescribeStacks                 |
+------------------+----------------------------+
|        Key       |           Value            |
+------------------+----------------------------+
|  VPCId           |  vpc-01234567890abcdef     |
|  SecurityGroupId |  sg-567890abcdef01234      |
|  PrivateSubnetId |  subnet-90abcdef012345678  |
+------------------+----------------------------+
ubuntu24-pipeline $

正常に完了すると、VPCId、SecurityGroupId、PrivateSubnetId が表示されるのでメモしておきます。

準備その3 : SNS トピックの作成

EC2 Image Builder のパイプラインの実行の完了を通知してもらう際の通知先として定義します。
完了するまでマネジメントコンソールを眺めていてもよいのですが、レシピの内容によっては実行に長時間かかる場合があるのでずっと待っているのは無駄だからです。

すでに通知用として機能するSNSのトピックに通知してもらうように設定する場合は、トピックARNをメモして 次の EC2 Image Builder パイプラインの作成に進んでください。

トピックの作成には create-topic コマンドを使用します。

$ aws sns create-topic --name my-image-builder-result-topic
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:123456789012:my-image-builder-result-topic"
}
$

応答の TopicArn をメモしておいて、そこに通知先のメールアドレスを追加します。

$ aws sns subscribe \
  --topic-arn arn:aws:sns:ap-northeast-1:123456789012:my-image-builder-result-topic \
  --protocol email \
  --notification-endpoint user@example.com
{
    "SubscriptionArn": "pending confirmation"
}
$

“pending confirmation” と表示されればOKです。
メールが届いているはずなので、Confirm subscription リンクをクリックしてサブスクリプションします。

Topicにサブスクリプションされたかどうかは以下のコマンドで確認できます。

$ aws sns list-subscriptions-by-topic \
   --topic-arn arn:aws:sns:ap-northeast-1:123456789012:my-image-builder-result-topic \
   --query 'Subscriptions[*].{Email:Endpoint,Protocol:Protocol,Status:SubscriptionArn}' \
   --output table
---------------------------------------------------------------------------------------------------------------------------------------------------
|                                                            ListSubscriptionsByTopic                                                             |
+------------------+-----------+------------------------------------------------------------------------------------------------------------------+
|       Email      | Protocol  |                                                     Status                                                       |
+------------------+-----------+------------------------------------------------------------------------------------------------------------------+
|  user@example.com|  email    |  arn:aws:sns:ap-northeast-1:123456789012:my-imagebuilder-result-topic:defg1234-ab56-cd78-ef90-45678abcdef        |
+------------------+-----------+------------------------------------------------------------------------------------------------------------------+
$

サブスクリプションが完了している場合には、Status欄にサブスクリプションARNが表示され、サブスクリプションが完了していない場合には、Status欄が pending confirmation となります。

 

EC2 Image Builderパイプラインのデプロイ

次はパイプラインのデプロイです。

必要な情報は

  • Security Group ID
  • Private Subnet ID
  • SNS Topic 名(メモしたTopic ARN の一番最後コロンより後の部分 例: my-image-builder-result-topic ) 
  • プロジェクト名 (他のリソースと競合しないように付与するリソースの prifix(接頭語)) 
  • コスト配分タグの値 

です。
パラメータ無しで、 `deploy-imagebuilder-pipelines.sh` を実行すると、必要な環境変数名と設定例を出力してくれますので、これを参考に値を設定します。

$ ./deploy-imagebuilder-pipelines.sh 
Error: The following environment variables are not set:
  - PROJECT_NAME
  - COST_TAG_VALUE
  - SECURITY_GROUP_ID
  - PRIVATE_SUBNET_ID
  - SNS_TOPIC_NAME

Please set them using the following commands:
export PROJECT_NAME="<your-project-name>"
export COST_TAG_VALUE="<your-cost-allocation-department-name>"
export SECURITY_GROUP_ID="sg-<your-sg-id>" like sg-01234567890abcdef
export PRIVATE_SUBNET_ID="subnet-<your-subnet-id>" like subnet-01234567890abcdef
export SNS_TOPIC_NAME="<your-sns-topic-name>" like imagebuilder-notifications
$

PROJECT_NAME と COST_TAG_VALUE は設定済みと思いますが PROJECT_NAME については 11 文字以内で 小文字のみ記号はハイフンのみ有効なことに注意してください。

パラメータを環境変数に設定して、 `deploy-imagebuilder-pipelines.sh` を実行します。

ubuntu24-pipeline $ export PROJECT_NAME=kodama-th
ubuntu24-pipeline $ export COST_TAG_VALUE=j.kodama
ubuntu24-pipeline $ export SECURITY_GROUP_ID=sg-567890abcdef01234
ubuntu24-pipeline $ export PRIVATE_SUBNET_ID=subnet-90abcdef012345678
ubuntu24-pipeline $ export SNS_TOPIC_NAME=my-imagebuilder-result-topic
ubuntu24-pipeline $ ./deploy-imagebuilder-pipelines.sh 
=== Validating AWS resources ===
Checking SNS Topic: my-imagebuilder-result-topic
  ✓ SNS Topic exists
Checking Security Group: sg-567890abcdef01234
  ✓ Security Group exists
Checking Subnet: subnet-90abcdef012345678
  ✓ Subnet exists
=== All AWS resources validated successfully ===

=== DEBUG: Tag values ===
PROJECT_NAME: kodama-th
COST_TAG_VALUE: j.kodama
CREATE_DATE: 2026-01-29
COMMON_TAGS: Cost=j.kodama Project=kodama-th CreateDate=2026-01-29
Press Enter to continue...

パラメータとして渡したリソースが実際に存在するかを確認して、タグ値の出力をしてくれます。
何度も間違ったパラメータを渡してデプロイして CREATE_FAILED となりやり直すのに時間がかかってしまった苦い経験からチェックを入れています。
問題なければ Enter キーで先に進めます。

Press Enter to continue...

=== Step 1: Create ChangeSet for shared resources ===
Stack does not exist, creating new stack...
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/kodama-th-shared-resources/12345678-1234-1234-1234-123456789abc",
    "OperationId": "23456789-2345-2345-2345-23456789abcd"
}
Waiting for stack creation to complete...
Shared resources created successfully
=== Step 2: Deploy Ubuntu2404 pipeline ===
Stack kodama-th-ubuntu2404-pipeline does not exist, creating new stack...
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/kodama-th-ubuntu2404-pipeline/34567890-3456-3456-3456-34567890abcd",
    "OperationId": "45678901-4567-4567-4567-45678901abcd"
}
kodama-th-ubuntu2404-pipeline created successfully
=== Deployment completed ===
ubuntu24-pipeline $

構築完了後、 SNS から EC2 Image Builder の作成時に発行される SNS発行権限確認メールが来ます。

 

イメージパイプラインの実行

作成されたイメージパイプラインからEC2 のイメージ (AMI) を作成します。
作成されたCloudFormation スタックのリソースタブから 作成された <プロジェクト名>-ubuntu2404-pipeline のスタックの、リソース ImagePipeline に表示されているリンクをクリックして EC2 Image Builder のイメージパイプラインの画面を表示します。

EC2 Image Builder のイメージパイプラインの画面から、アクション→パイプラインを実行 を選択し、パイプラインが動作させて AMI の作成を開始します。


 
AMIイメージの作成にはかなり時間がかかりますので、完了のメールが届くまで気長に待ちましょう。

パイプラインの実行結果の確認

完了メールが来ました!

メールに json で実行時された内容が詳しく記載されています。
“arn” が実行されたレシピの ARN、”state” の “status” が作成された AMI のステータスです。

SNS の完了通知は AVAILABLE か  FAILEDの場合のみ来るので、 AVAILABLE なら成功、FAILED なら失敗ですね。

 

Amazon SNS integration in Image Builder - EC2 Image Builder
Configure Image Builder to send detailed image build action messages to an Amazon SNS topic.

メール内の outputResources に 作成された AMI の ID が入ってます。

...
  "outputResources": {
    "amis": [
      {
        "region": "ap-northeast-1",
        "accountId": "123456789012",
        "image": "ami-01234567890abcdef",
        "name": "kodama-th-ubuntu2404-ami-2026-01-29T10-56-12.206Z"
      }
    ]
  },
...

 

SNS からメールですが、定期実行するなどのユースケースで、EC2 Image Builder からのメッセージは FAILED の場合だけ原因究明の必要があるので確認したいという場合、SNSサブスクリプションに サブスクリプションフィルタポリシーを設定することで実現できます。

SNSのサブスクリプションから、サブスクリプションフィルターポリシーの編集を選択し、

サブスクリプションフィルターポリシーのチェックをON

フィルターポリシーのスコープで、メッセージ属性を選択し、JSONエディタにサブスクリプションフィルターポリシーを貼り付けて変更を保存します。

設定するフィルタポリシは、以下のようになります。

{
  "$or": [
    {
      "sourcePipelineArn": [
        {"prefix": "arn:aws:imagebuilder:"}
      ],
      "state": {
        "status": ["FAILED"]
      }
    },
    {
      "sourcePipelineArn": {
        "exists": false
      }
    }
  ]
}

このフィルターの意味ですが、

“$or” は「以下のいずれかの条件に一致するメッセージを受信する」という意味です

条件1(OR の最初の要素)

{
  "sourcePipelineArn": [{"prefix": "arn:aws:imagebuilder:"}],
  "state": {"status": ["FAILED"]}
}

意味:

sourcePipelineArnフィールドが存在し、かつ
その値がarn:aws:imagebuilder:で始まり(Image Builderのメッセージ)、かつ
state.statusがFAILEDである
  → 該当するメッセージ: Image Builderのパイプライン実行が失敗した通知

条件2(OR の2番目の要素)

{
  "sourcePipelineArn": {"exists": false}
}

意味:

sourcePipelineArnフィールドが存在しない
→ 該当するメッセージ: Image Builder以外の全てのメッセージ(CodePipeline、CloudWatch Alarms、Lambda通知など)

となるので、SNSトピックを他のサービスの通知と共有している場合でも他のメッセージは引き続き受信することができます。

Monitoring CodePipeline events - AWS CodePipeline
Learn how to monitor changes in the state of a pipeline, stage, or action.

実行時のログをみると、今回は40分弱かかったようです。

次回は作成されたAMIからEC2を起動します。

著者について

ビデオゲームと昼寝と現実逃避をこよなく愛するシステムエンジニア。 好きなAWSサービスは AWS Lambda 。 マネジメントサービスであることだったり、pay-as-you-go の極致であったり、簡単に使えたり、でも使いこなすには奥が深かったりと、AWSの良さと楽しさを最大限に含まれるサービスだと思っています!

兒玉純をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(AWS)は、企業価値の向上につながるAWS 導入を全面支援するオールインワンサービスです。AWS最上位パートナーとして、多種多様な業界のシステム構築実績を持つSCSKが、お客様のDX推進を強力にサポートします。

AWSクラウドその他技術ナレッジ
シェアする
×
タイトルとURLをコピーしました