@@ -358,6 +358,155 @@ func decodeResourceBlock(block *hcl.Block, override bool) (*Resource, hcl.Diagno
358358 return r , diags
359359}
360360
361+ func decodeEphemeralBlock (block * hcl.Block , override bool ) (* Resource , hcl.Diagnostics ) {
362+ var diags hcl.Diagnostics
363+ r := & Resource {
364+ Mode : addrs .EphemeralResourceMode ,
365+ Type : block .Labels [0 ],
366+ Name : block .Labels [1 ],
367+ DeclRange : block .DefRange ,
368+ TypeRange : block .LabelRanges [0 ],
369+ }
370+
371+ content , remain , moreDiags := block .Body .PartialContent (ephemeralBlockSchema )
372+ diags = append (diags , moreDiags ... )
373+ r .Config = remain
374+
375+ if ! hclsyntax .ValidIdentifier (r .Type ) {
376+ diags = append (diags , & hcl.Diagnostic {
377+ Severity : hcl .DiagError ,
378+ Summary : "Invalid ephemeral resource type" ,
379+ Detail : badIdentifierDetail ,
380+ Subject : & block .LabelRanges [0 ],
381+ })
382+ }
383+ if ! hclsyntax .ValidIdentifier (r .Name ) {
384+ diags = append (diags , & hcl.Diagnostic {
385+ Severity : hcl .DiagError ,
386+ Summary : "Invalid ephemeral resource name" ,
387+ Detail : badIdentifierDetail ,
388+ Subject : & block .LabelRanges [1 ],
389+ })
390+ }
391+
392+ if attr , exists := content .Attributes ["count" ]; exists {
393+ r .Count = attr .Expr
394+ }
395+
396+ if attr , exists := content .Attributes ["for_each" ]; exists {
397+ r .ForEach = attr .Expr
398+ // Cannot have count and for_each on the same ephemeral block
399+ if r .Count != nil {
400+ diags = append (diags , & hcl.Diagnostic {
401+ Severity : hcl .DiagError ,
402+ Summary : `Invalid combination of "count" and "for_each"` ,
403+ Detail : `The "count" and "for_each" meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be created.` ,
404+ Subject : & attr .NameRange ,
405+ })
406+ }
407+ }
408+
409+ if attr , exists := content .Attributes ["provider" ]; exists {
410+ var providerDiags hcl.Diagnostics
411+ r .ProviderConfigRef , providerDiags = decodeProviderConfigRef (attr .Expr , "provider" )
412+ diags = append (diags , providerDiags ... )
413+ }
414+
415+ if attr , exists := content .Attributes ["depends_on" ]; exists {
416+ deps , depsDiags := DecodeDependsOn (attr )
417+ diags = append (diags , depsDiags ... )
418+ r .DependsOn = append (r .DependsOn , deps ... )
419+ }
420+
421+ var seenEscapeBlock * hcl.Block
422+ var seenLifecycle * hcl.Block
423+ for _ , block := range content .Blocks {
424+ switch block .Type {
425+
426+ case "_" :
427+ if seenEscapeBlock != nil {
428+ diags = append (diags , & hcl.Diagnostic {
429+ Severity : hcl .DiagError ,
430+ Summary : "Duplicate escaping block" ,
431+ Detail : fmt .Sprintf (
432+ "The special block type \" _\" can be used to force particular arguments to be interpreted as resource-type-specific rather than as meta-arguments, but each data block can have only one such block. The first escaping block was at %s." ,
433+ seenEscapeBlock .DefRange ,
434+ ),
435+ Subject : & block .DefRange ,
436+ })
437+ continue
438+ }
439+ seenEscapeBlock = block
440+
441+ // When there's an escaping block its content merges with the
442+ // existing config we extracted earlier, so later decoding
443+ // will see a blend of both.
444+ r .Config = hcl .MergeBodies ([]hcl.Body {r .Config , block .Body })
445+
446+ case "lifecycle" :
447+ if seenLifecycle != nil {
448+ diags = append (diags , & hcl.Diagnostic {
449+ Severity : hcl .DiagError ,
450+ Summary : "Duplicate lifecycle block" ,
451+ Detail : fmt .Sprintf ("This resource already has a lifecycle block at %s." , seenLifecycle .DefRange ),
452+ Subject : block .DefRange .Ptr (),
453+ })
454+ continue
455+ }
456+ seenLifecycle = block
457+
458+ lcContent , lcDiags := block .Body .Content (resourceLifecycleBlockSchema )
459+ diags = append (diags , lcDiags ... )
460+
461+ // All of the attributes defined for resource lifecycle are for
462+ // managed resources only, so we can emit a common error message
463+ // for any given attributes that HCL accepted.
464+ for name , attr := range lcContent .Attributes {
465+ diags = append (diags , & hcl.Diagnostic {
466+ Severity : hcl .DiagError ,
467+ Summary : "Invalid ephemeral resource lifecycle argument" ,
468+ Detail : fmt .Sprintf ("The lifecycle argument %q is defined only for managed resources (\" resource\" blocks), and is not valid for ephemeral resources." , name ),
469+ Subject : attr .NameRange .Ptr (),
470+ })
471+ }
472+
473+ for _ , block := range lcContent .Blocks {
474+ switch block .Type {
475+ case "precondition" , "postcondition" :
476+ cr , moreDiags := decodeCheckRuleBlock (block , override )
477+ diags = append (diags , moreDiags ... )
478+
479+ moreDiags = cr .validateSelfReferences (block .Type , r .Addr ())
480+ diags = append (diags , moreDiags ... )
481+
482+ switch block .Type {
483+ case "precondition" :
484+ r .Preconditions = append (r .Preconditions , cr )
485+ case "postcondition" :
486+ r .Postconditions = append (r .Postconditions , cr )
487+ }
488+ default :
489+ // The cases above should be exhaustive for all block types
490+ // defined in the lifecycle schema, so this shouldn't happen.
491+ panic (fmt .Sprintf ("unexpected lifecycle sub-block type %q" , block .Type ))
492+ }
493+ }
494+
495+ default :
496+ // Any other block types are ones we're reserving for future use,
497+ // but don't have any defined meaning today.
498+ diags = append (diags , & hcl.Diagnostic {
499+ Severity : hcl .DiagError ,
500+ Summary : "Reserved block type name in ephemeral block" ,
501+ Detail : fmt .Sprintf ("The block type name %q is reserved for use by Terraform in a future version." , block .Type ),
502+ Subject : block .TypeRange .Ptr (),
503+ })
504+ }
505+ }
506+
507+ return r , diags
508+ }
509+
361510func decodeDataBlock (block * hcl.Block , override , nested bool ) (* Resource , hcl.Diagnostics ) {
362511 var diags hcl.Diagnostics
363512 r := & Resource {
@@ -783,6 +932,15 @@ var dataBlockSchema = &hcl.BodySchema{
783932 },
784933}
785934
935+ var ephemeralBlockSchema = & hcl.BodySchema {
936+ Attributes : commonResourceAttributes ,
937+ Blocks : []hcl.BlockHeaderSchema {
938+ {Type : "lifecycle" },
939+ {Type : "locals" }, // reserved for future use
940+ {Type : "_" }, // meta-argument escaping block
941+ },
942+ }
943+
786944var resourceLifecycleBlockSchema = & hcl.BodySchema {
787945 // We tell HCL that these elements are all valid for both "resource"
788946 // and "data" lifecycle blocks, but the rules are actually more restrictive
0 commit comments