Your S3 bucket and CloudFront distribution are ready, but the bucket is empty. You need a way to get built files into it. CodePipeline automates the cycle: pull source from GitHub, build with CodeBuild, deploy to S3. This post sets up the foundation. The Source, Build, and Deploy stages each get their own post.
Artifact Bucket
The pipeline needs somewhere to store intermediate artifacts between stages. A separate AWS::S3::Bucket handles this.
PipelineArtifactBucket:
Type: 'AWS::S3::Bucket'
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: CleanupOldArtifacts
Status: Enabled
ExpirationInDays: 7
NoncurrentVersionExpiration:
NoncurrentDays: 3
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
This is not your site bucket. The site bucket holds your final deployed files. The artifact bucket holds zipped source code and build output as they move between pipeline stages.
PublicAccessBlockConfiguration keeps artifacts private. BucketEncryption sets SSE-S3 explicitly, same as the site bucket.
There is no VersioningConfiguration here because pipeline artifacts are disposable. The pipeline writes new artifacts on every run, so there is no rollback value in keeping old versions.
ExpirationInDays: 7 deletes current artifacts after 7 days. Unlike the site bucket where you only expire noncurrent versions, artifact files themselves are temporary.
NoncurrentVersionExpiration with NoncurrentDays: 3 catches any noncurrent versions if versioning is ever enabled at the bucket level. AbortIncompleteMultipartUpload with DaysAfterInitiation: 1 cleans up failed multipart uploads after 1 day. Without it, partial uploads from interrupted builds sit in the bucket forever and you pay for the storage.
Artifact Bucket Policy
The artifact bucket needs a bucket policy that enforces TLS. Without it, requests over plain HTTP would be allowed. An AWS::S3::BucketPolicy with a DenyInsecureTransport statement blocks any request where aws:SecureTransport is false.
ArtifactBucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref PipelineArtifactBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyInsecureTransport
Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt PipelineArtifactBucket.Arn
- !Sub '${PipelineArtifactBucket.Arn}/*'
Condition:
Bool:
'aws:SecureTransport': 'false'
This is the same pattern used in the site bucket policy from post 8. The Resource array covers both the bucket itself and all objects inside it, so bucket-level and object-level API calls are both required to use TLS.
Role Permissions
The IAM piece is worth understanding. CloudFormation has to create the pipeline’s own service roles (coming in the next posts), so the Git Sync role from post 3 carries iam:CreateRole and iam:PassRole. Those are scoped to role/site/*, the /site/ path the site stack puts its service roles under, with a PassedToService condition limiting where they can be handed. Unscoped, iam:CreateRole plus iam:PutRolePolicy plus iam:PassRole would let anyone who can push to the role repo mint an admin role and hand it to any service. That privilege-escalation risk is exactly why post 3 fences them by path and condition.