@@ -133,6 +133,37 @@ func resourceCloudStackTemplate() *schema.Resource {
133133 ForceNew : true ,
134134 },
135135
136+ "userdata_link" : {
137+ Type : schema .TypeList ,
138+ Optional : true ,
139+ MaxItems : 1 ,
140+ Elem : & schema.Resource {
141+ Schema : map [string ]* schema.Schema {
142+ "userdata_id" : {
143+ Type : schema .TypeString ,
144+ Required : true ,
145+ Description : "The ID of the user data to link to the template." ,
146+ },
147+ "userdata_policy" : {
148+ Type : schema .TypeString ,
149+ Optional : true ,
150+ Default : "ALLOWOVERRIDE" ,
151+ Description : "Override policy of the userdata. Possible values: ALLOWOVERRIDE, APPEND, DENYOVERRIDE. Default: ALLOWOVERRIDE" ,
152+ },
153+ "userdata_name" : {
154+ Type : schema .TypeString ,
155+ Computed : true ,
156+ Description : "The name of the linked user data." ,
157+ },
158+ "userdata_params" : {
159+ Type : schema .TypeString ,
160+ Computed : true ,
161+ Description : "The parameters of the linked user data." ,
162+ },
163+ },
164+ },
165+ },
166+
136167 "tags" : tagsSchema (),
137168 },
138169 }
@@ -224,6 +255,11 @@ func resourceCloudStackTemplateCreate(d *schema.ResourceData, meta interface{})
224255 return fmt .Errorf ("Error setting tags on the template %s: %s" , name , err )
225256 }
226257
258+ // Link userdata if specified
259+ if err = linkUserdataToTemplate (cs , d , r .RegisterTemplate [0 ].Id ); err != nil {
260+ return fmt .Errorf ("Error linking userdata to template %s: %s" , name , err )
261+ }
262+
227263 // Wait until the template is ready to use, or timeout with an error...
228264 currentTime := time .Now ().Unix ()
229265 timeout := int64 (d .Get ("is_ready_timeout" ).(int ))
@@ -300,6 +336,11 @@ func resourceCloudStackTemplateRead(d *schema.ResourceData, meta interface{}) er
300336 setValueOrID (d , "project" , t .Project , t .Projectid )
301337 setValueOrID (d , "zone" , t .Zonename , t .Zoneid )
302338
339+ // Read userdata link information
340+ if err := readUserdataFromTemplate (d , t ); err != nil {
341+ return fmt .Errorf ("Error reading userdata link from template: %s" , err )
342+ }
343+
303344 return nil
304345}
305346
@@ -349,6 +390,12 @@ func resourceCloudStackTemplateUpdate(d *schema.ResourceData, meta interface{})
349390 }
350391 }
351392
393+ if d .HasChange ("userdata_link" ) {
394+ if err := updateUserdataLink (cs , d ); err != nil {
395+ return fmt .Errorf ("Error updating userdata link for template %s: %s" , name , err )
396+ }
397+ }
398+
352399 return resourceCloudStackTemplateRead (d , meta )
353400}
354401
@@ -383,3 +430,105 @@ func verifyTemplateParams(d *schema.ResourceData) error {
383430
384431 return nil
385432}
433+
434+ func linkUserdataToTemplate (cs * cloudstack.CloudStackClient , d * schema.ResourceData , templateID string ) error {
435+ userdataLinks := d .Get ("userdata_link" ).([]interface {})
436+ if len (userdataLinks ) == 0 {
437+ return nil
438+ }
439+
440+ userdataLink := userdataLinks [0 ].(map [string ]interface {})
441+
442+ p := cs .Template .NewLinkUserDataToTemplateParams ()
443+ p .SetTemplateid (templateID )
444+ p .SetUserdataid (userdataLink ["userdata_id" ].(string ))
445+
446+ if policy , ok := userdataLink ["userdata_policy" ].(string ); ok && policy != "" {
447+ p .SetUserdatapolicy (policy )
448+ }
449+
450+ _ , err := cs .Template .LinkUserDataToTemplate (p )
451+ return err
452+ }
453+
454+ func readUserdataFromTemplate (d * schema.ResourceData , template * cloudstack.Template ) error {
455+ if template .Userdataid == "" {
456+ d .Set ("userdata_link" , []interface {}{})
457+ return nil
458+ }
459+
460+ userdataLink := map [string ]interface {}{
461+ "userdata_id" : template .Userdataid ,
462+ "userdata_name" : template .Userdataname ,
463+ "userdata_params" : template .Userdataparams ,
464+ }
465+
466+ if existingLinks := d .Get ("userdata_link" ).([]interface {}); len (existingLinks ) > 0 {
467+ if existingLink , ok := existingLinks [0 ].(map [string ]interface {}); ok {
468+ if policy , exists := existingLink ["userdata_policy" ]; exists {
469+ userdataLink ["userdata_policy" ] = policy
470+ }
471+ }
472+ }
473+
474+ d .Set ("userdata_link" , []interface {}{userdataLink })
475+ return nil
476+ }
477+
478+ func updateUserdataLink (cs * cloudstack.CloudStackClient , d * schema.ResourceData ) error {
479+ templateID := d .Id ()
480+
481+ oldLinks , newLinks := d .GetChange ("userdata_link" )
482+ oldLinksSlice := oldLinks .([]interface {})
483+ newLinksSlice := newLinks .([]interface {})
484+
485+ // Check if we're removing userdata link (had one before, now empty)
486+ if len (oldLinksSlice ) > 0 && len (newLinksSlice ) == 0 {
487+ unlinkP := cs .Template .NewLinkUserDataToTemplateParams ()
488+ unlinkP .SetTemplateid (templateID )
489+
490+ _ , err := cs .Template .LinkUserDataToTemplate (unlinkP )
491+ if err != nil {
492+ return fmt .Errorf ("Error unlinking userdata from template: %s" , err )
493+ }
494+ log .Printf ("[DEBUG] Unlinked userdata from template: %s" , templateID )
495+ return nil
496+ }
497+
498+ if len (newLinksSlice ) > 0 {
499+ newLink := newLinksSlice [0 ].(map [string ]interface {})
500+
501+ if len (oldLinksSlice ) > 0 {
502+ oldLink := oldLinksSlice [0 ].(map [string ]interface {})
503+
504+ if oldLink ["userdata_id" ].(string ) == newLink ["userdata_id" ].(string ) {
505+ oldPolicy := ""
506+ newPolicy := ""
507+
508+ if p , ok := oldLink ["userdata_policy" ].(string ); ok {
509+ oldPolicy = p
510+ }
511+ if p , ok := newLink ["userdata_policy" ].(string ); ok {
512+ newPolicy = p
513+ }
514+
515+ if oldPolicy == newPolicy {
516+ log .Printf ("[DEBUG] Userdata link unchanged, skipping API call" )
517+ return nil
518+ }
519+ }
520+
521+ unlinkP := cs .Template .NewLinkUserDataToTemplateParams ()
522+ unlinkP .SetTemplateid (templateID )
523+
524+ _ , err := cs .Template .LinkUserDataToTemplate (unlinkP )
525+ if err != nil {
526+ log .Printf ("[DEBUG] Error unlinking existing userdata (this may be normal): %s" , err )
527+ }
528+ }
529+
530+ return linkUserdataToTemplate (cs , d , templateID )
531+ }
532+
533+ return nil
534+ }
0 commit comments