File Coverage

blib/lib/Cfn.pm
Criterion Covered Total %
statement 312 398 78.3
branch 91 178 51.1
condition 32 60 53.3
subroutine 77 93 82.8
pod 18 50 36.0
total 530 779 68.0


line stmt bran cond sub pod time code
1             package Cfn::TypeLibrary {
2 22     22   2657309 use Moose::Util::TypeConstraints;
  22         4903821  
  22         207  
3              
4             sub try_function {
5 7786     7786   14129 my $arg = shift;
6 7786         28487 my @keys = keys %$arg;
7 7786         13811 my $first_key = $keys[0];
8 7786 100 100     55351 if (@keys == 1 and (substr($first_key,0,4) eq 'Fn::' or $first_key eq 'Ref' or $first_key eq 'Condition')){
      100        
9 4941 100       18210 if ($first_key eq 'Fn::GetAtt') {
    100          
    100          
10 397         3382 return Cfn::Value::Function::GetAtt->new(Function => $first_key, Value => $arg->{ $first_key });
11             } elsif ($keys[0] eq 'Ref'){
12 3530         11036 my $psdparam = Moose::Util::TypeConstraints::find_type_constraint('Cfn::PseudoParameterValue');
13 3530         332115 my $value = $arg->{ $first_key };
14 3530 100       13010 my $class = $psdparam->check($value) ?
15             'Cfn::Value::Function::PseudoParameter' :
16             'Cfn::Value::Function::Ref';
17            
18 3530         252687 return $class->new( Function => $first_key, Value => $value);
19             } elsif ($keys[0] eq 'Condition'){
20 7         66 return Cfn::Value::Function::Condition->new( Function => $first_key, Value => $arg->{ $first_key });
21             } else {
22 1007         5911 return Cfn::Value::Function->new(Function => $first_key, Value => $arg->{ $first_key });
23             }
24             } else {
25 2845         11384 return undef;
26             }
27             }
28            
29             coerce 'Cfn::Resource::UpdatePolicy',
30             from 'HashRef',
31             via { Cfn::Resource::UpdatePolicy->new( %$_ ) };
32              
33             coerce 'Cfn::Resource::UpdatePolicy::AutoScalingReplacingUpdate',
34             from 'HashRef',
35             via { Cfn::Resource::UpdatePolicy::AutoScalingReplacingUpdate->new( %$_ ) };
36              
37             coerce 'Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate',
38             from 'HashRef',
39             via { Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate->new( %$_ ) };
40              
41             coerce 'Cfn::Resource::UpdatePolicy::AutoScalingScheduledAction',
42             from 'HashRef',
43             via { Cfn::Resource::UpdatePolicy::AutoScalingScheduledAction->new( %$_ ) };
44              
45             subtype 'Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate::SuspendProcesses',
46             as 'Cfn::Value::Array',
47             where {
48             my $array = $_->Value;
49              
50             my $valid = { Launch => 1, Terminate => 1, HealthCheck => 1, ReplaceUnhealthy => 1,
51             AZRebalance => 1, AlarmNotification =>1, ScheduledActions => 1,
52             AddToLoadBalancer => 1 };
53              
54             my @val = grep { $valid->{ $_->Value } } @$array;
55             # The array is valid if all of it's values are found in $valid
56             return @val == @$array;
57             },
58             message { 'This type only supports the following values in the array: "Launch, Terminate, HealthCheck, ReplaceUnhealthy, AZRebalance, AlarmNotification, ScheduledActions, AddToLoadBalancer"' };
59              
60             coerce 'Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate::SuspendProcesses',
61             from 'HashRef', via (\&coerce_hash),
62             from 'ArrayRef', via (\&coerce_array);
63              
64             subtype 'Cfn::Resource::DeletionPolicy',
65             as 'Str',
66             where { $_ eq 'Delete' or $_ eq 'Retain' or $_ eq 'Snapshot' },
67             message { "$_ is an invalid DeletionPolicy" };
68              
69             subtype 'Cfn::Resource::UpdateReplacePolicy',
70             as 'Str',
71             where { $_ eq 'Delete' or $_ eq 'Retain' or $_ eq 'Snapshot' },
72             message { "$_ is an invalid UpdateReplacePolicy" };
73              
74             subtype 'Cfn::Value::ArrayOfPrimitives',
75             as 'Cfn::Value::Array',
76             where { @{ $_[0]->Value } == grep { $_->isa('Cfn::Value::Primitive') } @{ $_[0]->Value } },
77             message { 'This type only supports Primitives' };
78              
79             sub coerce_array {
80             Cfn::Value::Array->new(Value => [
81 2710     2710   1898350 map { Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($_) } @$_
  7164         1894957  
82             ])
83             }
84              
85             sub coerce_hash {
86 3683     3683   1698800 my $arg = $_;
87 3683         9732 my $function = try_function($arg);
88 3683 100       2842410 return $function if (defined $function);
89 1228         3045 my @keys = keys %$arg;
90             return Cfn::Value::Hash->new(Value => {
91 1228         2689 map { $_ => Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($arg->{$_}) } @keys
  2769         1166057  
92             });
93             }
94              
95             sub coerce_hashref_to_function {
96 2486     2486   2411590 my $val = $_;
97 2486         7839 my $function = try_function($val);
98 2486 50       2617923 return $function if (defined $function);
99 0         0 die "Is not a function";
100             }
101              
102             coerce 'Cfn::Value',
103             from 'Value', via { Cfn::Value::Primitive->new( Value => $_ ) },
104             from 'HashRef', via (\&coerce_hash),
105             from 'ArrayRef', via (\&coerce_array);
106              
107             subtype 'Cfn::Value::Boolean', as 'Cfn::Value';
108             subtype 'Cfn::Value::Integer', as 'Cfn::Value';
109             subtype 'Cfn::Value::Long', as 'Cfn::Value';
110             subtype 'Cfn::Value::String', as 'Cfn::Value';
111             subtype 'Cfn::Value::Double', as 'Cfn::Value';
112             subtype 'Cfn::Value::Timestamp', as 'Cfn::Value';
113             subtype 'Cfn::Value::Json', as 'Cfn::Value';
114              
115             coerce 'Cfn::Value::Boolean',
116             from 'Int', via {
117             my $val = $_;
118              
119             if ($val == 1) {
120             Cfn::Boolean->new( Value => 1, stringy => 0 );
121             } elsif($val == 0) {
122             Cfn::Boolean->new( Value => 0, stringy => 0 );
123             } else {
124             die "Cannot convert $val to an boolean value";
125             }
126             },
127             from 'Str', via {
128             my $val = $_;
129             if (lc($val) eq 'false') {
130             Cfn::Boolean->new( Value => 0, stringy => 1 );
131             } elsif (lc($val) eq 'true') {
132             Cfn::Boolean->new( Value => 1, stringy => 1 );
133             } else {
134             die "Cannot convert string $val to a boolean value";
135             }
136             },
137             from 'Object', via {
138             my $val = $_;
139              
140             die "Cannot coerce a boolean from a non JSON::PP::Boolean" if (not $val->isa('JSON::PP::Boolean'));
141             if ($val == 1) {
142             Cfn::Boolean->new( Value => 1, stringy => 0 );
143             } elsif($val == 0) {
144             Cfn::Boolean->new( Value => 0, stringy => 0 );
145             } else {
146             die "Cannot convert $val to an boolean value";
147             }
148             },
149             from 'HashRef', via \&coerce_hashref_to_function;
150              
151             coerce 'Cfn::Value::Integer',
152             from 'Int', via { Cfn::Integer->new( Value => $_ ) },
153             from 'HashRef', via \&coerce_hashref_to_function;
154              
155             coerce 'Cfn::Value::Long',
156             from 'Num', via { Cfn::Long->new( Value => $_ ) },
157             from 'HashRef', via \&coerce_hashref_to_function;
158              
159             coerce 'Cfn::Value::String',
160             from 'Str', via { Cfn::String->new( Value => $_ ) },
161             from 'HashRef', via \&coerce_hashref_to_function;
162              
163             coerce 'Cfn::Value::Double',
164             from 'Num', via { Cfn::Double->new( Value => $_ ) },
165             from 'HashRef', via \&coerce_hashref_to_function;
166              
167             coerce 'Cfn::Value::Timestamp',
168             from 'Num', via { Cfn::Timestamp->new( Value => $_ ) },
169             from 'HashRef', via \&coerce_hashref_to_function;
170              
171             subtype 'Cfn::Value::Json',
172             as 'Cfn::Value::Hash';
173              
174             coerce 'Cfn::Value::Array',
175             from 'HashRef', via (\&coerce_hash),
176             from 'ArrayRef', via (\&coerce_array);
177              
178             coerce 'Cfn::Value::ArrayOfPrimitives',
179             from 'HashRef', via (\&coerce_hash),
180             from 'ArrayRef', via (\&coerce_array);
181              
182             coerce 'Cfn::Value::Hash',
183             from 'HashRef', via (\&coerce_hash);
184              
185             subtype 'Cfn::Transform',
186             as 'ArrayRef[Str]';
187              
188             coerce 'Cfn::Transform',
189             from 'ArrayRef', via {
190             return $_;
191             };
192             coerce 'Cfn::Transform',
193             from 'Value', via {
194             return [ $_ ];
195             };
196              
197             subtype 'Cfn::MappingHash',
198             as 'HashRef[Cfn::Mapping]';
199              
200             my $cfn_mapping_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Mapping');
201              
202             coerce 'Cfn::MappingHash',
203             from 'HashRef', via {
204             my $original = $_;
205             return { map { ($_ => $cfn_mapping_constraint->coerce($original->{ $_ }) ) } keys %$original };
206             };
207              
208             coerce 'Cfn::Mapping',
209             from 'HashRef', via {
210             return Cfn::Mapping->new(Map => $_);
211             };
212              
213             subtype 'Cfn::OutputHash',
214             as 'HashRef[Cfn::Output]';
215              
216             my $cfn_output_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Output');
217             coerce 'Cfn::OutputHash',
218             from 'HashRef', via {
219             my $original = $_;
220             return { map { ($_ => $cfn_output_constraint->coerce($original->{ $_ }) ) } keys %$original };
221             };
222              
223             coerce 'Cfn::Output',
224             from 'HashRef', via {
225             return Cfn::Output->new(%$_);
226             };
227              
228             subtype 'Cfn::ConditionHash',
229             as 'HashRef[Cfn::Value]';
230              
231             my $cfn_value_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value');
232             coerce 'Cfn::ConditionHash',
233             from 'HashRef', via {
234             my $original = $_;
235             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
236             };
237              
238             subtype 'Cfn::ParameterHash',
239             as 'HashRef[Cfn::Parameter]';
240              
241             my $cfn_parameter_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Parameter');
242             coerce 'Cfn::ParameterHash',
243             from 'HashRef', via {
244             my $original = $_;
245             return { map { ($_ => $cfn_parameter_constraint->coerce($original->{ $_ }) ) } keys %$original };
246             };
247              
248             coerce 'Cfn::Parameter',
249             from 'HashRef', via {
250             return Cfn::Parameter->new(%$_);
251             };
252              
253             subtype 'Cfn::ResourceHash',
254             as 'HashRef[Cfn::Resource]';
255              
256             my $cfn_resource_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource');
257             coerce 'Cfn::ResourceHash',
258             from 'HashRef', via {
259             my $original = $_;
260             return { map { ($_ => $cfn_resource_constraint->coerce($original->{ $_ }) ) } keys %$original };
261             };
262              
263             coerce 'Cfn::Resource',
264             from 'HashRef', via {
265             my $type = $_->{Type};
266             die "Can't coerce HashRef into a Cfn::Resource if it doesn't have a Type key" if (not defined $type);
267             my $class_type = ($type =~ m/^Custom\:\:/) ? "AWS::CloudFormation::CustomResource" : $type;
268              
269             Cfn->load_resource_module($class_type);
270             return "Cfn::Resource::$class_type"->new(
271             %$_
272             );
273             };
274              
275             subtype 'Cfn::MetadataHash',
276             as 'HashRef[Cfn::Value]';
277              
278             coerce 'Cfn::MetadataHash',
279             from 'HashRef', via {
280             my $original = $_;
281             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
282             };
283              
284             coerce 'Cfn::Value::Json',
285             from 'HashRef', via (\&coerce_hash);
286              
287             enum 'Cfn::Parameter::Type', [
288             'String',
289             'Number',
290             'List<Number>',
291             'CommaDelimitedList',
292             'AWS::EC2::AvailabilityZone::Name',
293             'List<AWS::EC2::AvailabilityZone::Name>',
294             'AWS::EC2::Instance::Id',
295             'List<AWS::EC2::Instance::Id>',
296             'AWS::EC2::Image::Id',
297             'List<AWS::EC2::Image::Id>',
298             'AWS::EC2::KeyPair::KeyName',
299             'AWS::EC2::SecurityGroup::GroupName',
300             'List<AWS::EC2::SecurityGroup::GroupName>',
301             'AWS::EC2::SecurityGroup::Id',
302             'List<AWS::EC2::SecurityGroup::Id>',
303             'AWS::EC2::Subnet::Id',
304             'List<AWS::EC2::Subnet::Id>',
305             'AWS::EC2::Volume::Id',
306             'List<AWS::EC2::Volume::Id>',
307             'AWS::EC2::VPC::Id',
308             'List<AWS::EC2::VPC::Id>',
309             'AWS::Route53::HostedZone::Id',
310             'List<AWS::Route53::HostedZone::Id>',
311             'AWS::SSM::Parameter::Name',
312             'AWS::SSM::Parameter::Value<String>',
313             'AWS::SSM::Parameter::Value<List<String>>',
314             'AWS::SSM::Parameter::Value<CommaDelimitedList>',
315             'AWS::SSM::Parameter::Value<AWS::EC2::AvailabilityZone::Name>',
316             'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>',
317             'AWS::SSM::Parameter::Value<AWS::EC2::Instance::Id>',
318             'AWS::SSM::Parameter::Value<AWS::EC2::SecurityGroup::GroupName>',
319             'AWS::SSM:;Parameter::Value<AWS::EC2::SecurityGroup::Id>',
320             'AWS::SSM::Parameter::Value<AWS::EC2::Subnet::Id>',
321             'AWS::SSM::Parameter::Value<AWS::EC2::Volume::Id>',
322             'AWS::SSM::Parameter::Value<AWS::EC2::VPC::Id>',
323             'AWS::SSM::Parameter::Value<AWS::Route53::HostedZone::Id>',
324             'AWS::SSM::Parameter::Value<List<AWS::EC2::AvailabilityZone::Name>>',
325             'AWS::SSM::Parameter::Value<List<AWS::EC2::Image::Id>>',
326             'AWS::SSM::Parameter::Value<List<AWS::EC2::Instance::Id>>',
327             'AWS::SSM::Parameter::Value<List<AWS::EC2::SecurityGroup::GroupName>>',
328             'AWS::SSM::Parameter::Value<List<AWS::EC2::SecurityGroup::Id>>',
329             'AWS::SSM::Parameter::Value<List<AWS::EC2::Subnet::Id>>',
330             'AWS::SSM::Parameter::Value<List<AWS::EC2::Volume::Id>>',
331             'AWS::SSM::Parameter::Value<List<AWS::EC2::VPC::Id>>',
332             'AWS::SSM::Parameter::Value<List<AWS::Route53::HostedZone::Id>>',
333             ];
334              
335             subtype 'ArrayOfCfn::Resource::Properties::TagType',
336             as 'Cfn::Value',
337             where { $_->isa('Cfn::Value::Array') or $_->isa('Cfn::Value::Function') },
338             message { "$_ is not a Cfn::Value or a Cfn::Value::Function" };
339              
340             coerce 'ArrayOfCfn::Resource::Properties::TagType',
341             from 'HashRef',
342             via {
343             if (my $f = Cfn::TypeLibrary::try_function($_)) {
344             return $f
345             } else {
346             die 'Only accepts functions';
347             }
348             },
349             from 'ArrayRef',
350             via {
351             Cfn::Value::Array->new(Value => [
352             map {
353             Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource::Properties::TagType')->coerce($_)
354             } @$_
355             ]);
356             };
357              
358             subtype 'Cfn::Resource::Properties::TagType',
359             as 'Cfn::Value';
360              
361             coerce 'Cfn::Resource::Properties::TagType',
362             from 'HashRef',
363             via {
364             if (my $f = Cfn::TypeLibrary::try_function($_)) {
365             return $f
366             } else {
367             return Cfn::Resource::Properties::Tag->new( %$_ );
368             }
369             };
370              
371             enum 'Cfn::PseudoParameterValue', [
372             'AWS::AccountId',
373             'AWS::NotificationARNs',
374             'AWS::NoValue',
375             'AWS::Partition',
376             'AWS::Region',
377             'AWS::StackId',
378             'AWS::StackName',
379             'AWS::URLSuffix',
380             ];
381              
382             coerce 'Cfn::Internal::Options',
383             from 'HashRef',
384             via { Cfn::Internal::Options->new(%$_) };
385             };
386              
387             package Cfn::Value {
388 22     22   115246 use Moose;
  22         3803356  
  22         159  
389             # just a base class for everything that can go into a cloudformation
390             # object
391 0     0 0 0 sub as_hashref { shift->Value->as_hashref(@_) }
392             }
393              
394             package Cfn::DynamicValue {
395 22     22   166058 use Moose;
  22         54  
  22         116  
396 22     22   140271 use Scalar::Util qw/blessed/;
  22         117  
  22         10271  
397             extends 'Cfn::Value';
398             has Value => (isa => 'CodeRef', is => 'rw', required => 1);
399              
400             sub to_value {
401 0     0 0 0 my $self = shift;
402 0         0 return Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($self->resolve_value(@_));
403             }
404              
405             sub _resolve_value {
406 33     33   63 my ($v, $args) = @_;
407 33 100 66     253 if (blessed($v) and $v->isa('Cfn::Value')) {
    100 66        
    100 66        
408 5         22 return $v->as_hashref(@$args);
409             } elsif (not blessed($v) and ref($v) eq 'HASH') {
410 4         22 return { map { ($_ => _resolve_value($v->{ $_ })) } keys %$v }
  8         20  
411             } elsif (not blessed($v) and ref($v) eq 'ARRAY') {
412 4         8 return [ map { _resolve_value($_) } @$v ]
  5         10  
413             } else {
414 20         87 return $v
415             }
416             }
417              
418             sub resolve_value {
419 18     18 0 30 my $self = shift;
420 18         37 my @args = reverse @_;
421 18         438 my (@ret) = ($self->Value->(@args));
422 18         4552 @ret = map { _resolve_value($_, \@args) } @ret;
  20         53  
423 18         615 return (@ret);
424             }
425              
426             override as_hashref => sub {
427             my $self = shift;
428             return $self->resolve_value(@_);
429             };
430             }
431              
432             package Cfn::Value::Function {
433 22     22   185 use Moose;
  22         45  
  22         123  
434             extends 'Cfn::Value';
435             has Function => (isa => 'Str', is => 'rw', required => 1);
436             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
437              
438             sub as_hashref {
439 1089     1089 0 90190 my $self = shift;
440 1089         26223 my $key = $self->Function;
441 1089         25075 return { $key => $self->Value->as_hashref(@_) }
442             }
443              
444             sub path_to {
445 4623     4623 0 9901 my ($self, $path) = @_;
446 4623         8201 my ($part, $rest) = Cfn::path_split($path);
447              
448 4623 50       98054 die "Can't path $part into a $self" if ($part ne $self->Function);
449 4623 100       64640 return $self->Value->path_to($rest) if (defined $rest);
450 1672         36064 return $self->Value;
451             }
452             }
453              
454             package Cfn::Value::TypedValue {
455 22     22   145821 use Moose;
  22         48  
  22         127  
456             extends 'Cfn::Value';
457              
458             sub as_hashref {
459 698     698 0 1407 my $self = shift;
460 1780         6248 my $hr = { map { ( $_->[0] => $_->[1]->as_hashref(@_) ) }
461 3040         81523 grep { defined $_->[1] }
462 698         2876 map { [ $_->name, $_->get_value($self) ] }
  3040         328818  
463             $self->meta->get_all_attributes
464             };
465 698         3633 return $hr;
466             }
467              
468             sub path_to {
469 1985     1985 0 4148 my ($self, $path) = @_;
470 1985         3667 my ($part, $rest) = Cfn::path_split($path);
471              
472 1985 50       7713 die "Can't go into $part on $self" if (not $self->can($part));
473 1985 100       27032 return $self->$part->path_to($rest) if (defined $rest);
474 1250         41873 return $self->$part;
475             }
476             }
477              
478             package Cfn::Value::Function::Condition {
479 22     22   145533 use Moose;
  22         54  
  22         104  
480             extends 'Cfn::Value::Function';
481             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
482              
483             sub Condition {
484 0     0 0 0 shift->Value->Value;
485             }
486             }
487              
488             package Cfn::Value::Function::Ref {
489 22     22   140908 use Moose;
  22         61  
  22         98  
490             extends 'Cfn::Value::Function';
491             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
492              
493             sub LogicalId {
494 12     12 0 224 shift->Value->Value;
495             }
496             }
497              
498             package Cfn::Value::Function::PseudoParameter {
499 22     22   140703 use Moose;
  22         50  
  22         91  
500             extends 'Cfn::Value::Function::Ref';
501             }
502              
503             package Cfn::Value::Function::GetAtt {
504 22     22   139188 use Moose;
  22         51  
  22         92  
505             extends 'Cfn::Value::Function';
506             has Value => (isa => 'Cfn::Value::ArrayOfPrimitives', is => 'rw', required => 1, coerce => 1);
507              
508             sub LogicalId {
509 2     2 0 4 my $self = shift;
510 2         41 $self->Value->Value->[0]->Value;
511             }
512              
513             sub Property {
514 0     0 0 0 my $self = shift;
515 0         0 $self->Value->Value->[1]->Value;
516             }
517             }
518              
519              
520             package Cfn::Value::Array {
521 22     22   142659 use Moose;
  22         50  
  22         126  
522             extends 'Cfn::Value';
523             has Value => (
524             is => 'rw',
525             required => 1,
526             isa => 'ArrayRef[Cfn::Value|Cfn::Resource::Properties]',
527             traits => ['Array'],
528             handles => {
529             'Count' => 'count',
530             }
531             );
532              
533             sub as_hashref {
534 912     912 0 1681 my $self = shift;
535 912         1847 my @args = @_;
536 912         1484 return [ map { $_->as_hashref(@args) } @{ $self->Value } ]
  1855         5879  
  912         19841  
537             }
538              
539             sub path_to {
540 6232     6232 0 12635 my ($self, $path) = @_;
541 6232         9530 my ($part, $rest) = Cfn::path_split($path);
542              
543 6232 50       126485 die "Can't go into $part on $self" if (not exists $self->Value->[ $part ]);
544 6232 100       82252 return $self->Value->[ $part ]->path_to($rest) if (defined $rest);
545 2298         43808 return $self->Value->[ $part ];
546             }
547             }
548              
549             package Cfn::Value::Hash {
550 22     22   144479 use Moose;
  22         47  
  22         125  
551             extends 'Cfn::Value';
552             has Value => (
553             is => 'rw',
554             required => 1,
555             isa => 'HashRef[Cfn::Value]',
556             );
557              
558             override as_hashref => sub {
559             my $self = shift;
560             my @args = @_;
561             return { map { $_ => $self->Value->{$_}->as_hashref(@args) } keys %{ $self->Value } };
562             };
563              
564             sub path_to {
565 1155     1155 0 2169 my ($self, $path) = @_;
566 1155         1742 my ($part, $rest) = Cfn::path_split($path);
567              
568 1155 50       23097 die "Can't go into $part on $self" if (not exists $self->Value->{ $part });
569 1155 100       17210 return $self->Value->{ $part }->path_to($rest) if (defined $rest);
570 305         5700 return $self->Value->{ $part };
571             }
572             }
573              
574              
575              
576             package Cfn::Value::Primitive {
577 22     22   147835 use Moose;
  22         59  
  22         105  
578             extends 'Cfn::Value';
579             has Value => (isa => 'Value', is => 'rw', required => 1);
580             override as_hashref => sub {
581             my $self = shift;
582             return $self->Value;
583             }
584             }
585              
586             package Cfn::Boolean {
587 22     22   143714 use Moose;
  22         57  
  22         106  
588 22     22   157967 use JSON;
  22         213466  
  22         220  
589             extends 'Cfn::Value::Primitive';
590             has '+Value' => (isa => 'Bool');
591             has stringy => (is => 'ro', required => 1, isa => 'Bool');
592             override as_hashref => sub {
593             my $self = shift;
594             if ($self->stringy){
595             return ($self->Value)?'true':'false';
596             } else {
597             return ($self->Value)?JSON->true:JSON->false;
598             }
599             }
600             }
601              
602             package Cfn::Integer {
603 22     22   5576 use Moose;
  22         50  
  22         160  
604             extends 'Cfn::Value::Primitive';
605             has '+Value' => (isa => 'Int');
606             }
607              
608             package Cfn::Long {
609 22     22   145729 use Moose;
  22         52  
  22         98  
610             extends 'Cfn::Value::Primitive';
611             has '+Value' => (isa => 'Num');
612             }
613              
614             package Cfn::String {
615 22     22   137509 use Moose;
  22         48  
  22         92  
616             extends 'Cfn::Value::Primitive';
617             has '+Value' => (isa => 'Str');
618             }
619              
620             package Cfn::Double {
621 22     22   139535 use Moose;
  22         49  
  22         103  
622             extends 'Cfn::Value::Primitive';
623             has '+Value' => (isa => 'Num');
624             }
625              
626             package Cfn::Timestamp {
627 22     22   138272 use Moose;
  22         52  
  22         89  
628             extends 'Cfn::Value::Primitive';
629             has '+Value' => (isa => 'Str');
630             }
631              
632             package Cfn::Resource {
633 22     22   138339 use Moose;
  22         56  
  22         94  
634             # CCfnX::Dependencies is not production ready
635             with 'Cfn::Dependencies';
636              
637             sub BUILD {
638 1723     1723 0 4613245 my $self = shift;
639              
640 1723         9218 my $class_name = $self->meta->name;
641 1723         42624 $class_name =~ s/^Cfn::Resource:://;
642              
643             # If the user is forcing the Type we want to validate
644             # that we ended up with a valid object
645 1723 100       50327 if (defined $self->Type) {
646 1718 100 100     35939 if ($class_name ne $self->Type and $class_name ne 'AWS::CloudFormation::CustomResource') {
647 1         12 die "Invalid Cfn::Resource"
648             }
649             } else {
650 5         116 $self->Type($class_name);
651             }
652             }
653              
654             has Type => (isa => 'Str', is => 'rw');
655             has Properties => (isa => 'Cfn::Resource::Properties', is => 'rw');
656             has DeletionPolicy => (isa => 'Cfn::Resource::DeletionPolicy', is => 'rw');
657             has DependsOn => (isa => 'ArrayRef[Str]|Str', is => 'rw');
658             has Condition => (isa => 'Str', is => 'rw');
659              
660             sub Property {
661 5680     5680 0 9707 my ($self, $property) = @_;
662 5680 50       169299 return undef if (not defined $self->Properties);
663 5680         156555 return $self->Properties->$property;
664             }
665              
666             sub hasAttribute {
667 3     3 0 11 my ($self, $attribute) = @_;
668 3         5 my @matches = grep { $_ eq $attribute } @{ $self->AttributeList };
  2         8  
  3         11  
669 3         17 return @matches == 1;
670             }
671              
672             sub DependsOnList {
673 12     12 0 16 my $self = shift;
674 12 100       237 return () if (not defined $self->DependsOn);
675 4 100       67 return @{ $self->DependsOn } if (ref($self->DependsOn) eq 'ARRAY');
  3         49  
676 1         18 return $self->DependsOn;
677             }
678              
679             has Metadata => (isa => 'Cfn::Value::Hash', is => 'rw', coerce => 1);
680             has UpdatePolicy => (isa => 'Cfn::Resource::UpdatePolicy', is => 'rw', coerce => 1);
681             has CreationPolicy => (isa => 'HashRef', is => 'rw');
682             has UpdateReplacePolicy => (isa => 'Cfn::Resource::UpdateReplacePolicy', is => 'rw');
683              
684             sub as_hashref {
685 468     468 0 1200 my $self = shift;
686 468         1193 my @args = @_;
687             return {
688 468         14441 (map { $_ => $self->$_->as_hashref(@args) }
689 1404         37730 grep { defined $self->$_ } qw/Properties Metadata UpdatePolicy/),
690 473         9748 (map { $_ => $self->$_ }
691 468         1322 grep { defined $self->$_ } qw/Type DeletionPolicy UpdateReplacePolicy DependsOn CreationPolicy Condition/),
  2808         66591  
692             }
693             }
694              
695             sub path_to {
696 6810     6810 0 12419 my ($self, $path) = @_;
697 6810         10886 my ($part, $rest) = Cfn::path_split($path);
698              
699 6810 50 0     15944 if ($part eq 'Properties') {
    0          
    0          
    0          
700 6810 50       11808 return $self->Properties if (not defined $rest);
701 6810         206093 return $self->Properties->path_to($rest);
702             } elsif ($part eq 'Metadata') {
703 0 0       0 return $self->Metadata if (not defined $rest);
704 0         0 return $self->Metadata->{ $rest };
705             } elsif ($part eq 'DependsOn') {
706 0 0       0 return $self->DependsOn if (not defined $rest);
707 0         0 die "Can't go into $path on resource";
708             } elsif ($part eq 'Type' or $path eq 'Condition') {
709 0 0       0 return $self->$part if (not defined $rest);
710 0         0 die "Can't go into $path on resource";
711             } else {
712 0         0 die "Can't go into $path on resource";
713             }
714             }
715             }
716              
717             package Cfn::Resource::Properties {
718 22     22   156428 use Moose;
  22         53  
  22         397  
719             sub as_hashref {
720 460     460 0 1255 my $self = shift;
721 460         1174 my @args = @_;
722              
723 460         1235 my $ret = {};
724 460         2145 foreach my $att ($self->meta->get_all_attributes) {
725 4175         46890 my $el = $att->name;
726 4175 100       146620 if (defined $self->$el) {
727 1619         52131 my @ret = $self->$el->as_hashref(@args);
728 1619 50       4215 if (@ret == 1) {
729 1619         4890 $ret->{ $el } = $ret[0];
730             } else {
731 0         0 die "A property returned an odd number of values";
732             }
733             }
734             }
735 460         2826 return $ret;
736             }
737              
738             sub path_to {
739 6810     6810 0 15931 my ($self, $path) = @_;
740 6810         12543 my ($part, $rest) = Cfn::path_split($path);
741              
742 6810 50       28032 die "Can't go into $part on $self" if (not $self->can($part));
743 6810 100       162333 return $self->$part->path_to($rest) if (defined $rest);
744 1915         64417 return $self->$part;
745             }
746              
747             sub resolve_references_to_logicalid_with {
748 0     0 0 0 my ($self, $logical_id, $object) = @_;
749 0         0 foreach my $att ($self->meta->get_attribute_list) {
750 0 0       0 next if (not defined $self->$att);
751              
752 0 0 0     0 if ($self->$att->isa('Cfn::Value::Function::Ref') and $self->$att->LogicalId eq $logical_id) {
    0 0        
    0          
    0          
    0          
    0          
753 0         0 my $func = $self->$att;
754             #$self->$att('TBD'); #$object->$objects_ref_prop
755             #warn "Resolved TBD $logical_id";
756 0         0 my @attrs = $object->meta->get_all_attributes;
757 0         0 my @ref = grep { $_->does('CCfnX::Meta::Attribute::Trait::RefValue') } @attrs;
  0         0  
758 0 0       0 if (not @ref) { die $object . " has no RefValue trait. Cannot resolve Ref" }
  0         0  
759             else {
760 0         0 my $property = $ref[0]->name;
761 0         0 my $value = $object->$property;
762 0         0 $self->$att($value);
763             }
764             } elsif ($self->$att->isa('Cfn::Value::Function::GetAtt') and $self->$att->LogicalId eq $logical_id) {
765 0         0 my $func = $self->$att;
766 0         0 my $property = $func->Property;
767 0         0 $self->$att($object->$property);
768 0         0 warn "Resolved $logical_id $property";
769             } elsif ($self->$att->isa('Cfn::Value::Array')) {
770 0         0 map { resolve_references_to_logicalid_with($_, $logical_id, $object) } @{ $self->$att->Value };
  0         0  
  0         0  
771             } elsif ($self->$att->isa('Cfn::Value::Function')) {
772 0         0 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
773             } elsif ($self->$att->isa('Cfn::Value::Primitive')) {
774             # End case. Primitives do nothing
775             # This case is important to be here, as it filters out any Primitives for
776             # the next if
777             } elsif ($self->$att->isa('Cfn::Value')) {
778 0         0 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
779             } else {
780 0         0 die "Don't know how to resolve $att on " . $self->$att;
781             }
782             }
783             }
784             }
785              
786             package Cfn::Resource::UpdatePolicy {
787 22     22   153956 use Moose;
  22         74  
  22         114  
788             extends 'Cfn::Value::TypedValue';
789             has AutoScalingReplacingUpdate => (isa => 'Cfn::Resource::UpdatePolicy::AutoScalingReplacingUpdate', is => 'rw', coerce => 1);
790             has AutoScalingRollingUpdate => (isa => 'Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate', is => 'rw', coerce => 1);
791             has AutoScalingAutoScalingScheduledAction => (isa => 'Cfn::Resource::UpdatePolicy::AutoScalingScheduledAction', is => 'rw', coerce => 1);
792             has UseOnlineResharding => (isa => 'Cfn::Value::Boolean', is => 'rw', coerce => 1);
793             }
794              
795             package Cfn::Resource::UpdatePolicy::AutoScalingReplacingUpdate {
796 22     22   141579 use Moose;
  22         52  
  22         98  
797             extends 'Cfn::Value::TypedValue';
798             has WillReplace => (isa => 'Cfn::Value::Boolean', is => 'rw', required => 1, coerce => 1);
799             }
800              
801             package Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate {
802 22     22   139888 use Moose;
  22         48  
  22         92  
803             extends 'Cfn::Value::TypedValue';
804             has MaxBatchSize => (isa => 'Cfn::Value::Integer', is => 'rw', coerce => 1);
805             has MinInstancesInService => (isa => 'Cfn::Value::Integer', is => 'rw', coerce => 1);
806             has MinSuccessfulInstancesPercent => (isa => 'Cfn::Value::Integer', is => 'rw', coerce => 1);
807             has PauseTime => (isa => 'Cfn::Value::String', is => 'rw', coerce => 1);
808             # TODO: better validate SuspendProcesses
809             has SuspendProcesses => (isa => 'Cfn::Resource::UpdatePolicy::AutoScalingRollingUpdate::SuspendProcesses', is => 'rw', coerce => 1);
810             has WaitOnResourceSignals => (isa => 'Cfn::Value::Boolean', is => 'rw', coerce => 1);
811             }
812              
813             package Cfn::Resource::UpdatePolicy::AutoScalingScheduledAction {
814 22     22   141970 use Moose;
  22         54  
  22         105  
815             extends 'Cfn::Value::TypedValue';
816             has IgnoreUnmodifiedGroupSizeProperties => (isa => 'Cfn::Value::Boolean', is => 'rw', required => 1, coerce => 1);
817             }
818              
819             package Cfn::Output {
820 22     22   139411 use Moose;
  22         48  
  22         93  
821             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
822             has Description => (isa => 'Str', is => 'rw');
823             has Condition => (isa => 'Str', is => 'rw');
824             has Export => (isa => 'Cfn::Value::Hash', is => 'rw', coerce => 1);
825             sub as_hashref {
826 4     4 0 10 my $self = shift;
827 4         10 my @args = @_;
828             return {
829 4 100       105 Value => $self->Value->as_hashref(@args),
    50          
    50          
830             (defined $self->Condition) ? (Condition => $self->Condition) : (),
831             (defined $self->Description) ? (Description => $self->Description) : (),
832             (defined $self->Export) ? (Export => $self->Export->as_hashref) : (),
833             }
834             }
835             sub path_to {
836 765     765 0 1834 my ($self, $path) = @_;
837 765         1278 my ($part, $rest) = Cfn::path_split($path);
838              
839 765 0 33     2115 die "Can't path into $part on $self" if ($part ne 'Value' and
      33        
      0        
840             $part ne 'Description' and
841             $part ne 'Condition' and
842             $part ne 'Export'
843             );
844 765 50 0     1666 if ($part eq 'Value') {
    0          
    0          
845 765 100       4175 return $self->Value if (not defined $rest);
846 630         13256 return $self->Value->path_to($rest);
847             } elsif ($part eq 'Description' or $part eq 'Condition') {
848 0 0       0 die "Can't path into $part on $self" if (defined $rest);
849 0         0 return $self->$part;
850             } elsif ($part eq 'Export') {
851 0 0       0 return $self->Export if (not defined $rest);
852 0         0 return $self->Value->path_to($rest);
853             }
854              
855             }
856             }
857              
858             package Cfn::Parameter {
859 22     22   150656 use Moose;
  22         51  
  22         115  
860             has Type => (isa => 'Cfn::Parameter::Type', is => 'ro', required => 1);
861             has Default => (isa => 'Str', is => 'rw');
862             has NoEcho => (isa => 'Str', is => 'rw');
863             has AllowedValues => ( isa => 'ArrayRef[Str]', is => 'rw');
864             has AllowedPattern => ( isa => 'Str', is => 'rw');
865             has MaxLength => ( isa => 'Str', is => 'rw');
866             has MinLength => ( isa => 'Str', is => 'rw');
867             has MaxValue => ( isa => 'Str', is => 'rw');
868             has MinValue => ( isa => 'Str', is => 'rw');
869             has Description => ( isa => 'Str', is => 'rw');
870             has ConstraintDescription => ( isa => 'Str', is => 'rw');
871              
872             sub as_hashref {
873 0     0 0 0 my $self = shift;
874             return {
875 0 0       0 map { (defined $self->$_) ? ($_ => $self->$_) : () }
  0         0  
876             qw/Type Default NoEcho AllowedValues AllowedPattern MaxLength
877             MinLength MaxValue MinValue Description ConstraintDescription/,
878             }
879             }
880             }
881              
882             package Cfn::Mapping {
883 22     22   142734 use Moose;
  22         48  
  22         119  
884             has Map => (isa => 'HashRef', is => 'ro');
885              
886             sub as_hashref {
887 2     2 0 3 my $self = shift;
888 2         42 return $self->Map;
889             }
890             }
891              
892             package Cfn::Internal::Options {
893 22     22   140724 use Moose;
  22         54  
  22         110  
894             has custom_resource_rename => (is => 'rw', isa => 'Bool', default => 0);
895             }
896              
897             package Cfn {
898 22     22   139206 use Moose;
  22         70  
  22         96  
899 22     22   138288 use Moose::Util;
  22         54  
  22         184  
900 22     22   4223 use Scalar::Util;
  22         55  
  22         1325  
901 22     22   14821 use Cfn::ResourceModules;
  22         57  
  22         51591  
902              
903             has AWSTemplateFormatVersion => (isa => 'Str', is => 'rw');
904             has Description => (isa => 'Str', is => 'rw');
905             has Transform => (isa => 'Cfn::Transform', is => 'rw', coerce => 1);
906              
907             our $VERSION = '0.11';
908              
909             has Parameters => (
910             is => 'rw',
911             isa => 'Cfn::ParameterHash',
912             coerce => 1,
913             traits => [ 'Hash' ],
914             handles => {
915             Parameter => 'accessor',
916             ParameterList => 'keys',
917             ParameterCount => 'count',
918             ParameterList => 'keys',
919             },
920             );
921             has Mappings => (
922             is => 'rw',
923             isa => 'Cfn::MappingHash',
924             coerce => 1,
925             traits => [ 'Hash' ],
926             handles => {
927             Mapping => 'accessor',
928             MappingCount => 'count',
929             MappingList => 'keys',
930             },
931             );
932             has Conditions => (
933             is => 'rw',
934             isa => 'Cfn::ConditionHash',
935             traits => [ 'Hash' ],
936             coerce => 1,
937             handles => {
938             Condition => 'accessor',
939             ConditionList => 'keys',
940             ConditionCount => 'count',
941             },
942             );
943             has Resources => (
944             is => 'rw',
945             isa => 'Cfn::ResourceHash',
946             coerce => 1,
947             traits => [ 'Hash' ],
948             handles => {
949             Resource => 'accessor',
950             ResourceList => 'keys',
951             ResourceCount => 'count',
952             },
953             );
954             has Outputs => (
955             is => 'rw',
956             isa => 'Cfn::OutputHash',
957             coerce => 1,
958             traits => [ 'Hash' ],
959             handles => {
960             Output => 'accessor',
961             OutputList => 'keys',
962             OutputCount => 'count',
963             },
964             );
965             has Metadata => (
966             is => 'rw',
967             isa => 'Cfn::MetadataHash',
968             coerce => 1,
969             traits => [ 'Hash' ],
970             handles => {
971             MetadataItem => 'accessor',
972             MetadataList => 'keys',
973             MetadataCount => 'count',
974             },
975             );
976              
977             has cfn_options => (
978             is => 'ro',
979             isa => 'Cfn::Internal::Options',
980             coerce => 1,
981             default => sub { Cfn::Internal::Options->new },
982             );
983              
984             sub list_resource_modules {
985 0     0 0 0 return Cfn::ResourceModules::list();
986             }
987              
988             sub load_resource_module {
989 1720     1720 0 5594 my (undef, $type) = @_;
990 1720         7235 return Cfn::ResourceModules::load($type);
991             }
992              
993             sub ResourcesOfType {
994 0     0 1 0 my ($self, $type) = @_;
995 0         0 return grep { $_->Type eq $type } values %{ $self->Resources };
  0         0  
  0         0  
996             }
997              
998             sub addParameter {
999 0     0 1 0 my ($self, $name, $type, %rest) = @_;
1000 0 0       0 Moose->throw_error("A parameter named $name already exists") if (defined $self->Parameter($name));
1001 0 0       0 if (ref $type) {
1002 0         0 return $self->Parameter($name, $type);
1003             } else {
1004 0         0 return $self->Parameter($name, Cfn::Parameter->new(Type => $type, %rest));
1005             }
1006             }
1007              
1008             sub addMapping {
1009 3     3 1 3118 my ($self, $name, $mapping) = @_;
1010 3 100       99 Moose->throw_error("A mapping named $name already exists") if (defined $self->Mapping($name));
1011 2 50       6 if (ref $mapping eq 'HASH') {
1012 2         15 return $self->Mapping($name, Cfn::Mapping->new(Map => $mapping));
1013             } else {
1014 0         0 return $self->Mapping($name, $mapping);
1015             }
1016             }
1017              
1018             sub addOutput {
1019 4     4 1 2326 my ($self, $name, $output, @rest) = @_;
1020 4 50       133 Moose->throw_error("An output named $name already exists") if (defined $self->Output($name));
1021 4 50       17 if (my $class = blessed $output) {
1022 0 0       0 die "Can't call addOutput with a $class" if ($class ne 'Cfn::Output');
1023 0         0 return $self->Output($name, $output);
1024             } else {
1025 4         69 return $self->Output($name, Cfn::Output->new( Value => $output, @rest ));
1026             }
1027             }
1028              
1029             sub addCondition {
1030 1     1 1 5 my ($self, $name, $value) = @_;
1031 1 50       35 Moose->throw_error("A condition named $name already exists") if (defined $self->Condition($name));
1032 1         5 return $self->Condition($name, Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($value));
1033             }
1034              
1035             sub addResource {
1036 492     492 1 541274 my ($self, $name, $second_param, $third_param, @rest) = @_;
1037 492 50       15552 Moose->throw_error("A resource named $name already exists") if (defined $self->Resource($name));
1038              
1039 492 100       1826 if (not ref $second_param){
1040 267         705 my $type = $second_param;
1041 267         656 my (@properties, @extra_props);
1042              
1043 267 100       915 if (ref($third_param) eq 'HASH') {
1044 33         131 @properties = %$third_param;
1045 33 100 33     126 if (not defined $rest[0]){
    50          
1046 26         52 @extra_props = ();
1047             } elsif (defined $rest[0] and ref($rest[0]) eq 'HASH') {
1048 7         12 @extra_props = %{ $rest[0] }
  7         21  
1049             } else {
1050 0         0 die "Don't know what to do with the fourth parameter to addResource";
1051             }
1052             } else {
1053 234   66     1370 @properties = ( $third_param // () , @rest);
1054 234         755 @extra_props = ();
1055             }
1056              
1057 267         1264 return $self->Resources->{ $name } = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource')->coerce({
1058             Type => $type,
1059             Properties => { @properties },
1060             @extra_props,
1061             })
1062             } else {
1063 225         655 my $object = $second_param;
1064 225         6662 return $self->Resource($name, $object);
1065             }
1066             }
1067              
1068             sub addMetadata {
1069 5     5 0 10014 my ($self, $name, $metadata) = @_;
1070              
1071 5 50       17 if (ref($name) eq 'HASH') {
1072 5 50       111 Moose->throw_error("The stack already has metadata") if (defined $self->Metadata);
1073 5         97 $self->Metadata($name);
1074             } else {
1075 0 0       0 Moose->throw_error("A metadata item named $name already exists") if (defined $self->MetadataItem($name));
1076 0         0 return $self->MetadataItem($name, Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($metadata));
1077             }
1078             }
1079              
1080             sub addResourceMetadata {
1081 0     0 1 0 my ($self, $name, %args) = @_;
1082 0 0       0 Moose->throw_error("A resource named $name must already exist") if (not defined $self->Resources->{ $name });
1083 0         0 $self->Resources->{ $name }->Metadata({ %args });
1084             }
1085             sub addDependsOn {
1086 0     0 1 0 my ($self, $name, @args) = @_;
1087 0 0       0 Moose->throw_error("A resource named $name must already exist") if (not defined $self->Resources->{ $name });
1088 0         0 $self->Resources->{ $name }->DependsOn( [ @args ] );
1089             }
1090             sub addDeletionPolicy {
1091 0     0 1 0 my ($self, $name, $policy) = @_;
1092 0 0       0 Moose->throw_error("A resource named $name must already exist") if (not defined $self->Resources->{ $name });
1093 0         0 $self->Resources->{ $name }->DeletionPolicy( $policy );
1094             }
1095             sub addUpdatePolicy {
1096 0     0 1 0 my ($self, $name, $policy) = @_;
1097 0 0       0 Moose->throw_error("A resource named $name must already exist") if (not defined $self->Resources->{ $name });
1098 0         0 $self->Resources->{ $name }->UpdatePolicy( Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource:UpdatePolicy')->coerce($policy) );
1099             }
1100              
1101             sub addTransform {
1102 0     0 0 0 my ($self, $name, $transform) = @_;
1103 0 0       0 if ( not defined $self->Transform) { $self->Transform([]) };
  0         0  
1104 0         0 push @{$self->Transform}, @{$transform};
  0         0  
  0         0  
1105             }
1106              
1107             sub from_hashref {
1108 208     208 1 79502 my ($class, $hashref) = @_;
1109 208         1688 return $class->new(%$hashref);
1110             }
1111              
1112             sub resolve_dynamicvalues {
1113 3     3 1 8 my $self = shift;
1114 3         14 return Cfn->from_hashref($self->as_hashref);
1115             }
1116              
1117             sub as_hashref {
1118 470     470 1 5568 my $self = shift;
1119             return {
1120             (defined $self->AWSTemplateFormatVersion)?(AWSTemplateFormatVersion => $self->AWSTemplateFormatVersion):(),
1121             (defined $self->Description)?(Description => $self->Description):(),
1122             (defined $self->Transform) ? (Transform => $self->Transform) : (),
1123 2         40 (defined $self->Mappings)?(Mappings => { map { ($_ => $self->Mappings->{ $_ }->as_hashref) } keys %{ $self->Mappings } }):(),
  1         20  
1124 0         0 (defined $self->Parameters)?(Parameters => { map { ($_ => $self->Parameters->{ $_ }->as_hashref) } keys %{ $self->Parameters } }):(),
  0         0  
1125 4         84 (defined $self->Outputs)?(Outputs => { map { ($_ => $self->Outputs->{ $_ }->as_hashref($self)) } keys %{ $self->Outputs } }):(),
  2         42  
1126 1         31 (defined $self->Conditions)?(Conditions => { map { ($_ => $self->Condition($_)->as_hashref($self)) } $self->ConditionList }):(),
1127 8         155 (defined $self->Metadata)?(Metadata => { map { ($_ => $self->Metadata->{ $_ }->as_hashref($self)) } $self->MetadataList }):(),
1128 470 50       13406 Resources => { map { ($_ => $self->Resource($_)->as_hashref($self)) } $self->ResourceList },
  467 50       13617  
    100          
    100          
    50          
    100          
    100          
    100          
1129             }
1130             }
1131              
1132             sub path_split {
1133 45770     45770 0 62708 my $path = shift;
1134 45770 50       78344 die "No path specified" if (not defined $path);
1135 45770         92747 my @parts = split/\./, $path, 2;
1136 45770         107109 return ($parts[0], $parts[1]);
1137             }
1138              
1139             sub path_to {
1140 8695     8695 1 39828 my ($self, $path) = @_;
1141 8695         18205 my ($part, $rest) = Cfn::path_split($path);
1142              
1143 8695 50 100     28165 die "Can't path into $part" if ($part ne 'Resources' and
      100        
      100        
      66        
      33        
1144             $part ne 'Mappings' and
1145             $part ne 'Parameters' and
1146             $part ne 'Outputs' and
1147             $part ne 'Conditions' and
1148             $part ne 'Metadata');
1149              
1150 8695         182556 my $current_element = $self->$part;
1151 8695 50       17247 return $current_element if (not defined $rest);
1152              
1153 8695         13274 ($part, $rest) = Cfn::path_split($rest);
1154              
1155 8695 50       18340 die "Must specify a resource to traverse into" if (not defined $part);
1156              
1157 8695 50       17584 die "No element $part found" if (not defined $current_element->{ $part });
1158 8695 100       32003 return $current_element->{ $part }->path_to($rest) if (defined $rest);
1159 1120         24539 return $current_element->{ $part };
1160             }
1161              
1162             has json => (is => 'ro', lazy => 1, default => sub {
1163             require JSON;
1164             return JSON->new->canonical
1165             });
1166              
1167             sub as_json {
1168 0     0 1 0 my $self = shift;
1169 0         0 my $href = $self->as_hashref;
1170 0         0 return $self->json->encode($href);
1171             }
1172              
1173             sub from_json {
1174 188     188 1 2958881 my ($class, $json) = @_;
1175              
1176 188         1754 require JSON;
1177 188         1165 return $class->from_hashref(JSON::from_json($json));
1178             }
1179              
1180             sub _get_yaml_pp {
1181 13     13   99 require YAML::PP;
1182 13         95 YAML::PP->new(
1183             schema => [ ':Cfn::YAML::Schema' ],
1184             );
1185             }
1186              
1187             has yaml => (is => 'ro', lazy => 1, default => \&_get_yaml_pp);
1188              
1189             sub as_yaml {
1190 0     0 1 0 my $self = shift;
1191 0         0 return $self->yaml->dump_string($self);
1192             }
1193              
1194             sub from_yaml {
1195 12     12 1 57004 my ($class, $yaml) = @_;
1196 12         47 my $parser = _get_yaml_pp;
1197 12         2468 return $class->from_hashref($parser->load_string($yaml));
1198             }
1199             }
1200              
1201             package Cfn::MutabilityTrait {
1202 22     22   11175 use Moose::Role;
  22         109828  
  22         87  
1203 22     22   120371 use Moose::Util;
  22         57  
  22         105  
1204             Moose::Util::meta_attribute_alias('CfnMutability');
1205             has mutability => (is => 'ro', isa => 'Str', required => 1);
1206             }
1207              
1208             package Cfn::Resource::Properties::Tag {
1209 22     22   5150 use Moose;
  22         48  
  22         157  
1210             extends 'Cfn::Value::TypedValue';
1211             has Key => (isa => 'Cfn::Value::String', is => 'rw', coerce => 1, required => 1, traits => [ 'CfnMutability' ], mutability => 'Immutable');
1212             has Value => (isa => 'Cfn::Value::String', is => 'rw', coerce => 1, traits => [ 'CfnMutability' ], mutability => 'Mutable');
1213             }
1214              
1215             1;
1216             ### main pod documentation begin ###
1217              
1218             =encoding UTF-8
1219              
1220             =head1 NAME
1221              
1222             Cfn - An object model for CloudFormation documents
1223              
1224             =head1 DESCRIPTION
1225              
1226             This module helps parse, manipulate, validate and generate CloudFormation documents in JSON
1227             and YAML formats (see stability section for more information on YAML). It creates an object
1228             model of a CloudFormation template so you can work with the document as a set of objects.
1229             See L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html> for
1230             more information.
1231              
1232             It provides full blown objects for all know CloudFormation resources. See
1233             L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html> for a list
1234             of all resource types. These objects live in the C<Cfn::Resource> namespace.
1235              
1236             The module provides a set of objects representing each piece of CloudFormation. Following is a list of all
1237             object types in the distribution:
1238              
1239             =head1 Cfn object
1240              
1241             The C<Cfn> class is the "root" of a CloudFormation document. It represents an entire CloudFormation document.
1242             It has attributes and methods to access the parts of a CloudFormation document.
1243              
1244             use Cfn;
1245             my $cfn = Cfn->new;
1246             $cfn->addResource('MyRes' => ...);
1247             my $res = $cfn->Resource('MyRes');
1248              
1249             =head2 Constructors
1250              
1251             =head3 new(Resources => { ... }, Outputs => { }, ...)
1252              
1253             The default Moose constructor. You can initialize an empty document like this:
1254              
1255             my $cfn = Cfn->new;
1256             print $cfn->as_json;
1257              
1258             =head3 from_hashref
1259              
1260             CloudFormation documents resemble Perl HashRefs (since they're just JSON datastructures).
1261             This method converts a hashref that represents a CloudFormation document into a Cfn object.
1262              
1263             use Data::Dumper;
1264             my $cfn = Cfn->from_hashref({ Resources => { R1 => { Type => '...', Properties => { ... } } } });
1265             print Dumper($cfn->Resource('R1');
1266              
1267             =head3 from_json
1268              
1269             This method creates a Cfn object from a JSON string that contains a CloudFormation document in JSON format
1270              
1271             =head3 from_yaml
1272              
1273             This method creates a Cfn object from a YAML string that contains a CloudFormation document in YAML format
1274              
1275             =head2 Attributes
1276              
1277             =head3 json
1278              
1279             When serializing to JSON with C<as_json>, the encode method on this object is called passing the
1280             documents hashref representation. By default the JSON generated is "ugly", that is, all in one line,
1281             but in canonical form (so a given serialization always has attributes in the same order).
1282              
1283             You can specify your own JSON serializer to control how JSON is generated:
1284              
1285             my $cfn = Cfn->new(json => JSON->new->canonical->pretty);
1286             ...
1287             print $cfn->as_json;
1288              
1289             =head3 yaml
1290              
1291             Holds a configured C<YAML::PP> parser for use when serializing and deserializing to and from YAML.
1292             Methods C<load_string> and C<dump_string> are called when needed from convert the object model
1293             to a YAML document, and to convert a YAML document to a datastructure that can later be coerced
1294             into the object model.
1295              
1296             =head3 cfn_options
1297              
1298             A C<Cfn::Internal::Options> object instance that controls how the as_hashref method converts the Cfn object
1299             to a datastructure suitable for CloudFormation (only HashRefs, ArrayRefs and Scalars).
1300              
1301             You can specify your own options as a hashref with the attributes to C<Cfn::Internal::Options> in the
1302             constructor.
1303              
1304             my $cfn = Cfn->new(cfn_options => { custom_resource_rename => 1 });
1305             ...
1306             print Dumper($cfn->as_hashref);
1307              
1308             See the C<Cfn::Internal::Options> object for more details
1309              
1310             =head3 AWSTemplateFormatVersion
1311              
1312             A string with the value of the AWSTemplateFormatVersion field of the CloudFormation document. Can be undef.
1313              
1314             =head3 Description
1315              
1316             A string with the value of the Description field of the CloudFormation document. Can be undef.
1317              
1318             =head3 Transform
1319              
1320             An ArrayRef of Strings with the values of the Transform field of the CloudFormation document. Can be undef.
1321              
1322             =head3 Parameters
1323              
1324             A HashRef of C<Cfn::Parameter> objects. The keys are the name of the Parameters.
1325             There are a set of convenience methods for accessing this attribute:
1326              
1327             $cfn->Parameter('ParamName') # returns a Cfn::Parameter or undef
1328             $cfn->ParameterList # returns a list of the parameters in the document
1329             $cfn->ParameterCount # returns the number of parameters in the document
1330              
1331             =head3 Mappings
1332              
1333             A HashRef of C<Cfn::Mapping> objects. The keys are the name of the Mappings.
1334             There are a set of convenience methods for accessing this attribute:
1335              
1336             $cfn->Mapping('MappingName') # returns a Cfn::Parameter or undef
1337             $cfn->MappingList # returns a list of the mappings in the document
1338             $cfn->MappingCount # returns the number of mappings in the document
1339              
1340             =head3 Conditions
1341              
1342             A HashRef of C<Cfn::Condition> objects. The keys are the name of the Mappings.
1343             There are a set of convenience methods for accessing this attribute:
1344              
1345             $cfn->Mapping('MappingName') # returns a Cfn::Mapping or undef
1346             $cfn->MappingList # returns a list of the mappings in the document
1347             $cfn->MappingCount # returns the number of mappings in the document
1348              
1349             =head3 Resources
1350              
1351             A HashRef of C<Cfn::Resource> objects. The keys are the name of the Resources.
1352             There are a set of convenience methods for accessing this attribute:
1353              
1354             $cfn->Resource('ResourceName') # returns a Cfn::Resource or undef
1355             $cfn->ResourceList # returns a list of the resources in the document
1356             $cfn->ResourceCount # returns the number of resources in the document
1357              
1358             =head3 Outputs
1359              
1360             A HashRef of C<Cfn::Output> objects. The keys are the name of the Outputs.
1361             There are a set of convenience methods for accessing this attribute:
1362              
1363             $cfn->Output('OutputName') # returns a Cfn::Output or undef
1364             $cfn->OutputList # returns a list of the outputs in the document
1365             $cfn->OutputCount # returns the number of outputs in the document
1366              
1367             =head3 Metadata
1368              
1369             A HashRef of C<Cfn::Value> or subclasses of C<Cfn::Value>. Represents the
1370             Metadata key of the CloudFormation document.
1371              
1372             There are a set of convenience methods for accessing this attribute:
1373              
1374             $cfn->Metadata('MetadataName') # returns a Cfn::Metadata or undef
1375             $cfn->MetadataList # returns a list of keys in the document Metadata
1376             $cfn->MetadataCount # returns the number of keys in the document Metadata
1377              
1378             =head2 Methods
1379              
1380             =head3 as_hashref
1381              
1382             Returns a Perl HashRef representation of the CloudFormation document. This HashRef
1383             has no objects in it. It is suitable for converting to JSON and passing to CloudFormation
1384              
1385             C<as_hashref> triggers the serialization process of the document, which scans the whole
1386             object model asking it's components to serialize (calling their C<as_hashref>). Objects
1387             can decide how they serialize to a hashref.
1388              
1389             When C<$cfn->as_hashref> is invoked, all the dynamic values in the Cfn object will be
1390             called with the C<$cfn> instance as the first parameter to their subroutine
1391              
1392             $cfn->addResource('R1', 'AWS::IAM::User', Path => Cfn::DynamicValue->new(Value => sub {
1393             my $cfn = shift;
1394             return $cfn->ResourceCount + 41
1395             }));
1396             $cfn->as_hashref->{ Resources }->{ R1 }->{ Properties }->{ Path } # == 42
1397              
1398             =head3 as_json
1399              
1400             Returns a JSON representation of the current instance
1401              
1402             =head3 as_yaml
1403              
1404             Returns a YAML representation of the current instance
1405              
1406             =head3 path_to($path)
1407              
1408             Given a path in the format C<'Resources.R1.Properties.PropName'> it will return the value
1409             stored in PropName of the resource R1. Use C<'Resource.R1.Properties.ArrayProp.0'> to access
1410             Arrays.
1411              
1412             =head3 resolve_dynamicvalues
1413              
1414             Returns a new C<Cfn> object with all C<Cfn::DynamicValues> resolved.
1415              
1416             =head3 ResourcesOfType($type)
1417              
1418             Returns a list of all the Resources of a given type.
1419              
1420             foreach my $iam_user ($cfn->ResourcesOfType('AWS::IAM::User')) {
1421             ...
1422             }
1423              
1424             =head3 addParameter($name, $object)
1425              
1426             Adds an already instanced C<Cfn::Parameter> object. Throws an exception if the parameter already exists.
1427              
1428             $cfn->addParameter('P1', Cfn::Parameter->new(Type => 'String', MaxLength => 5));
1429              
1430             =head3 addParameter($name, $type, %properties)
1431              
1432             Adds a named parameter to the document with the specified type and properties. See C<Cfn::Parameter> for available
1433             properties. Throws an exception if the parameter already exists.
1434              
1435             $cfn->addParameter('P1', 'String', MaxLength => 5);
1436              
1437             =head3 addMapping($name, $object_or_hashref);
1438              
1439             Adds a named mapping to the mappings of the document. The second parameter can be a C<Cfn::Mapping> object or
1440             a HashRef that will be coerced to a C<Cfn::Mapping> object
1441              
1442             $cfn->addMapping('amis', { 'eu-west-1' => 'ami-12345678' });
1443             $cfn->addMapping('amis', Cfn::Mapping->new(Map => { 'eu-west-1' => 'ami-12345678' }));
1444             # $cfn->Mapping('amis') is a Cfn::Mapping object
1445              
1446             =head3 addOutput($name, $object)
1447              
1448             Adds an already instanced C<Cfn::Output> object. Throws an exception if the output already exists.
1449              
1450             $cfn->addParameter('O1', Cfn::Output->new(Value => { Ref => 'R1' });
1451              
1452             =head3 addOutput($name, $output[, %output_attributes]);
1453              
1454             Adds a named output to the document. See C<Cfn::Output> for available
1455             output_attributes. Throws an exception if the output already exists.
1456              
1457             $cfn->addParameter('O1', { Ref => 'R1' });
1458             $cfn->addParameter('O1', { Ref => 'R1' }, Description => 'Bla bla');
1459              
1460              
1461             =head3 addCondition($name, $value)
1462              
1463             Adds a named condition to the document. The value parameter should be
1464             a HashRef that expresses a CloudFormation condition. See L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html>
1465              
1466             =head3 addResource($name, $object)
1467              
1468             Adds a named resource to the document. $object has to be an instance of a
1469             subclass of C<Cfn::Resource>. Throws an exception if a resource already
1470             exists with that name.
1471              
1472             =head3 addResource($name, $type, %properties)
1473              
1474             Adds a named resource to the document, putting the specified properties in the
1475             resources properties. See subclasses of C<Cfn::Resource> for more details.
1476              
1477             $cfn->addResource('R1', 'AWS::IAM::User');
1478              
1479             $cfn->addResource('R2', 'AWS::IAM::User', Path => '/');
1480             # $cfn->Resource('R2')->Properties->Path is '/'
1481              
1482             Throws an exception if a resource already exists with that name.
1483              
1484             =head3 addResource($name, $name, $properties, $resource_attributes)
1485              
1486             Adds a named resource to the document. properties and resource_attributes
1487             are hashrefs.
1488              
1489             $cfn->addResource('R3', 'AWS::IAM::User', { Path => '/' });
1490             # $cfn->Resource('R3')->Properties->Path is '/'
1491             $cfn->addResource('R3', 'AWS::IAM::User', { Path => '/' }, { DependsOn => [ 'R2' ] });
1492             # $cfn->Resource('R3')->DependsOn->[0] is 'R2'
1493              
1494             Throws an exception if a resource already exists with that name.
1495              
1496             =head3 addResourceMetadata($name, %metadata);
1497              
1498             Adds metadata to the Metadata attribute of a Resource.
1499              
1500             $cfn->addResourceMetadata('R1', MyMetadataKey1 => 'Value');
1501             # $cfn->Resource('R1')->Metadata->{ MyMedataKey1 } is 'Value'
1502              
1503             =head3 addDependsOn($resource_name, $depends_on1, $depends_on2)
1504              
1505             $cfn->addDependsOn('R1', 'R2', 'R3');
1506             # $cfn->Resource('R1')->DependsOn is [ 'R2', 'R3' ]
1507              
1508             =head3 addDeletionPolicy($resource_name)
1509              
1510             Adds a DeletionPolicy to the resource. L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html>
1511              
1512             =head3 addUpdatePolicy($resource_name)
1513            
1514             Adds an UpdatePolicy to the resource. L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html>
1515              
1516             =head1 Cfn::Value
1517              
1518             Is a base class for the attributes of Cloudformation values. In Cloudformation you can find that in
1519             a resources attributes you can place functions, references, etc.
1520              
1521             "Attribute": "hello"
1522             "Attribute": { "Ref": "R1" }
1523             "Attribute": { "Fn::GetAtt": [ "R1", "InstanceId" ] }
1524              
1525             All value objects in the Cfn toolkit subclass C<Cfn::Value> as a common ancestor. Once the object model is built,
1526             you can find that a
1527              
1528             $cfn->addResource('R1', 'AWS::IAM::User', Path => '/');
1529             # $cfn->Resource('R1')->Properties->Path is a Cfn::Value::Primitive
1530              
1531             $cfn->addResource('R1', 'AWS::IAM::User', Path => { 'Fn::Join' => [ '/', { Ref => 'Param1' }, '/' ] });
1532             # $cfn->Resource('R1')->Properties->Path is a Cfn::Value::Function::Join
1533              
1534             All C<Cfn::Value> subclasses have to implement an C<as_hashref> method that returns a HashRef suitable for
1535             conversion to JSON for CloudFormation. A attributes of objects that hold C<Cfn::Value> subclasses should
1536             enable coercion of the attribute so that plain hashrefs can be coerced into the appropiate Cfn::Value objects
1537              
1538             Here is a Hierarchy of the different Cfn::Value descendant object:
1539              
1540             Cfn::Value
1541             |--Cfn::DynamicValue
1542             |--Cfn::Value::Function
1543             | |--Cfn::Value::Function::Condition
1544             | |--Cfn::Value::Function::Ref
1545             | |--Cfn::Value::Function::PseudoParameter
1546             | |--Cfn::Value::Function::GetAtt
1547             |--Cfn::Value::Array
1548             |--Cfn::Value::Hash
1549             |--Cfn::Value::Primitive
1550             | |--Cfn::Boolean
1551             | |--Cfn::Integer
1552             | |--Cfn::Long
1553             | |--Cfn::String
1554             | |--Cfn::Double
1555             | |--Cfn::Timestamp
1556             |--Cfn::Value::TypedValue
1557            
1558              
1559             =head2 Cfn::DynamicValue
1560              
1561             The C<Value> attribute of this object is a CodeRef that get's called
1562             when as_hashref is called.
1563              
1564             $cfn->addResource('R1', 'AWS::IAM::User', Path => Cfn::DynamicValue->new(Value => sub { return 'Hello' });
1565             $cfn->path_to('Resources.R1.Properties.Path') # isa Cfn::DynamicValue
1566             $cfn->path_to('Resources.R1.Properties.Path')->as_hashref # eq 'Hello'
1567              
1568             When C<$cfn->as_hashref> is invoked, all the dynamic values in the Cfn object will be
1569             called with the C<$cfn> instance as the first parameter to their subroutine
1570              
1571             $cfn->addResource('R1', 'AWS::IAM::User', Path => Cfn::DynamicValue->new(Value => sub {
1572             my $cfn = shift;
1573             return $cfn->ResourceCount + 41
1574             }));
1575             $cfn->as_hashref->{ Resources }->{ R1 }->{ Properties }->{ Path } # == 42
1576              
1577             =head2 Cfn::Value::Function
1578              
1579             All function statements derive from Cfn::Value::Function.
1580             The name of the function can be found in the C<Function> attribute
1581             It's value can be found in the C<Value> attribute
1582              
1583             =head2 Cfn::Value::Function::Ref
1584              
1585             Object of this class represent a CloudFormation Ref. You can find the value
1586             of the reference in the C<Value> attribute. Note that the Value attribute contains
1587             another C<Cfn::Value>. It derives from C<Cfn::Value::Function>
1588              
1589             $cfn->addResource('R1', 'AWS::IAM::User', Path => { Ref => 'AWS::Region' });
1590             $cfn->path_to('Resources.R1.Properties.Path') # isa Cfn::Value::Function::PseudoParameter
1591              
1592             =head2 Cfn::Value::Function::PseudoParameter
1593              
1594             This is a subclass of C<Cfn::Value::Function::Ref> used to hold what CloudFormation
1595             calls PseudoParameters.
1596              
1597             $cfn->addResource('R1', 'AWS::IAM::User', Path => { Ref => 'AWS::Region' });
1598             $cfn->path_to('Resources.R1.Properties.Path') # isa Cfn::Value::Function::PseudoParam
1599              
1600             =head2 Cfn::Value::Function::GetAtt
1601              
1602             This class represents 'Fn::GetAtt' nodes in the object model. It's a subclass of C<Cfn::Value::Function>.
1603              
1604             $cfn->addResource('R1', 'AWS::IAM::User', Path => { 'Fn::GetAtt' => [ 'R1', 'InstanceId' ] });
1605             $cfn->path_to('Resources.R1.Properties.Path') # isa Cfn::Value::Function::GetAtt
1606             $cfn->path_to('Resources.R1.Properties.Path')->LogicalId # eq 'R1'
1607             $cfn->path_to('Resources.R1.Properties.Path')->Property # eq 'InstanceId'
1608              
1609             =head2 Cfn::Value::Array
1610              
1611             This class represents Arrays in the object model. It's C<Value> property is an ArrayRef
1612             of C<Cfn::Values> or C<Cfn::Resource::Properties>.
1613              
1614             There is also a subtype called C<Cfn::Value::ArrayOfPrimitives> that restricts the values
1615             in the array to C<Cfn::Value::Primitive> types.
1616              
1617             =head2 Cfn::Value::Hash
1618              
1619             This class represents JSON objects whose keys are not defined beforehand (arbitrary keys).
1620             It's C<Value> property is a HashRef of C<Cfn::Value>s.
1621              
1622             =head2 Cfn::Value::Primitive
1623              
1624             This is a base class for any "simple" value (what the CloudFormation spec calls C<PrimitiveType>).
1625             This classes C<Value> attribute has no type constraint, so it actually accepts anything. This class
1626             is supposed to only be inherited from, specializing the C<Value> attribute to a specific type.
1627              
1628             =head2 Cfn::Boolean
1629              
1630             Used to store and validate CloudFormation C<Boolean> values. Has a C<stringy> attribute that controls if C<as_hashref>
1631             returns a string boolean C<"true"> or C<"false"> or a literal C<true> or C<false>, since these two
1632             boolean forms are accepted in CloudFormation.
1633              
1634              
1635             =head2 Cfn::Integer
1636              
1637             Used to store and validate CloudFormation C<Integer> values.
1638              
1639             =head2 Cfn::Long
1640              
1641             Used to store and validate CloudFormation C<Long> values.
1642              
1643             =head2 Cfn::String
1644              
1645             Used to store and validate CloudFormation C<String> values.
1646              
1647             =head2 Cfn::Double
1648              
1649             Used to store and validate CloudFormation C<Double> values.
1650              
1651             =head2 Cfn::Timestamp
1652              
1653             Used to store CloudFormation C<Timestamp> values. Only validates that it's a string.
1654            
1655             =head2 Cfn::Value::TypedValue
1656              
1657             Used as a base class for structured properties of CloudFormation resources. The subclasses
1658             of TypedValue declare Moose attributes that are used to represent and validate that the
1659             properties of a CloudFormation resource are well formed.
1660              
1661             =head1 Cfn::Resource
1662              
1663             Represents a CloudFormation Resource. All C<Cfn::Resource::*> objects (like L<Cfn::Resource::AWS::IAM::User>)
1664             use C<Cfn::Resource> as a base class.
1665              
1666             =head2 Attributes for Cfn::Resource objects
1667              
1668             The attributes for Cfn::Resource objects map to the attributes of CloudFormation Resources.
1669              
1670             {
1671             "Type": "AWS::IAM::User",
1672             "Properties": { ... },
1673             "DependsOn": "R2"
1674             ...
1675             }
1676              
1677             =head3 Type
1678              
1679             Holds a string with the type of the resource.
1680              
1681             =head3 Properties
1682              
1683             Holds a C<Cfn::Value::Properties> subclass with the properties of the resource.
1684              
1685             =head3 DeletionPolicy
1686              
1687             Holds the DeletionPolicy. Validates that the DeletionPolicy is valid
1688              
1689             =head3 DependsOn
1690              
1691             Can hold either a single string or an arrayref of strings. This is because CloudFormation
1692             supports C<DependsOn> in these two forms. Method C<DependsOnList> provides a uniform way
1693             of accessing the DependsOn attribute.
1694              
1695             =head3 Condition
1696              
1697             Can hold a String identifying the Condition property of a resource
1698              
1699             =head3 Metadata
1700              
1701             Is a C<Cfn::Value::Hash> for the resources metadata
1702              
1703             =head3 UpdatePolicy
1704              
1705             Holds the UpdatePolicy. Validates that the UpdatePolicy is valid
1706              
1707             =head3 CreationPolicy
1708              
1709             HashRef with the CreationPolicy. Doesn't validate CreationPolicies.
1710              
1711             =head2 Methods for Cfn::Resource objects
1712              
1713             =head3 AttributeList
1714              
1715             Returns an ArrayRef of attributes that can be recalled in CloudFormation via C<Fn::GetAtt>.
1716              
1717             Can also be retrieved as a class method C<Cfn::Resource::...->AttributeList>
1718              
1719             =head3 supported_regions
1720              
1721             Returns an ArrayRef of the AWS regions where the resource can be provisioned.
1722              
1723             Can also be retrieved as a class method C<Cfn::Resource::...->supported_regions>
1724              
1725             =head3 DependsOnList
1726              
1727             Returns a list of dependencies from the DependsOn attribute (it doesn't matter
1728             if the DependsOn attribute is a String or an ArrayRef of Strings.
1729              
1730             my @deps = $cfn->Resource('R1')->DependsOnList;
1731              
1732             =head3 hasAttribute($attribute)
1733              
1734             Returns true if the specified attribute is in the C<AttributeList>. Note that some resources
1735             (AWS::CloudFormation::CustomResource) can return true for values that are not in AttributeList
1736              
1737             =head3 as_hashref
1738              
1739             Like C<Cfn::Values>, as_hashref returns a HashRef representation of the object ready
1740             for transforming to JSON.
1741              
1742             =head1 Cfn::Resource::Properties
1743              
1744             A base class for the objects that the C<Properties> attribute of C<Cfn::Resource>s hold.
1745             Subclasses of C<Cfn::Resource::Properties> are used to validate and represent the properties
1746             of resources inside the object model. See L<Cfn::Resource::Properties::AWS::IAM::User> for
1747             an example.
1748              
1749             Each subclass of C<Cfn::Resource::Properties> has to have attributes to hold the values of
1750             the properties of the resource it represents.
1751              
1752             =head1 Cfn::Parameter
1753              
1754             Represents a Parameter in a CloudFormation document
1755              
1756             my $cfn = Cfn->new;
1757             $cfn->addParameter('P1', 'String', Default => 5);
1758             $cfn->Parameter('P1')->Default # 5
1759             $cfn->Parameter('P1')->NoEcho # undef
1760              
1761             =head2 Cfn::Parameter Attributes
1762              
1763             =head3 Type
1764              
1765             A string with the type of parameter. Validates that it's a CloudFormation supported parameter type.
1766              
1767             =head3 Default
1768              
1769             Holds the default value for the parameter
1770              
1771             =head3 NoEcho
1772              
1773             Holds the NoEcho property of the parameter
1774              
1775             =head3 AllowedValues
1776              
1777             An ArrayRef of the allowed values of the parameter
1778              
1779             =head3 AllowedPattern
1780              
1781             A String holding the pattern that the value of this parameter can take
1782              
1783             =head3 MaxLength, MinLength, MaxValue, MinValue
1784              
1785             Values holding the MaxLength, MinLength, MaxValue, MinValue of the parameter
1786              
1787             =head3 Description
1788              
1789             A string description of the parameter
1790              
1791             =head3 ConstraintDescription
1792              
1793             A string description of the constraint of the parameter
1794              
1795             =head1 Cfn::Mapping
1796              
1797             This object represents the value of the C<Mappings> key in a CloudFormation
1798             document. It has a C<Map> attribute to hold the Mappings in the CloudFormation
1799             document.
1800              
1801             =head1 Cfn::Output
1802              
1803             Represents an output object in a CloudFormation document
1804              
1805             =head2 Attributes for Cfn::Output objects
1806              
1807             "Outputs": {
1808             "Output1": {
1809             "Value": { "Ref": "Instance" }
1810             }
1811             }
1812              
1813             =head3 Value
1814              
1815             Holds the Value key of an output. Is a C<Cfn::Value>
1816              
1817             =head3 Description
1818              
1819             Holds a String with the descrption of the output
1820              
1821             =head3 Condition
1822              
1823             Holds a String with the condition of the output
1824              
1825             =head3 Export
1826              
1827             Holds a HashRef with the export definition of the object
1828              
1829             =head2 Methods for Cfn::Output objects
1830              
1831             =head3 as_hashref
1832              
1833             Returns a HashRef representation of the output that is convertible to JSON
1834              
1835             =head1 STABILITY
1836              
1837             YAML support is recent, and due to the still evolving YAML::PP module, may break
1838             (altough the tests are there to detect that). This distribution will try to keep up
1839             as hard as it can with latest YAML::PP developments.
1840              
1841             =head1 SEE ALSO
1842              
1843             L<https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html>
1844              
1845             This module kind of resembles troposphere (python): L<https://github.com/cloudtools/troposphere>.
1846              
1847             =head1 CLI utils
1848              
1849             This distribution includes a series of CLI utilities to help you with CloudFormation:
1850              
1851             =head2 cfn_list_resources [STRING]
1852              
1853             Lists all the resources supported by Cfn. If a string is specified, will filter the ones matching
1854             the STRING.
1855              
1856             =head2 cfn_region_matrix
1857              
1858             Displays a table of what resource types are supported in each region
1859              
1860             =head2 cfn_region_compatibility FILE
1861              
1862             Takes a cloudformation template and calculates in what regions it will be deployable
1863              
1864             =head2 cfn_resource_properties RESOURCE
1865              
1866             Outputs information about a resource type: properties accessible via Fn::GetAtt, region availability
1867             and it's whole property structure.
1868              
1869             =head1 AUTHOR
1870              
1871             Jose Luis Martinez
1872             CAPSiDE
1873             jlmartinez@capside.com
1874              
1875             =head1 Contributions
1876              
1877             Thanks to Sergi Pruneda, Miquel Ruiz, Luis Alberto Gimenez, Eleatzar Colomer, Oriol Soriano,
1878             Roi Vazquez for years of work on this module.
1879              
1880             TINITA for helping make the YAML support possible. First for the YAML::PP module, which is the only
1881             Perl module to support sufficiently modern YAML features, and also for helping me in the use of
1882             YAML::PP.
1883              
1884             =head1 BUGS and SOURCE
1885              
1886             The source code is located here: L<https://github.com/pplu/cfn-perl>
1887              
1888             Please report bugs to: L<https://github.com/pplu/cfn-perl/issues>
1889              
1890             =head1 COPYRIGHT and LICENSE
1891              
1892             Copyright (c) 2013 by CAPSiDE
1893             This code is distributed under the Apache 2 License. The full text of the
1894             license can be found in the LICENSE file included with this module.
1895              
1896             =cut