In größeren Unternehmen gibt es eine Menge Dinge, die bereits vorhanden sind. Denken Sie an Dinge wie Bereitstellungspipelines für Ihre Anwendungen. Aber wie richten Sie diese mit den Services von AWS ein? In diesem Blogbeitrag zeige ich Ihnen, wie ich das für meine Lieblingsprojekte mache.
Ich bin ein großer Fan von CloudFormation, daher sind die Beispiele, die ich zeigen werde, CloudFormation-Snippets.
Das Git-Repository
Ich verwende AWS CodeCommit als Quellcode-Repository. Der Grund für diese Wahl ist, dass es mit AWS CodePipeline integriert ist. Wir möchten auch die Möglichkeit haben, unsere Infrastruktur zu testen. Dazu verwende ich normalerweise Feature Branches. Hier zeigt sich die erste Herausforderung. Wir brauchen nur 1 Repository und eine Pipeline für die Bereitstellung unserer Infrastruktur. Aber wir brauchen auch eine Pipeline, die unseren Feature Branch bereitstellt. In CloudFormation können Sie dies mithilfe von Bedingungen tun.
Parameters:
FeatureGitBranch:
Type: String
Default: ""
ProjectName:
Type: String
Default: ""
Conditions:
IsMainBranchPipeline: !Equals [!Ref FeatureGitBranch, ""]
IsFeatureBranchPipeline: !Not [Condition: IsMainBranchPipeline]
Durch die Angabe eines FeatureGitBranch-Parameters können wir nun einige Auswahlmöglichkeiten treffen. Zum Beispiel können wir das Repository nur in der Produktionspipeline erstellen.
Resources:
PipelineRepo:
Condition: IsMainBranchPipeline
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Ref ProjectName
RepositoryDescription: <repository-description>
Die Anforderungen an die Pipeline
Die Pipeline verwendet eine Reihe von Ressourcen. Sie können diese Ressourcen in Ihren Pipelines gemeinsam nutzen. Sie befinden sich also in einer separaten Vorlage und ich setze diese Ressourcen einmal pro Region ein, die ich verwende.
Zunächst benötigen Sie einen S3-Bucket, um die in jeder Phase erstellten Artefakte zu speichern.
ArtifactsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub codepipeline-artifacts-${AWS::AccountId}-${AWS::Region}
VersioningConfiguration:
Status: Enabled
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
Ich verwende einen vorhersehbaren Bucket-Namen, damit ich ihn in meiner Pipeline verwenden kann. Da Bucket-Namen einen global eindeutigen Namen haben müssen, füge ich auch die Konto-ID und die Region hinzu.
Sie benötigen außerdem eine IAM-Rolle. Sie werden diese Rolle in Ihrer Pipeline verwenden, um Ihre CloudFormation-Vorlage bereitzustellen.
CloudFormationServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub cloudformation-execution-role-${AWS::Region}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal: { Service: cloudformation.amazonaws.com }
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
Auch hier verwende ich einen vorhersehbaren Namen und gebe die Region an. Da Rollennamen in Ihrem Konto eindeutig sind, können Sie dies in jeder Region einsetzen. Der Einfachheit halber habe ich die verwaltete Richtlinie AdministratorAccess verwendet. Vielleicht möchten Sie dies für Ihren eigenen Bereich einschränken.
Die Pipeline
Die Pipeline selbst benötigt ebenfalls eine Rolle. Diese Rolle kann die CloudFormation-Rolle übergeben und die im vorherigen Schritt erstellten Buckets verwenden.
CodePipelineExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action: "sts:AssumeRole"
Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Policies:
- PolicyName: CodePipelineAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "iam:PassRole"
Resource: !Sub cloudformation-execution-role-${AWS::Region}
- PolicyName: CodePipelineCodeAndS3Bucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- s3:GetBucketAcl
- s3:GetBucketLocation
Effect: Allow
Resource:
- !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-west-1
- !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-central-1
- Action:
- "s3:GetObject"
- "s3:GetObjectVersion"
- "s3:PutObject"
Effect: Allow
Resource:
- !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-west-1/*
- !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${AWS::AccountId}-eu-central-1/*
- PolicyName: CodeCommitAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- codecommit:GitPull
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:UploadArchive
- codecommit:GetUploadArchiveStatus
Effect: Allow
Resource:
- !Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}
- !Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}/*
- PolicyName: CodePipelineCodeBuildAndCloudformationAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "codebuild:StartBuild"
- "codebuild:BatchGetBuilds"
Resource:
- !GetAtt CodeBuildProjectBuildAndPackage.Arn
- Effect: Allow
Action:
- "cloudformation:DescribeStacks"
Resource: !Sub "arn:${AWS::Partition}:cloudformation:eu-*:${AWS::AccountId}:stack/*"
- Effect: Allow
Action:
- "cloudformation:CreateStack"
- "cloudformation:DeleteStack"
- "cloudformation:UpdateStack"
- "cloudformation:CreateChangeSet"
- "cloudformation:ExecuteChangeSet"
- "cloudformation:DeleteChangeSet"
- "cloudformation:DescribeChangeSet"
- "cloudformation:SetStackPolicy"
- "cloudformation:SetStackPolicy"
- "cloudformation:ValidateTemplate"
Resource:
- !Sub "arn:${AWS::Partition}:cloudformation:eu-*:${AWS::AccountId}:stack/${ProjectName}-*/*"
Wie Sie in dem Schnipsel sehen können, darf die Rolle auch ein CodeBuild-Projekt starten. Und sie kann eine CloudFormation-Vorlage bereitstellen. Sie haben vielleicht bemerkt, dass ich in dieser Rolle 2 Regionen verwendet habe. Das liegt daran, dass ich in diesem speziellen Beispiel eine Pipeline habe, die 2 Regionen verwendet.
Da wir 2 Regionen verwenden, benötigen wir auch 2 Artefaktspeicher.
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStores:
- Region: eu-west-1
ArtifactStore:
Location: !Sub codepipeline-artifacts-${AWS::AccountId}-eu-west-1
Type: S3
- Region: eu-central-1
ArtifactStore:
Location: !Sub codepipeline-artifacts-${AWS::AccountId}-eu-central-1
Type: S3
RoleArn: !GetAtt CodePipelineExecutionRole.Arn
RestartExecutionOnUpdate: true
Stages:
- Name: Source
Actions:
- Name: SourceCodeRepo
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
Configuration:
PollForSourceChanges: False
RepositoryName: !Ref ProjectName
BranchName: !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, "main"]
OutputArtifacts:
- Name: SourceCodeAsZip
RunOrder: 1
Abhängig von der IsFeatureBranchPipeline-Bedingung wählen wir den richtigen Zweig für diese Pipeline aus. Abhängig von Ihrem Projekt benötigen Sie möglicherweise einen Build-Schritt. Ich verwende CodeBuild, um Unit-Tests auszuführen und alle benötigten Ressourcen zu erstellen und zu verpacken.
- Name: BuildAndPackage
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildProjectBuildAndPackage
InputArtifacts:
- Name: SourceCodeAsZip
OutputArtifacts:
- Name: BuildArtifactAsZip
Ich habe das CodeBuild-Projekt und das IAM-Rollen-Snippet für CodeBuild nicht aufgenommen. Dazu werde ich in Zukunft einen Blogbeitrag verfassen.
Kommen wir also zu dem Teil, der Spaß macht. Der nächste Schritt ist die eigentliche Bereitstellung der CloudFormation-Vorlage. Ich verwende 2 Regionen und möchte die Bereitstellung nur in diesen 2 Regionen für meine Produktionsumgebung vornehmen. Außerdem habe ich verschiedene Parameter für meine Produktionsumgebung. Auch hier verwende ich Bedingungen, um dieses Verhalten zu steuern.
- !If
- IsMainBranchPipeline
- Name: Production
Actions:
- Name: Ireland-CreateChangeSet
RunOrder: 1
Region: eu-west-1
InputArtifacts:
- Name: BuildArtifactAsZip
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-production
ChangeSetName: !Sub ${ProjectName}-production-ChangeSet
TemplatePath: BuildArtifactAsZip::packaged-template.yaml
Capabilities: CAPABILITY_NAMED_IAM
ParameterOverrides: |-
{
"EnvType": "production"
}
- Name: Frankfurt-CreateChangeSet
RunOrder: 1
Region: eu-central-1
InputArtifacts:
- Name: BuildArtifactAsZip
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-production
ChangeSetName: !Sub ${ProjectName}-production-ChangeSet
TemplatePath: BuildArtifactAsZip::packaged-template.yaml
Capabilities: CAPABILITY_NAMED_IAM
ParameterOverrides: |
{
"EnvType": "production"
}
- Name: Ireland-ExecuteChangeSet
RunOrder: 2
Region: eu-west-1
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-production
ChangeSetName: !Sub ${ProjectName}-production-ChangeSet
- Name: Frankfurt-ExecuteChangeSet
RunOrder: 2
Region: eu-central-1
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-production
ChangeSetName: !Sub ${ProjectName}-production-ChangeSet
- !Ref AWS::NoValue
In der 1 Aktion erstelle ich einen Änderungssatz. In der zweiten Aktion führe ich ihn aus. Bitte beachten Sie den hier verwendeten RoleArn. Sie können auch eine Rolle aus einem anderen Konto angeben. So können Sie ein separates Konto für verschiedene Umgebungen verwenden. Sie könnten die Pipeline zum Beispiel in einem Bereitstellungskonto hosten. Und separate Konten für Entwicklung, Test, Abnahme und Produktion haben.
Der Feature-Zweig
Wir haben jetzt also eine funktionierende Pipeline für die Produktionsumgebung. Wie sieht es mit dem Feature-Zweig aus? Hierfür verwenden wir wieder die Bedingungen.
- !If
- IsFeatureBranchPipeline
- Name: FeatureDevelopment
Actions:
- Name: Ireland-CreateChangeSet
RunOrder: 1
Region: eu-west-1
InputArtifacts:
- Name: BuildArtifactAsZip
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-${FeatureName}
ChangeSetName: !Sub ${ProjectName}-feature-ChangeSet
TemplatePath: BuildArtifactAsZip::packaged-template.yaml
Capabilities: CAPABILITY_NAMED_IAM
ParameterOverrides: |
{
"EnvType": "development"
}
- Name: Ireland-ExecuteChangeSet
RunOrder: 2
Region: eu-west-1
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role
StackName: !Sub ${ProjectName}-${FeatureName}
ChangeSetName: !Sub ${ProjectName}-feature-ChangeSet
- !Ref AWS::NoValue
Wie Sie sehen können, wird der Funktionszweig nur in der Region eu-west-1 bereitgestellt. Und er hat den Parameter EnvType auf development gesetzt.
Neue Übertragungen abholen
Wenn Sie Ihre Commits in das CodeCommit-Repository übertragen. Sie können eine Ereignisregel verwenden, um eine neue Pipeline-Ausführung auszulösen. Dazu benötigen wir eine IAM-Rolle und die Regel selbst.
CloudWatchEventRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: { Service: events.amazonaws.com }
Action: sts:AssumeRole
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action: codepipeline:StartPipelineExecution
Resource: !Sub arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
Effect: Allow
CloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
Description: Check CodeCommit Repo Changes
EventPattern:
detail-type:
- "CodeCommit Repository State Change"
source:
- aws.codecommit
resources:
- !Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${ProjectName}
detail:
event:
anything-but:
- referenceDeleted
referenceType:
- branch
referenceName:
- !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, "main"]
Targets:
- Id: codepipeline
Arn: !Sub arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}
RoleArn: !Sub ${CloudWatchEventRole.Arn}
Wir lösen bei jedem Ereignis aus, außer wenn der Zweig entfernt wird.
Fazit
Sie können die Vorlage für die Pipeline in Ihrem CodeCommit-Repository speichern. Wenn Sie einen Feature-Zweig erstellen, können Sie diese Vorlage unter Angabe des Zweignamens bereitstellen. Die Pipeline kann dann die CloudFormation-Vorlage des Projekts bereitstellen. Und mit jeder Übertragung, die Sie durchführen, wird Ihre Funktionsumgebung aktualisiert.
Mit dieser Einrichtung haben Sie alles in einem einzigen Repository. (Mit Ausnahme des gemeinsamen S3-Buckets und der IAM-Rolle.) Da sich alles in einem einzigen Repository befindet, ist es einfacher zu pflegen.
Verfasst von

Joris Conijn
Joris is the AWS Practise CTO of the Xebia Cloud service line and has been working with the AWS cloud since 2009 and focussing on building event-driven architectures. While working with the cloud from (almost) the start, he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learning new things and experimenting with them because that is the way to learn and grow.
Unsere Ideen
Weitere Blogs
Contact




