Service Catalog導入9ヶ月、セルフサービス化でテンプレート管理の地獄から脱出した

AWS Service Catalogで本番運用9ヶ月。営業チームの環境セットアップ2日問題を解決した実装記。テンプレート管理、セキュリティガバナンス、新人オンボーディングがどう変わったか、失敗も含めてお伝えします。

Service Catalogって、結局何が嬉しいのか

正直、最初は懐疑的だった。うちのチームは既にCloudFormationテンプレートで標準化されてるし、わざわざService Catalogを入れる必要あるのか、って思ってたんですよ。でも営業チームが「新しいプロジェクトの開発環境セットアップに毎回2日かかる」って泣きついてきたのがきっかけ。その時点で「あ、これはセルフサービス化で解決できるやつだ」って気づいた。

9ヶ月本番運用してみた結果、単なるテンプレート配布ツールじゃなくて、組織全体のIaCガバナンスを仕組み化できる仕掛けだってことが分かった。特に大きかったのが3つ。

まず、開発チームが勝手にリソース作れなくなった。今まではVPC、セキュリティグループ、RDS辺りを適当に作ってたのが、プリセットされたポートフォリオから選ぶだけになった。これでセキュリティ監査のときの「なんこのリソース?」が激減した。次に、テンプレート更新が一元化された。複数チームが同じCloudFormationテンプレートをコピペしてたから、片方を直しても別チームが古いバージョン使ってるとか、そういう地獄がなくなった。最後が、新人オンボーディングがめっちゃ短くなった。「このカタログから選んで発行ボタン押して」の3ステップで済む。以前は「ここのセキュリティグループ設定、こっちのテンプレートだけ違うんですよ」みたいな説明してた。

実装の流れ:テンプレート整備→ポートフォリオ設計→プロビジョニング自動化

ステップ1:CloudFormationテンプレートの整備

Service Catalogで管理するには、CloudFormationテンプレートを「再利用可能な形」にする必要があった。うちの場合、既存テンプレートは個別プロジェクト向けにカスタマイズされすぎてて、そのまま流用できなかった。

実際にやったのはこんな感じ:

  • パラメータ化:VPC CIDR、RDSインスタンスタイプ、Kubernetesバージョンなど、変数化できるところは全部Parametersに
  • Metadataセクション:テンプレート説明、パラメータのデフォルト値、制約を定義して、ユーザーの迷いを減らす
  • Outputsセクション:発行後に「このリソースのID何?」って調べなくて済むようにEndpoint、セキュリティグループIDとかを出力
AWSTemplateFormatVersion: '2010-09-09'
Description: 'ECS Cluster with VPC - Service Catalog Product'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Network Configuration"
        Parameters:
          - VpcCIDR
          - PrivateSubnet1CIDR
          - PrivateSubnet2CIDR
      - Label:
          default: "ECS Configuration"
        Parameters:
          - ClusterName
          - DesiredCount
          - InstanceType

Parameters:
  VpcCIDR:
    Type: String
    Default: "10.0.0.0/16"
    Description: CIDR block for the VPC
  
  ClusterName:
    Type: String
    Description: ECS Cluster name
    MinLength: 1
    MaxLength: 64
  
  DesiredCount:
    Type: Number
    Default: 3
    MinValue: 1
    MaxValue: 10
    Description: Desired number of ECS tasks
  
  InstanceType:
    Type: String
    Default: t3.medium
    AllowedValues:
      - t3.small
      - t3.medium
      - t3.large
      - m5.large
      - m5.xlarge

Resources:
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref ClusterName
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

  # ...その他のリソース

Outputs:
  ClusterName:
    Value: !Ref ECSCluster
    Export:
      Name: !Sub "${AWS::StackName}-ClusterName"
  
  ClusterArn:
    Value: !GetAtt ECSCluster.ClusterArn
    Export:
      Name: !Sub "${AWS::StackName}-ClusterArn"

この工程、実は思ったより時間かかった。既存テンプレートの「ここはなぜこの値?」「このセキュリティグループ設定の背景は?」みたいなのを掘り下げる必要があって。けど逆にこれを機に、チーム内で「なぜこういう設定にしてるのか」の共通理解が進んだんだ。地味に大事だった。

ステップ2:ポートフォリオの設計と管理

Service Catalogは「ポートフォリオ」という概念でプロダクトをグループ化する。うちは組織構造に合わせてこんな感じで構成した。

