public function testSensitive() { $merged_params = $this->cfn_params->merge($this->stack_params); $all = $merged_params->sensitive()->toArray(); $this->assertArrayHasKey('cloud_credentials', $all); $this->assertArrayNotHasKey('StackName', $all); $this->assertArrayNotHasKey('Capabilities', $all); // Assert immutability of the original collection $this->assertArrayNotHasKey('cloud_credentials', $this->cfn_params->toArray()); }
/** * @param Stack $stack * @param $action * @return Stack * @throws \Aws\CloudFormation\Exception\CloudFormationException * @throws \Exception */ private function manageStack(Stack $stack, $action) { $cfn_params = new ParametersCollection(['StackName' => $stack->get('name'), 'DisableRollback' => true, 'Capabilities' => ['CAPABILITY_IAM']]); switch ($action) { case 'create': case 'update': $stack_params = $this->getParams($stack, $with_outputs = true); $stack_params->forget('cloud_credentials'); $template = $this->generateTemplate($stack, $stack_params); $bucket = $stack_params->get(Stack::PARAM_TEMPLATE_BUCKET)->value; $s3_template = $this->publishTemplate($stack, $bucket, $template); $cfn_params->put('TemplateURL', $s3_template['url']); $params = $cfn_params->merge($stack_params); $params->forget('components'); $defined_params = TemplateEngine::getParams($stack->template); $defined_params_keys = ParametersCollection::make($defined_params)->parametersKeys(); $sensitive = $params->onlyIds($defined_params_keys)->sensitive()->flat()->studly()->decrypted()->toCollection(); if ($sensitive_aws = $sensitive->toAws()) { $cfn_params->put('Parameters', $sensitive_aws); } // TODO protect credentials from logging / output try { $action == 'create' ? $this->cfn($stack)->createStack($cfn_params->toArray()) : $this->cfn($stack)->updateStack($cfn_params->toArray()); } catch (CloudFormationException $e) { if ($e->getMessage() != 'No updates are to be performed.') { throw $e; } } finally { $this->destroyTemplate($stack, $bucket, $s3_template['key']); } break; case 'describe': Log::debug(__METHOD__); if ($stack->provisioned) { $components = ComponentCollection::make($stack->components->all()); $cache_key = 'CloudFormer-DescribeStack-' . $stack->name; if (($cf_response = Cache::get($cache_key)) === null) { Log::debug('No cache for ' . $cache_key); $cf_response = $this->cfn($stack)->describeStacks($cfn_params->toArray()); Cache::add($cache_key, $cf_response, self::CACHE_LIFETIME); } $cf_response = head($cf_response->get('Stacks')); $stack->status = $this->translateAwsStatus($cf_response['StackStatus']); $stack->outputs = Collection::make([]); $distributedOutputs = $components->distributeResources(data_get($cf_response, 'Outputs', []), 'OutputKey'); $distributedOutputs->map(function (Collection $componentOutputs, $componentName) use($components, $stack) { if (in_array($componentName, ['_stack', '_ungrouped'])) { $stack->outputs = $stack->outputs->merge($componentOutputs); } else { $components[$componentName]->outputs = $componentOutputs; } }); $cache_key = 'CloudFormer-ListStackResources-' . $stack->name; if (($cf_stack_resources = Cache::get($cache_key)) === null) { Log::debug('No cache for ' . $cache_key); $cf_stack_resources = $this->cfn($stack)->listStackResources($cfn_params->toArray())['StackResourceSummaries']; Cache::add($cache_key, $cf_stack_resources, self::CACHE_LIFETIME); } $components->distributeResources($cf_stack_resources)->map(function (Collection $resources, $componentName) use($components, $stack) { $status = $resources->reduce(function ($carry, $item) { $itemStatus = $this->translateAwsStatus($item['ResourceStatus']); if ($carry === null) { return $itemStatus; } return $this->weighStatus($itemStatus) > $this->weighStatus($carry) ? $itemStatus : $carry; }); if (!in_array($componentName, ['_stack', '_ungrouped']) && is_object($status)) { $components[$componentName]->status = $status; } }); } break; case 'delete': $this->cfn($stack)->deleteStack($cfn_params->toArray()); break; } return $stack; }