
こんにちは。 カミナシでソフトウェアエンジニアをやっている Taku です。 先日、社内で AWS の Workshop を開催してみたところ良い反応をいただいたのでその共有となります。
Workshop 開催の目的
今回 Workshop を開催した主な目的はAWS の自己学習を推進するためです。
カミナシには学習・実験・検証を目的とした「AWS アカウント(検証用個人 AWS アカウント)」を発行して利用できる制度があります。
もっとこの良い制度を活用していきたいという思いと、特に新しく入社した人にはあまり知られていない状態をカイゼンしようと思い、 Workshop を開催することで気軽に AWS を触っていただけるようにしたいと考えました。
Workshop でやったこと
Workshop の題材としては、昨年末に参加した AWS re:Invent で体験した以下を利用することとしました。
Serverless Security Workshop
catalog.us-east-1.prod.workshops.aws
このワークショップでは、AWS Lambda、Amazon API Gateway、RDS Aurora で構築されたサーバーレス アプリケーションを保護するテクニックを学びます。次の 5 つのドメインでサーバーレス アプリケーションのセキュリティを向上させるために活用できる AWS のサービスと機能について説明します。
1. ID とアクセスの管理 2. コード 3. データ 4. インフラストラクチャー 5. ロギングとモニタリング
こちらを採用した理由としては、カミナシは組織としてセキュリティの意識を高めていこうとしているため、AWS におけるセキュリティの対応方法を学ぶ良い機会になると考えたためです。 ぜひ以下も参考にご覧ください。
kaminashi-developer.hatenablog.jp
AWS re:Invent に参加した際の様子は以下ブログに記載しておりますので、よろしければご覧いただけると嬉しいです。
kaminashi-developer.hatenablog.jp kaminashi-developer.hatenablog.jp
re:Invent でも様々な Workshop が開催されてましたが、他にも様々な Workshop や ハンズオンが公開されているため、興味があるサービスがあれば探してやってみることをオススメします。
事前準備
事前準備として実施したことは以下の2つです。
- 検証用個人 AWS アカウントの申請依頼
- Workshop 用の環境構築手順の作成
1. 検証用個人 AWS アカウントの申請依頼
当日の Workshop で利用するために、社内 Slack にて制度の周知と申請の依頼を行いました。

カミナシでは検証用個人 AWS アカウントの作成をGithub の issue で依頼し、その申請を元に AWS Control Tower を利用してアカウントを作成しています。

これにて今度 AWS の Workshop をやるよ〜という周知と、これを機会に社内の制度をもっと使ってもらえるようにしました。
2. Workshop 用の環境構築手順の作成
今回 AWS の workshop studio にて公開されている Workshop を利用しましたが、AWS が主催するイベントではないため環境は自前で用意する必要がありました。 今回の Workshop では環境構築用の CloudFormation のテンプレートも公開されていたのですが、情報が古くなっているのかそのままでの利用は出来ませんでした。
CloudFormation のテンプレート以外にも、Workshop で利用している Cloud9 の設定変更が必要などいくつか修正する必要がありましたので環境構築の部分は新たに手順書を作り直しました。

