Skip to content

Commit

Permalink
fix: certificate provisioning via pipeline. move layers definition to…
Browse files Browse the repository at this point in the history
… the unload package. remove logging bucket.
  • Loading branch information
yhortuk committed Jan 22, 2023
1 parent 6e6364c commit 5a81ab0
Show file tree
Hide file tree
Showing 10 changed files with 602 additions and 69 deletions.
6 changes: 5 additions & 1 deletion app/Aws/Certificate.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ class Certificate
private CloudFormationClient $cloudformation;
private UnloadConfig $unload;
private Domain $domain;
private ContinuousIntegration $continuousIntegration;

public function __construct(UnloadConfig $unload, Domain $domain)
public function __construct(UnloadConfig $unload, Domain $domain, ContinuousIntegration $continuousIntegration)
{
$this->cloudformation = new CloudFormationClient(['region' => 'us-east-1', 'profile' => $unload->profile(), 'version' => 'latest',]);
$this->domain = $domain;
$this->unload = $unload;
$this->continuousIntegration = $continuousIntegration;
}

public function createStack(): ?PendingStack
Expand All @@ -34,6 +36,7 @@ public function createStack(): ?PendingStack
$this->cloudformation->describeStacks(['StackName' => $stackName])->get('Stacks');
$this->cloudformation->updateStack([
'StackName' => $stackName,
'RoleARN' => $this->continuousIntegration->getCloudformationRole(),
'EnableTerminationProtection' => true,
'TemplateBody' => Cloudformation::get("construct/certificate.yaml", compact('domains')),
'Capabilities' => ['CAPABILITY_IAM'],
Expand All @@ -47,6 +50,7 @@ public function createStack(): ?PendingStack

$this->cloudformation->createStack([
'StackName' => $stackName,
'RoleARN' => $this->continuousIntegration->getCloudformationRole(),
'EnableTerminationProtection' => true,
'TemplateBody' => Cloudformation::get("construct/certificate.yaml", compact('domains')),
'Capabilities' => ['CAPABILITY_IAM'],
Expand Down
25 changes: 18 additions & 7 deletions app/Aws/ContinuousIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
use Aws\CloudFormation\CloudFormationClient;
use Aws\CloudFormation\Exception\CloudFormationException;
use Aws\Iam\IamClient;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

class ContinuousIntegration
{
private CloudFormationClient $cloudformation;
private IamClient $iam;
private UnloadConfig $unload;
private static $memoize = null;

public function __construct(UnloadConfig $unload)
{
Expand Down Expand Up @@ -97,20 +99,20 @@ public function deleteStack(): ?PendingStack

public function getPipelineExecutionRoleArn(): string
{
$outputs = collect($this->cloudformation->describeStacks(['StackName' => $this->unload->ciStackName()])
->search('Stacks[0].Outputs'))->keyBy('OutputKey');
return $outputs->get('PipelineExecutionRole')['OutputValue'];
return $this->stackOutput()->get('PipelineExecutionRole')['OutputValue'];
}

public function getArtifactsBucketName(): string
{
$outputs = collect($this->cloudformation->describeStacks(['StackName' => $this->unload->ciStackName()])
->search('Stacks[0].Outputs'))->keyBy('OutputKey');

$bucketArn = $outputs->get('ArtifactsBucket')['OutputValue'];
$bucketArn = $this->stackOutput()->get('ArtifactsBucket')['OutputValue'];
return Str::of($bucketArn)->replace('arn:aws:s3:::', '')->toString();
}

public function getCloudformationRole(): string
{
return $this->stackOutput()->get('CloudFormationExecutionRole')['OutputValue'];
}

public function getOpenIDConnectProviderArn(OidcInterface $oidc): ?string
{
$condition = str_replace('https://', '', $oidc->url());
Expand Down Expand Up @@ -140,4 +142,13 @@ public function applicationStackExists(): bool
return false;
}
}

protected function stackOutput(): Collection
{
if(is_null(self::$memoize)) {
self::$memoize = collect($this->cloudformation->describeStacks(['StackName' => $this->unload->ciStackName()])
->search('Stacks[0].Outputs'))->keyBy('OutputKey');
}
return self::$memoize;
}
}
34 changes: 22 additions & 12 deletions app/Aws/PendingStack.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Aws;

use Aws\CloudFormation\CloudFormationClient;
use Aws\CloudFormation\Exception\CloudFormationException;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Output\ConsoleOutput;

Expand All @@ -24,25 +25,34 @@ public function wait($section = null): void
$stackEventsTable->setStyle('compact');
$stackEventsTable->render();

$stackDescribeAttempts = 0;
$stackInProgress = true;
$stackEvents = [];

while($stackInProgress) {
foreach ($this->cloudformation->describeStackEvents(['StackName' => $this->stackName])->get('StackEvents') as $event) {
if (in_array($event['EventId'], $stackEvents)) {
continue;
try {
foreach ($this->cloudformation->describeStackEvents(['StackName' => $this->stackName])->get('StackEvents') as $event) {
if (in_array($event['EventId'], $stackEvents)) {
continue;
}
$stackEvents[] = $event['EventId'];
$stackEventsTable->appendRow(
collect($event)->only(['ResourceStatus', 'ResourceType', 'LogicalResourceId'])->values()->prepend("\t")->toArray()
);
}
$stackEvents[] = $event['EventId'];
$stackEventsTable->appendRow(
collect($event)->only(['ResourceStatus', 'ResourceType', 'LogicalResourceId'])->values()->prepend("\t")->toArray()
);
}

$stackStatus = $this->cloudformation->describeStacks(['StackName' => $this->stackName])->search('Stacks[0].StackStatus');
if (in_array($stackStatus, ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE', 'ROLLBACK_COMPLETE'])) {
$stackInProgress = false;
$stackStatus = $this->cloudformation->describeStacks(['StackName' => $this->stackName])->search('Stacks[0].StackStatus');
if (in_array($stackStatus, ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE', 'ROLLBACK_COMPLETE'])) {
$stackInProgress = false;
}
$stackDescribeAttempts = 0;
} catch (CloudFormationException $e) {
if ($stackDescribeAttempts > 3) {
throw $e;
}
$stackDescribeAttempts++;
sleep($stackDescribeAttempts*5);
}

sleep(0.5);
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/Aws/SystemManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(UnloadConfig $unload)

public function initialized(): bool
{
$environmentParts = $this->ssm->describeParameters(['Path' => $this->unload->ssmEnvPath(),]);
$environmentParts = $this->ssm->getParametersByPath(['Path' => $this->unload->ssmEnvPath(),]);
return !!$environmentParts->search('Parameters');
}

Expand Down
4 changes: 2 additions & 2 deletions app/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ public static function ignoreFile(): string

public static function layersFile(): string
{
return self::tmpApp('/vendor/unload/unload-laravel/layer/php.json');
return base_path('layers/php.json');
}

public static function extensionFile(): string
{
return self::tmpApp('/vendor/unload/unload-laravel/layer/extension.json');
return base_path('layers/extension.json');
}

public static function unloadTemplatePath(?string $config, bool $relative = false): string
Expand Down
23 changes: 12 additions & 11 deletions app/Templates/SamConfigTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@
namespace App\Templates;

use App\Aws\Certificate;
use App\Aws\ContinuousIntegration;
use App\Configs\UnloadConfig;
use App\Path;
use Aws\CloudFormation\CloudFormationClient;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\File;

class SamConfigTemplate extends Template
{
private ContinuousIntegration $continuousIntegration;

public function __construct(UnloadConfig $unloadConfig, ContinuousIntegration $continuousIntegration)
{
parent::__construct($unloadConfig);
$this->continuousIntegration = $continuousIntegration;
}

public function make(): bool
{
list($assetBucket, $assumeRole) = $this->ciConfiguration();
$assetBucket = $this->continuousIntegration->getArtifactsBucketName();
$assumeRole = $this->continuousIntegration->getCloudformationRole();
$vpcParameters = $this->vpcConfiguration();
$certificate = $this->getCertificate();
$assetHash = $this->calculateAssetHash();
Expand Down Expand Up @@ -56,16 +67,6 @@ protected function vpcConfiguration(): string
return $vpcParameters;
}

protected function ciConfiguration(): array
{
$cloudformation = new CloudFormationClient(['profile' => $this->unloadConfig->profile(), 'region' => $this->unloadConfig->region(), 'version' => 'latest']);
$outputs = collect($cloudformation->describeStacks(['StackName' => $this->unloadConfig->ciStackName()])->search('Stacks[0].Outputs'))->keyBy('OutputKey');
return [
str_replace('arn:aws:s3:::', '', $outputs->get('ArtifactsBucket')['OutputValue']),
$outputs->get('CloudFormationExecutionRole')['OutputValue']
];
}

protected function calculateAssetHash(): string
{
$filesHash = [];
Expand Down
3 changes: 2 additions & 1 deletion box.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"config",
"cloudformation",
"vendor",
"resources"
"resources",
"layers"
],
"files": [
"composer.json"
Expand Down
36 changes: 2 additions & 34 deletions cloudformation/construct/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,6 @@ Resources:
Condition: MissingArtifactsBucket
DeletionPolicy: "Retain"
Properties:
LoggingConfiguration:
DestinationBucketName:
!Ref ArtifactsLoggingBucket
LogFilePrefix: "artifacts-logs"
VersioningConfiguration:
Status: Enabled
BucketEncryption:
Expand Down Expand Up @@ -175,36 +171,6 @@ Resources:
- !GetAtt CloudFormationExecutionRole.Arn
- !Ref CloudFormationExecutionRoleArn

ArtifactsLoggingBucket:
Type: AWS::S3::Bucket
Condition: MissingArtifactsBucket
DeletionPolicy: "Retain"
Properties:
AccessControl: "LogDeliveryWrite"
VersioningConfiguration:
Status: Enabled
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256

ArtifactsLoggingBucketPolicy:
Type: AWS::S3::BucketPolicy
Condition: MissingArtifactsBucket
Properties:
Bucket: !Ref ArtifactsLoggingBucket
PolicyDocument:
Statement:
- Effect: "Deny"
Action: "s3:*"
Principal: "*"
Resource:
- !Join [ '',[ !GetAtt ArtifactsLoggingBucket.Arn, '/*' ] ]
- !GetAtt ArtifactsLoggingBucket.Arn
Condition:
Bool:
aws:SecureTransport: false

PipelineExecutionRolePermissionPolicy:
Type: AWS::IAM::Policy
Condition: MissingPipelineExecutionRole
Expand All @@ -223,6 +189,8 @@ Resources:
- Effect: Allow
Action:
- "cloudformation:CreateChangeSet"
- "cloudformation:CreateStack"
- "cloudformation:UpdateStack"
- "cloudformation:DescribeChangeSet"
- "cloudformation:ExecuteChangeSet"
- "cloudformation:DeleteStack"
Expand Down
Loading

0 comments on commit 5a81ab0

Please sign in to comment.