CloudFront + S3 静的サイト(開発/本番環境)のCI/CDパイプラインをCloudFormationで構築する
はじめに
こんにちは、フクナガです。
この記事はNHN テコラス Advent Calendar 2023の25日目の記事です。
本記事では、CloudFront + S3でホストする静的サイト環境とCI/CDパイプラインの環境概要や設定とそれらを構築するためのCloudFormationテンプレートをご紹介します。
構築する環境

S3のアクセス許可について
CloudFrontからS3の接続はOAC(Origin Access Control)を利用し、S3への直接接続を禁止することで、セキュアな接続を実現します。OACについての詳細はこちらの記事をご参考にしていただければ幸いです。
【アップデート】Amazon CloudFront で Origin Access Control (OAC) が利用開始されました!
運用IAMユーザーについて
ご紹介するテンプレート内にはIAMユーザーも含まれております。こちらを利用すると、CodeCommitへのソースコードアップロードが可能になります。
また、対象IAMユーザーが直接mainブランチへプッシュやマージを行うことができないように権限を設定してあります。
開発/本番環境デプロイについて
今回の環境は開発/本番環境と用途に応じて環境を分ける構成としています。また、S3にデプロイするソースコードはCodeCommit上で管理する設定となっています。
1. ブランチ戦略
CodeCommitの「dev」ブランチの更新時に開発環境へデプロイ、「main」ブランチの更新時に本番環境へデプロイする設定を実装します。
開発作業を実施する際は、個別でブランチを作成し、「dev」ブランチへプルリクエストを作成、マージします。
「main」ブランチの更新は、必ず「dev」ブランチの内容を「main」ブランチへマージすることで実施することとします。
2. S3へのデプロイ
CodeDeployを利用し、S3へのコンテンツアップロードを実施します。
3. キャッシュクリア
CloudFrontでキャッシュを利用する場合も考慮し、CI/CDパイプライン実行時にCloudFrontのキャッシュクリアを実行する設定とします。
CodeDeployでS3にコンテンツをデプロイ後、CodeBuildで下記キャッシュクリアコマンドを実施します。
aws cloudfront create-invalidation --distribution-id $CloudFront_Distribution --paths "$CloudFront_Invalidation_Path" --region us-east-1
CloudFormationテンプレート
※こちらのテンプレートはバージニア北部リージョン(us-east-1)で実行してください
AWSTemplateFormatVersion: 2010-09-09
Description: Static contents distribution using S3 and CloudFront.
Parameters:
SiteNameProd:
Type: String
Description: domain name e.g. test-site.com
SiteNameDev:
Type: String
Description: domain name e.g. dev.test-site.com
SiteIdentifier:
Type: String
Description: Site Identifier. Don't Use [.] !! e.g. test-site-com
CloudFrontInvalidationPath:
Description: input the invalidation path of CloudFront cache clear
Type: String
Default: /*
Resources:
AssetsBucketProd:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Sub ${SiteNameProd}-s3-bucket
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
WebsiteConfiguration:
IndexDocument: index.html
AssetsBucketProdPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucketProd
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub arn:aws:s3:::${AssetsBucketProd}/*
Principal:
Service: "cloudfront.amazonaws.com"
Condition:
StringEquals:
AWS:SourceArn:
- !Join
- ""
- - !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/"
- !Ref AssetsCloudFrontDistributionProd
- Action:
- "s3:GetObject"
- "s3:DeleteObject"
- "s3:GetObjectVersion"
- "s3:DeleteObjectVersion"
- "s3:PutObject"
- "s3:ListBucket"
- "s3:ListBucketVersions"
Effect: Allow
Resource:
- !Sub arn:aws:s3:::${AssetsBucketProd}/*
- !Sub arn:aws:s3:::${AssetsBucketProd}
Principal:
AWS: !GetAtt CodeBuildServiceRole.Arn
AssetsBucketDev:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Sub ${SiteNameDev}-s3-bucket
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
WebsiteConfiguration:
IndexDocument: index.html
AssetsBucketDevPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucketDev
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub arn:aws:s3:::${AssetsBucketDev}/*
Principal:
Service: "cloudfront.amazonaws.com"
Condition:
StringEquals:
AWS:SourceArn:
- !Join
- ""
- - !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/"
- !Ref AssetsCloudFrontDistributionDev
- Action:
- "s3:GetObject"
- "s3:DeleteObject"
- "s3:GetObjectVersion"
- "s3:DeleteObjectVersion"
- "s3:PutObject"
- "s3:ListBucket"
- "s3:ListBucketVersions"
Effect: Allow
Resource:
- !Sub arn:aws:s3:::${AssetsBucketDev}/*
- !Sub arn:aws:s3:::${AssetsBucketDev}
Principal:
AWS: !GetAtt CodeBuildServiceRole.Arn
AssetsCloudFrontDistributionProd:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3
DomainName: !GetAtt AssetsBucketProd.DomainName
OriginAccessControlId: !GetAtt OACProd.Id
S3OriginConfig:
OriginAccessIdentity: ""
Enabled: true
DefaultRootObject: index.html
Comment: !Ref SiteNameProd
DefaultCacheBehavior:
AllowedMethods:
- HEAD
- GET
CachedMethods:
- HEAD
- GET
DefaultTTL: 0
MaxTTL: 0
MinTTL: 0
TargetOriginId: S3
ForwardedValues:
QueryString: false
ViewerProtocolPolicy: redirect-to-https
IPV6Enabled: false
OACProd:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Ref SiteNameProd
Name: !Ref SiteNameProd
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
AssetsCloudFrontDistributionDev:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3
DomainName: !GetAtt AssetsBucketDev.DomainName
OriginAccessControlId: !GetAtt OACDev.Id
S3OriginConfig:
OriginAccessIdentity: ""
Enabled: true
DefaultRootObject: index.html
Comment: !Ref SiteNameDev
DefaultCacheBehavior:
AllowedMethods:
- HEAD
- GET
CachedMethods:
- HEAD
- GET
DefaultTTL: 0
MaxTTL: 0
MinTTL: 0
TargetOriginId: S3
ForwardedValues:
QueryString: false
ViewerProtocolPolicy: redirect-to-https
IPV6Enabled: false
OACDev:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Ref SiteNameDev
Name: !Ref SiteNameDev
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
CodeCommit:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Sub ${SiteIdentifier}-source-repository
RepositoryDescription: CodeCommit Repository
CodeBuildCWLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${SiteIdentifier}-CodeBuild
AmazonCloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- "CodeCommit Repository State Change"
resources:
- !Join [
"",
[
"arn:aws:codecommit:",
!Ref AWS::Region,
":",
!Ref AWS::AccountId,
":",
!GetAtt CodeCommit.Name,
],
]
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- main
Targets:
- Arn: !Join
- ""
- - "arn:aws:codepipeline:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":"
- !Ref Pipeline
RoleArn: !GetAtt AmazonCloudWatchEventRole.Arn
Id: codepipeline-AppPipeline
AmazonCloudWatchEventRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- "sts:AssumeRole"
Path: /
Policies:
- PolicyName: cwe-pipeline-execution
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "codepipeline:StartPipelineExecution"
Resource: !Join
- ""
- - "arn:aws:codepipeline:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":"
- !Ref Pipeline
CodeBuildProjectCloudFrontCacheClearProd:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${SiteIdentifier}-prod-CacheClear
Artifacts:
Type: CODEPIPELINE
Source:
Type: CODEPIPELINE
BuildSpec: |
version: 0.2
phases:
build:
commands:
- aws cloudfront create-invalidation --distribution-id $CloudFront_Distribution --paths "$CloudFront_Invalidation_Path" --region us-east-1
Environment:
Type: LINUX_CONTAINER
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
ComputeType: BUILD_GENERAL1_SMALL
EnvironmentVariables:
- Name: CloudFront_Distribution
Type: PLAINTEXT
Value: !GetAtt AssetsCloudFrontDistributionProd.Id
- Name: CloudFront_Invalidation_Path
Type: PLAINTEXT
Value: !Ref CloudFrontInvalidationPath
LogsConfig:
CloudWatchLogs:
GroupName: !Sub ${SiteIdentifier}-CodeBuild
Status: ENABLED
ServiceRole: !Ref CodeBuildServiceRole
TimeoutInMinutes: 60
QueuedTimeoutInMinutes: 480
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: CodeBuildAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource: "*"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- Effect: Allow
Resource: "arn:aws:s3:::*"
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
- Effect: Allow
Resource: !Join
- ""
- - "arn:aws:codebuild:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":report-group/"
- !Ref SiteIdentifier
- "-prod-CacheClear"
Action:
- codebuild:CreateReportGroup
- codebuild:CreateReport
- codebuild:UpdateReport
- codebuild:BatchPutTestCases
- codebuild:BatchPutCodeCoverages
- Effect: Allow
Resource: "*"
Action:
- iam:*
- Effect: Allow
Resource:
- "*"
Action:
- cloudfront:CreateInvalidation
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub ${SiteIdentifier}-prod-codepipeline
RoleArn: !GetAtt CodePipelineServiceRole.Arn
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !GetAtt CodeCommit.Name
PollForSourceChanges: false
BranchName: main
RunOrder: 1
OutputArtifacts:
- Name: BuildArtifact
- Name: "Deploy"
Actions:
- Name: "Deploy"
ActionTypeId:
Category: "Deploy"
Owner: "AWS"
Version: "1"
Provider: "S3"
Configuration:
BucketName: !Ref AssetsBucketProd
Extract: "true"
RunOrder: 1
InputArtifacts:
- Name: "BuildArtifact"
- Name: CacheClear
Actions:
- Name: CacheClear
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
Configuration:
ProjectName: !Ref CodeBuildProjectCloudFrontCacheClearProd
RunOrder: 1
InputArtifacts:
- Name: BuildArtifact
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: PipelinePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource:
- !Sub arn:aws:s3:::${ArtifactBucket}/*
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
- Effect: Allow
Resource: "*"
Action:
- cloudformation:*
- codecommit:*
- codedeploy:*
- codebuild:*
- s3:*
CodeBuildCWLogGroupDev:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${SiteIdentifier}-CodeBuild-dev
AmazonCloudWatchEventRuleDev:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- "CodeCommit Repository State Change"
resources:
- !Join [
"",
[
"arn:aws:codecommit:",
!Ref AWS::Region,
":",
!Ref AWS::AccountId,
":",
!GetAtt CodeCommit.Name,
],
]
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- dev
Targets:
- Arn: !Join
- ""
- - "arn:aws:codepipeline:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":"
- !Ref PipelineDev
RoleArn: !GetAtt AmazonCloudWatchEventRoleDev.Arn
Id: codepipeline-AppPipeline-Dev
AmazonCloudWatchEventRoleDev:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- "sts:AssumeRole"
Path: /
Policies:
- PolicyName: cwe-pipeline-execution-dev
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "codepipeline:StartPipelineExecution"
Resource: !Join
- ""
- - "arn:aws:codepipeline:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":"
- !Ref PipelineDev
CodeBuildProjectCloudFrontCacheClearDev:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${SiteIdentifier}-dev-CacheClear
Artifacts:
Type: CODEPIPELINE
Source:
Type: CODEPIPELINE
BuildSpec: |
version: 0.2
phases:
build:
commands:
- aws cloudfront create-invalidation --distribution-id $CloudFront_Distribution --paths "$CloudFront_Invalidation_Path" --region us-east-1
Environment:
Type: LINUX_CONTAINER
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
ComputeType: BUILD_GENERAL1_SMALL
EnvironmentVariables:
- Name: CloudFront_Distribution
Type: PLAINTEXT
Value: !GetAtt AssetsCloudFrontDistributionDev.Id
- Name: CloudFront_Invalidation_Path
Type: PLAINTEXT
Value: !Ref CloudFrontInvalidationPath
LogsConfig:
CloudWatchLogs:
GroupName: !Sub ${SiteIdentifier}-CodeBuild-dev
Status: ENABLED
ServiceRole: !Ref CodeBuildServiceRoleDev
TimeoutInMinutes: 60
QueuedTimeoutInMinutes: 480
CodeBuildServiceRoleDev:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: CodeBuildAccessDev
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource: "*"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- Effect: Allow
Resource: "arn:aws:s3:::*"
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
- Effect: Allow
Resource: !Join
- ""
- - "arn:aws:codebuild:"
- !Ref AWS::Region
- ":"
- !Ref AWS::AccountId
- ":report-group/"
- !Ref SiteIdentifier
- "-dev-CacheClear"
Action:
- codebuild:CreateReportGroup
- codebuild:CreateReport
- codebuild:UpdateReport
- codebuild:BatchPutTestCases
- codebuild:BatchPutCodeCoverages
- Effect: Allow
Resource: "*"
Action:
- iam:*
- Effect: Allow
Resource:
- "*"
Action:
- cloudfront:CreateInvalidation
PipelineDev:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub ${SiteIdentifier}-dev-codepipeline
RoleArn: !GetAtt CodePipelineServiceRoleDev.Arn
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !GetAtt CodeCommit.Name
PollForSourceChanges: false
BranchName: dev
RunOrder: 1
OutputArtifacts:
- Name: BuildArtifact
- Name: "Deploy"
Actions:
- Name: "Deploy"
ActionTypeId:
Category: "Deploy"
Owner: "AWS"
Version: "1"
Provider: "S3"
Configuration:
BucketName: !Ref AssetsBucketDev
Extract: "true"
RunOrder: 1
InputArtifacts:
- Name: "BuildArtifact"
- Name: CacheClear
Actions:
- Name: CacheClear
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
Configuration:
ProjectName: !Ref CodeBuildProjectCloudFrontCacheClearDev
RunOrder: 1
InputArtifacts:
- Name: BuildArtifact
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
CodePipelineServiceRoleDev:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: PipelinePolicyDev
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource:
- !Sub arn:aws:s3:::${ArtifactBucket}/*
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
- Effect: Allow
Resource: "*"
Action:
- cloudformation:*
- codecommit:*
- codedeploy:*
- codebuild:*
- s3:*
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${SiteIdentifier}-artifact-bucket
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
IAMUserForGit:
Type: AWS::IAM::User
Properties:
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/ReadOnlyAccess
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action:
- codecommit:GitPush
- codecommit:DeleteBranch
- codecommit:PutFile
- codecommit:CreateCommit
- codecommit:MergeBranchesByFastForward
- codecommit:MergeBranchesBySquash
- codecommit:MergeBranchesByThreeWay
- codecommit:MergePullRequestByFastForward
- codecommit:MergePullRequestBySquash
Resource: "*"
Condition:
StringEqualsIfExists:
codecommit:References:
- refs/heads/main
"Null":
codecommit:References:
- false
PolicyName: DenyCommit
UserName: !Sub ${SiteIdentifier}-Developer
テンプレートの利用方法
バージニア北部リージョン(us-east-1)でCloudFormationコンソールから上記テンプレートをアップロードし、下記パラメータを入力し、実行することで構築が完了します。
- SiteNameProd
構築対象の本番サイトの名前を入力します。本番用S3バケット、CloudFrontのCommentなどで利用されます。
主に識別子としての役割になるので、S3バケットの命名規則に準じていれば任意の値で問題ありません。 -
SiteNameDev
構築対象の開発サイトの名前を入力します。開発用S3バケット、CloudFrontのCommentなどで利用されます。
主に識別子としての役割になるので、S3バケットの命名規則に準じていれば任意の値で問題ありません。 -
SiteIdentifier
CI/CD関連リソースの命名に利用される値です。他リソースや名前との兼ね合いからアンダースコアではなくハイフンを使うことを推奨します。 -
CloudFrontInvalidationPath
デフォルトのままで実行します。
稼働確認
実運用とCI/CDパイプラインが動作する様子を簡単にデモします。
事前準備
事前にこういったサイトをCI/CDパイプラインを使って初回デプロイをしておきます。

【初回デプロイ手順】
1. 作成したCodeCommitでdev、mainブランチを作成
2. 任意のhtmlを「index.html」という名前でアップロード
3. 自動デプロイが開始、1~2分以内に反映される
【サイトへの接続】
対象のCloudFront「ディストリビューションドメイン名」をURLとして入力すると、構築したサイトへ接続できます。
実行準備が整ったので、実際に一部を変更し、CI/CDパイプラインを実行します。
稼働確認
1. ソースの変更
今回は、元々「Technology Event 2023」となっていた部分を「Techblog Test Event 2023」と変更します。
CodeCommitからローカルに取得したソースを変更します。
【変更前】

【変更後】

ソース変更後に、「20231215-title-change」という名前で新たなブランチを作成しました。

2. devブランチへ変更をマージ
新規作成した「20231215-title-change」から「dev」ブランチへプルリクエストを作成し、マージします。


3. パイプラインの実行が開始される

4. パイプラインが完了後、開発環境サイトへアクセスする

開発環境へは、変更が適応されていることが確認できました!

「main」ブランチは変更していないため、本番環境サイトは変更前の状態でした!

まとめ
本記事では、CloudFront + S3でホストする静的サイト環境とCI/CDパイプラインの環境概要や設定とそれらを構築するためのCloudFormationテンプレートをご紹介しました。誰がどういった変更を加えた、という情報を素早く把握できるうえ、セキュアなデプロイを実現できるため、開発/本番環境の構築やCI/CDの構築は非常に大切です。今回ご紹介したテンプレートを活用して、CI/CDパイプライン構築のハードルが少しでも下がればとてもうれしいです!!
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitter2025 Japan AWS Ambassadors / Google Cloud Partner Top Engineer 2025 / 2024 Japan AWS Top Engineers 選出されました! 生成 AI 多めで発信していますが、CI/CDやIaCへの関心も高いです。休日はベースを弾いてます。
Recommends
こちらもおすすめ
-
AWS CodePipelineのエラーを検知し、Backlog自動起票する手順
2023.12.20
-
AWS re:Invent 2023に行ってきました!
2023.12.9
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28

AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16