※公開されている Workshop から修正が必要だった点について知りたい方は、最後のおまけをご覧ください。
この準備している際に、有識者の協力を得ながら利用するサービスの特性を学ぶことが出来て個人的に勉強になりました。
当日
オフラインでエンジニアメンバー全員が集まるタイミングで、2時間の時間を貰い Workshop を開催しました。 Workshop の流れとしては、AWS に慣れていない方もいたため、以下のように環境構築まで(約1時間)は前で画面を見せながら一緒に実施し、完了した人から 3 の Workshop に取り組んでいただきました。
- Workshop の目的の共有
- 環境構築
- 各自 Workshop の実施
- 作成したリソースの削除
事前に環境構築の手順を用意していたことから環境構築は比較的スムーズに進めていただくことができ、開催者の私は一部エラーが発生した人のサポートに回ることができました。
今回は AWS を気軽に触ってもらえるようになることが主目的でしたので、この時間で全ての Workshop の内容を終わらせるようにはしませんでした。 ※もともと全部実施する場合3時間程度を要する Workshop で、複数あるカリキュラムから興味のある分野を自由に実施できる内容でした。
そのため後日安心して各自好きなタイミングで取り組んでいただけるように、最後にはリソースの削除の手順を案内するようにしました。
振り返り
実施後アンケートを取り、フィードバックを貰いましたので、私の所感を踏まえて振り返りました。
よかった点
- 「検証用個人 AWS アカウント」という社内の制度を共有し、実際に使ってもらうことができた
- Workshop 後も、新規に技術検証が必要なシーンで引き続き利用してくれている
- 事前に環境構築手順を検証・用意しておいたので当日スムーズに進めることができた
- リソースの削除まで Workshop に含めておいた点
- AWS を触る障壁として、消し忘れて大量に課金されたらどうしようというのがある様子でした
メンバーからのフィードバック
- 普段 AWS は全く触らないのでこういった機会があり、ありがたかったです。
- 知らない技術、話題には上がるが普段は触らない、触ったことのない技術に触れる機会となって良かったです!
- はまりポイントを先に踏んで修正してくれていたのがとても良かったです!
- リソースの削除まで含まれていたことで、すごく安心感を持って参加することができました。
次の開催に向けて改善できそうな点
Workshop 開催の主な目的である AWS の自己学習を推進は達成できましたが、メンバーからのフィードバックを踏まえ次回より良い Workshop 開催するために以下のようなものができたらと思いました。
- AWS の環境構築は事前にやって貰っておく
- 事前に手順を検証して資料を作って貰っておけばあまり問題なく進めることができるはず
- Workshop 環境で利用するサービスやコマンドの説明も行う
- 環境構築に時間を要したため、初心者の方にはサービスの内容の説明をちゃんとできずただコマンドを打つだけの時間があった。
- 補足として、サービスやコマンドの説明も構築手順に盛り込んでおきたい。
- 一人でもくもく作業をさせるのではなく、ペアワークをしてもらう
- AWS 初心者と熟練者を組み合わせると知識の共有ができて良さそう。
メンバーからのフィードバック
- 環境構築に時間が掛かって Module-1 が完了できなかったので、環境構築の時間をスキップできたらなお良かったかなと思いました!
- AWS 初心者もいるので、「何をやっているのか?」の説明が都度あっても良かったかも〜と思いました
- ペアワークあると良かったかも知れません。隣同士に座っていたので会話していたのですが、もくもくするよりは良いかも?
総合的に Workshop を実施することで主催者・参加者ともに AWS について学ぶ良い機会になりましたので、また機会があれば実施したいと考えております。
おまけ(環境構築にあたり修正した部分)
CloudFormation のテンプレート修正
エラーになる箇所を以下のとおり修正
修正箇所
- PublicSubnet2 が PublicSubnet3になっている箇所があったため修正
- Aurora の Engine 指定法が古かったため修正
AWSTemplateFormatVersion: '2010-09-09'
Description: Initial resource setup for serverless security workshop
Parameters:
DbPassword:
Type: String
NoEcho: true
Resources:
PubPrivateVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: Secure-Serverless
PublicSubnet1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref PubPrivateVPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: pub-subnet-1-Secure-Serverless
PublicSubnet2:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref PubPrivateVPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: pub-subnet-2-Secure-Serverless
PrivateSubnet1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref PubPrivateVPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.3.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: priv-subnet-1-Secure-Serverless
PrivateSubnet2:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref PubPrivateVPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: 10.0.4.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: priv-subnet-2-Secure-Serverless
InternetGateway:
Type: 'AWS::EC2::InternetGateway'
GatewayToInternet:
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref PubPrivateVPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref PubPrivateVPC
PublicRoute:
Type: 'AWS::EC2::Route'
DependsOn: GatewayToInternet
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
NatGateway:
Type: 'AWS::EC2::NatGateway'
DependsOn: NatPublicIP
Properties:
AllocationId: !GetAtt NatPublicIP.AllocationId
SubnetId: !Ref PublicSubnet1
NatPublicIP:
Type: 'AWS::EC2::EIP'
DependsOn: PubPrivateVPC
Properties:
Domain: vpc
PrivateRouteTable:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref PubPrivateVPC
PrivateRoute:
Type: 'AWS::EC2::Route'
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnet1RouteTableAssociation:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2RouteTableAssociation:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
Cloud9Environment:
Type: AWS::Cloud9::EnvironmentEC2
Properties:
Description: Use Cloud 9 as the default environment to launch your operations.
InstanceType: t2.micro
Name: Secure-Serverless-Cloud9
SubnetId: !Ref PublicSubnet1
DeploymentsS3Bucket:
Type: AWS::S3::Bucket
AuroraSubnetGroup:
Type: 'AWS::RDS::DBSubnetGroup'
Properties:
DBSubnetGroupDescription: Subnet for Serverless Aurora
DBSubnetGroupName: secure-serverless-aurora
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
AuroraSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Serverless Aurora Access trhough the VPC
VpcId:
Ref: PubPrivateVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: 10.0.0.0/16
# should we start with a broad SG and narrow it down as part of workshop?
# move this to the sam template instead?
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: SecurityGroup for lambda function
VpcId:
Ref: PubPrivateVPC
SecurityGroupEgress:
- Description: Access to Aurora MYSQL
FromPort: 3306
IpProtocol: tcp
DestinationSecurityGroupId: !Ref AuroraSecurityGroup
ToPort: 3306
- Description: Access to Secrets Manager
FromPort: 80
IpProtocol: tcp
CidrIp: 0.0.0.0/0
ToPort: 80
- Description: Access to Secrets Manager SSL
FromPort: 443
IpProtocol: tcp
CidrIp: 0.0.0.0/0
ToPort: 443
- Description: Access to Secrets Manager SSL
FromPort: 53
IpProtocol: udp
CidrIp: 0.0.0.0/0
ToPort: 53
AuroraDBInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.t2.small
Engine: aurora-mysql
DBClusterIdentifier: !Ref AuroraDBCluster
AuroraDBCluster:
Type: AWS::RDS::DBCluster
DependsOn: AuroraSubnetGroup
DeletionPolicy: Delete
Properties:
MasterUsername: admin
MasterUserPassword: !Ref DbPassword
Engine: aurora-mysql
DBSubnetGroupName: !Ref AuroraSubnetGroup
VpcSecurityGroupIds:
- !Ref AuroraSecurityGroup
Outputs:
AuroraEndpoint:
Description: Aurora endpoint for aurora database
Value: !GetAtt AuroraDBCluster.Endpoint.Address
DeploymentS3Bucket:
Description: S3 Bucket to place your SAM deployments
Value: !Ref DeploymentsS3Bucket
LambdaSecurityGroup:
Description: SecurityGroup for lambda function
Value: !Ref LambdaSecurityGroup
Export:
Name: !Sub ${AWS::StackName}-LambdaSecurityGroup
PublicSubnet1:
Description: PublicSubnet1
Value: !Ref PublicSubnet1
Export:
Name: !Sub ${AWS::StackName}-PublicSubnet1
PublicSubnet2:
Description: PublicSubnet2
Value: !Ref PublicSubnet2
Export:
Name: !Sub ${AWS::StackName}-PublicSubnet2
PrivateSubnet1:
Description: PrivateSubnet1
Value: !Ref PrivateSubnet1
Export:
Name: !Sub ${AWS::StackName}-PrivateSubnet1
PrivateSubnet2:
Description: PrivateSubnet2
Value: !Ref PrivateSubnet2
Export:
Name: !Sub ${AWS::StackName}-PrivateSubnet2
Cloud9 の設定変更
ワークショップの手順には含まれていなかったのですが、デフォルトの AWS managed temporary credentials(Cloud9 サービスが用意した仮の AWS クレデンシャル)では IAM に関する操作ができなかったため、これをオフにした上でCloud9 の EC2 インスタンスに独自に作成した Administrator 権限を持つ IAM ロールをアタッチすることで対応しました。

最後に宣伝です!カミナシでは絶賛採用中です!📣 一緒に強いチームを作っていく仲間を募集しています📣