ポートフォリオプロダクト例対象チーム
Platform FoundationVPC + Security Groups、RDS + Backup全チーム
Application RuntimeECS Cluster、Lambda Layer、API Gatewayバックエンド・アプリ
Data PipelineS3 + Glue + Athena、EventBridge Workflowデータ・分析
Development ToolsCloudWatch Dashboard、SNS Alert Topic全チーム

これをAWSコンソール上で設定するのもいいけど、自分たちはTerraformで管理することにした。理由は、ポートフォリオとプロダクト自体もコード化したかったから。GitOpsで追跡可能になるしね。

# main.tf - Service Catalog Portfolio
resource "aws_servicecatalog_portfolio" "platform" {
  name          = "Platform Foundation"
  description   = "Core infrastructure products"
  provider_name = "Platform Engineering Team"

  tags = {
    Environment = "shared"
    ManagedBy   = "terraform"
  }
}

resource "aws_servicecatalog_portfolio_share" "engineering" {
  portfolio_id = aws_servicecatalog_portfolio.platform.id
  principal_arn = "arn:aws:iam::ACCOUNT:role/EngineeringTeamRole"
  type = "ACCOUNT"
}

# プロダクト登録(CloudFormationテンプレートS3 URIを指定)
resource "aws_servicecatalog_product" "vpc_foundation" {
  name             = "VPC + Security Groups"
  owner            = "Platform"
  type             = "CLOUD_FORMATION_TEMPLATE"
  description      = "Standard VPC setup with public/private subnets"
  distributor      = "Platform Engineering"
  
  provisioning_artifact_parameters {
    template_url = "s3://${aws_s3_bucket.templates.id}/vpc-foundation-v2.yaml"
    type         = "CLOUD_FORMATION_TEMPLATE"
    name         = "v2.0"
  }

  tags = {
    Category = "Network"
  }
}

resource "aws_servicecatalog_principal_portfolio_association" "vpc_for_engineering" {
  portfolio_id  = aws_servicecatalog_portfolio.platform.id
  principal_arn = "arn:aws:iam::ACCOUNT:role/EngineeringTeamRole"
  principal_type = "IAM_PATTERN"
}

Terraform管理の利点は、GitOpsで追跡できることと、プロダクト版管理が簡単なこと。テンプレートS3内に新しいバージョン作ったら、provisioning_artifact_parametersのブロック追加するだけで「v2.0」「v3.0」が並走する。エンドユーザーが「どのバージョン使おう」って選べるんだ。

ステップ3:プロビジョニング自動化とセルフサービス

ここが本当に重要。Service Catalogのプロダクト発行自体は誰でも2分でできる。でも本番運用を見据えると、いくつか工夫が必要だった。

CloudFormation実行ロールの制限

デフォルトだとService Catalogユーザーが発行すると、彼ら自身の権限でCloudFormationが実行される。これだと危ない。VPCは作れるのに、IAMロールは作らせたくない、みたいなガバナンスが効かないんだ。

解決策は、プロダクトごとに専用のCloudFormation実行ロールを用意して、そのロールに最小権限を付与する方法。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateVpc",
        "ec2:CreateSubnet",
        "ec2:CreateSecurityGroup",
        "ec2:CreateInternetGateway",
        "ec2:CreateRouteTable",
        "ec2:CreateRoute",
        "ec2:AssociateRouteTable",
        "ec2:ModifyVpcAttribute",
        "ec2:DescribeVpcs",
        "ec2:DescribeSubnets"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": [
        "iam:*",
        "kms:*",
        "s3:DeleteBucket"
      ],
      "Resource": "*"
    }
  ]
}

発行後の自動タグ付け

Service Catalogから発行されたStackには、CloudFormationタグが自動で付く。これを使って、「このリソースはセルフサービスで作られたやつ」ってフィルタリングできるようにした。Config Rulesで定期的に「タグなしリソース」を検出して通知する運用にしたんだ。

EventBridge × SNS で承認フロー

ある程度大きなテンプレート(RDS、複数AZ構成)の発行は、承認が入るようにしたい。Service Catalog自体には承認機能がないので、CloudFormationのStack CreateイベントをEventBridgeで拾って、SNSで承認者にメール送信する構成にした。

# Lambda function for approval notification
import json
import boto3
import os
from datetime import datetime

sns_client = boto3.client('sns')
cfn_client = boto3.client('cloudformation')

