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;
 }