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 Foundation | VPC + Security Groups、RDS + Backup | 全チーム |
| Application Runtime | ECS Cluster、Lambda Layer、API Gateway | バックエンド・アプリ |
| Data Pipeline | S3 + Glue + Athena、EventBridge Workflow | データ・分析 |
| Development Tools | CloudWatch 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人以上で、複数プロジェクト走ってる組織なら、投資する価値あると思うよ。