def lambda_handler(event, context):
    detail = event['detail']
    stack_name = detail['stack-name']
    request_id = detail['request-id']
    account_id = detail['account-id']
    
    # テンプレートサイズで承認要否判定
    try:
        stack_info = cfn_client.describe_stacks(StackName=stack_name)
        template_description = stack_info['Stacks'][0].get('Description', '')
        
        # "HighRisk" タグがあれば承認が必要
        tags = stack_info['Stacks'][0].get('Tags', [])
        requires_approval = any(t['Key'] == 'ApprovalRequired' and t['Value'] == 'true' for t in tags)
        
        if requires_approval:
            message = f"""
New CloudFormation Stack Approval Required

Stack Name: {stack_name}
Request ID: {request_id}
Account: {account_id}
Time: {datetime.now().isoformat()}

Please review and approve/reject in the AWS Console.
https://console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks
            """
            
            sns_client.publish(
                TopicArn=os.environ['APPROVAL_TOPIC_ARN'],
                Subject=f'CF Stack Approval Required: {stack_name}',
                Message=message
            )
    except Exception as e:
        print(f"Error: {str(e)}")
        raise
    
    return {'statusCode': 200}

実運用で困ったこと、そして解決策

困ったこと1:テンプレート更新した時、既存Stackに反映されない

これはService Catalogの仕様というか、設計思想の問題だな。新しいバージョンのプロダクト(例えばVPCテンプレートv2.0)をアップロードしても、v1.0で既に発行されてるStackには影響しない。当然だけど、運用上は「全チームのVPCをセキュリティ設定のアップデートで統一したい」とか出てくる。

解決策は、CloudFormationスタックポリシーで制御するか、EventBridgeのScheduled Ruleで定期的にテンプレート古いバージョンのStackを検出して、チーム別に「更新推奨」通知する仕組みを作ることだった。

#!/bin/bash
# 古いバージョンのStackを検出
aws cloudformation list-stacks \
  --region ap-northeast-1 \
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE \
  --query 'StackSummaries[?Tags[?Key==`CatalogProduct` && Value==`VpcFoundation`]]' \
  | jq -r '.[] | "Stack: \(.StackName) - TemplateVersion: \(.Description)"'

困ったこと2:複雑なテンプレート(RDS + Lambda + EventBridge)だと、パラメータが20個超える

パラメータが多すぎると、エンドユーザーが発行画面で「何を選んだらいい?」ってなる。Service Catalogには「デフォルト値」は指定できるけど、「このパラメータの意味は?」ってのを詳しく説明するUIが弱いんだ。

これは正直な話、テンプレートを分割するしかない。単発で「ECS Cluster作りたい」なら1つのプロダクト。その中のコンテナイメージ詳細(ECR URIとか)は、CloudFormationのDynamicsReferenceで後からSSM ParameterStoreから取得する仕組みにした。

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Image: !Sub '{{resolve:ssm:/catalog-products/ecs-cluster/${ClusterName}/image-uri}}'
          Name: app
          Memory: 512

困ったこと3:複数アカウント環境だと、StackSetの方が便利なのか?

うちは開発・ステージング・本番で3アカウント分かれてて、同じテンプレートを複数アカウントに展開したかった。最初はService Catalogで各アカウントに発行するつもりだったけど、正直めんどい。StackSetの方が「1回のセットアップで3アカウントに一括デプロイ」できて楽だった。

結論としては、Service Catalogは単一アカウント内のセルフサービス化に最適。複数アカウント横断なら、StackSetとの組み合わせか、より高度な自動化ツール(自動化フレームワーク)を検討するのが現実的だね。

AWS構成図:Service Catalogの全体像

