こんにちは。 カミナシでソフトウェアエンジニアをやっている 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 ロールをアタッチすることで対応しました。
最後に宣伝です!カミナシでは絶賛採用中です!📣 一緒に強いチームを作っていく仲間を募集しています📣