graph TB
    subgraph Organization["Organization"]
        AdminAccount["Admin Account"]
        DevAccount["Dev Account"]
        ProdAccount["Prod Account"]
    end

    subgraph ServiceCatalog["Service Catalog Setup in Admin Account"]
        Portfolio["Portfolio: Platform Foundation<br/>Application Runtime"]
        Product1["Product: VPC + SG"]
        Product2["Product: RDS Cluster"]
        Product3["Product: ECS Cluster"]
        
        Portfolio --> Product1
        Portfolio --> Product2
        Portfolio --> Product3
    end

    subgraph TemplateStorage["Template Management"]
        S3Bucket["S3: CloudFormation Templates<br/>- vpc-foundation-v2.yaml<br/>- rds-cluster-v3.yaml<br/>- ecs-cluster-v2.yaml"]
        ChangeLog["Version History<br/>in Git"]
    end

    subgraph Distribution["Distribution & Automation"]
        StackSets["CloudFormation StackSet<br/>Replicate across accounts"]
        EventBridge["EventBridge Rules<br/>Monitor Stack Creation"]
        SNS["SNS Topics<br/>Approval Notifications"]
    end

    subgraph UserWorkflow["User Self-Service Workflow"]
        Engineer1["Engineer A<br/>Dev Team"]
        Engineer2["Engineer B<br/>Data Team"]
        Console["AWS Service Catalog Console<br/>Select Product → Configure → Launch"]
    end

    subgraph Governance["Governance & Monitoring"]
        ConfigRules["AWS Config Rules<br/>Detect untagged resources"]
        CloudTrail["CloudTrail<br/>Audit all provisioning"]
        CloudWatch["CloudWatch Dashboards<br/>Provisioning metrics"]
    end

    Portfolio --> S3Bucket
    S3Bucket --> StackSets
    StackSets --> DevAccount
    StackSets --> ProdAccount
    
    Engineer1 --> Console
    Engineer2 --> Console
    Console --> EventBridge
    EventBridge --> SNS
    
    StackSets --> ConfigRules
    StackSets --> CloudTrail
    CloudTrail --> CloudWatch
    
    ChangeLog -.->|version control| S3Bucket
    SNS -.->|approval workflow| AdminAccount

    style Organization fill:#e1f5ff
    style ServiceCatalog fill:#fff3e0
    style TemplateStorage fill:#f3e5f5
    style Distribution fill:#e8f5e9
    style UserWorkflow fill:#fce4ec
    style Governance fill:#ede7f6

実運用で見えた効果

うちが9ヶ月間で得られた変化を、実際に測定した数値で示すとこんな感じ。

xychart-beta
    x-axis [導入前, 3ヶ月目, 6ヶ月目, 9ヶ月目]
    y-axis "新環境セットアップ時間(時間)" 0 --> 10
    line [8, 5.5, 3, 1.5] title "平均セットアップ時間"
    line [0, 15, 35, 58] title "Service Catalog経由の発行数"

実際の感覚的な効果としては:

  • セットアップ時間:2日→4時間。95%の自動化率達成。営業チームが「今日中にデモ環境作りたい」って言っても対応できるようになった
  • セキュリティ監査の楽さ:「なんこのセキュリティグループ?」「なぜこのRDSはバックアップ無効?」みたいな質問が激減。テンプレートがソースオブトゥルースになった
  • テンプレート重複排除:同じVPCテンプレートが5つのプロジェクトに散在してたのが、1つに統一

次のステップ:2026年に気になっていること

Service Catalogだけだと、複雑な多段階プロビジョニング(VPC作成→その中にEKS→サービスメッシュ設定)が難しい。最近、より高度なワークフロー自動化でこれを実装するケースが増えてる。うちのチームでも、個人的には「Service CatalogからのStackSets発行をトリガーに、さらにLambda呼んでアプリケーションレイヤーの設定も自動化」みたいなのを試したいな。

あと、AIを使ったテンプレート生成も気になる。「VPCが必要な用途を説明して」→生成AIがCloudFormation YAML生成→それをService Catalogに登録、みたいなワークフローが現実的になってきた気がする。正直まだ検証中だけど、やってみる価値はあると思ってる。

まとめ

9ヶ月運用してみて、Service Catalogの真価が分かった。単なるテンプレート配布ツールじゃなくて、組織全体のインフラ標準化とセルフサービスのバランスを取れる仕掛けなんだ。

  • Service Catalogはセルフサービス化の仕掛け:テンプレート配布だけじゃなく、ガバナンスと利便性のバランスを取れる
  • テンプレート整備が本当に重要:パラメータ化、デフォルト値、Output定義に時間かけるだけで、エンドユーザーの満足度が段違い
  • 複数アカウント環境はStackSet活用:Service Catalogとの組み合わせで、スケーラブルなインフラ管理が可能
  • ガバナンスはコードで実装:ポートフォリオやプロダクト自体をTerraformで管理すると、GitOpsで追跡可能
  • 次のレベルは自動化ワークフロー:EventBridge、Lambda、Systems Manager Automationを組み合わせることで、さらに複雑なプロビジョニングも自動化できる

正直、導入して最初の2〜3ヶ月は「本当に必要か?」って思ってた。でも、テンプレート更新時の安心感、新人オンボーディングの短さ、セキュリティ監査での「なぜ?」が減ったことを考えると、やってよかった。特にチームが5人以上で、複数プロジェクト走ってる組織なら、投資する価値あると思うよ。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事