44package automation
55
66import (
7+ "context"
78 "errors"
89 "fmt"
910 "log"
@@ -22,9 +23,9 @@ import (
2223
2324func resourceAutomationModule () * pluginsdk.Resource {
2425 return & pluginsdk.Resource {
25- Create : resourceAutomationModuleCreateUpdate ,
26+ Create : resourceAutomationModuleCreate ,
2627 Read : resourceAutomationModuleRead ,
27- Update : resourceAutomationModuleCreateUpdate ,
28+ Update : resourceAutomationModuleUpdate ,
2829 Delete : resourceAutomationModuleDelete ,
2930
3031 Importer : pluginsdk .ImporterValidatingResourceId (func (id string ) error {
@@ -91,29 +92,27 @@ func resourceAutomationModule() *pluginsdk.Resource {
9192 }
9293}
9394
94- func resourceAutomationModuleCreateUpdate (d * pluginsdk.ResourceData , meta interface {}) error {
95+ func resourceAutomationModuleCreate (d * pluginsdk.ResourceData , meta interface {}) error {
9596 client := meta .(* clients.Client ).Automation .Module
9697 subscriptionId := meta .(* clients.Client ).Account .SubscriptionId
97- ctx , cancel := timeouts .ForCreateUpdate (meta .(* clients.Client ).StopContext , d )
98+ ctx , cancel := timeouts .ForCreate (meta .(* clients.Client ).StopContext , d )
9899 defer cancel ()
99100
100101 log .Printf ("[INFO] preparing arguments for AzureRM Automation Module creation." )
101102
102103 id := module .NewModuleID (subscriptionId , d .Get ("resource_group_name" ).(string ), d .Get ("automation_account_name" ).(string ), d .Get ("name" ).(string ))
103104
104- if d .IsNewResource () {
105- existing , err := client .Get (ctx , id )
106- if err != nil {
107- if ! response .WasNotFound (existing .HttpResponse ) {
108- return fmt .Errorf ("checking for presence of existing %s: %s" , id , err )
109- }
105+ existing , err := client .Get (ctx , id )
106+ if err != nil {
107+ if ! response .WasNotFound (existing .HttpResponse ) {
108+ return fmt .Errorf ("checking for presence of existing %s: %s" , id , err )
110109 }
110+ }
111111
112- // for existing global module do update instead of raising ImportAsExistsError
113- isGlobal := existing .Model != nil && existing .Model .Properties != nil && existing .Model .Properties .IsGlobal != nil && * existing .Model .Properties .IsGlobal
114- if ! response .WasNotFound (existing .HttpResponse ) && ! isGlobal {
115- return tf .ImportAsExistsError ("azurerm_automation_module" , id .ID ())
116- }
112+ // for existing global module do update instead of raising ImportAsExistsError
113+ isGlobal := existing .Model != nil && existing .Model .Properties != nil && existing .Model .Properties .IsGlobal != nil && * existing .Model .Properties .IsGlobal
114+ if ! response .WasNotFound (existing .HttpResponse ) && ! isGlobal {
115+ return tf .ImportAsExistsError ("azurerm_automation_module" , id .ID ())
117116 }
118117
119118 parameters := module.ModuleCreateOrUpdateParameters {
@@ -123,63 +122,59 @@ func resourceAutomationModuleCreateUpdate(d *pluginsdk.ResourceData, meta interf
123122 }
124123
125124 if _ , err := client .CreateOrUpdate (ctx , id , parameters ); err != nil {
125+ return fmt .Errorf ("creating %s: %+v" , id , err )
126+ }
127+
128+ if err := waitForModuleProvisioningCompletion (ctx , client , id , d .Timeout (pluginsdk .TimeoutCreate )); err != nil {
126129 return err
127130 }
128131
129- // the API returns 'done' but it's not actually finished provisioning yet
130- // tracking issue: https://github.com/Azure/azure-rest-api-specs/pull/25435
131- stateConf := & pluginsdk.StateChangeConf {
132- Pending : []string {
133- string (module .ModuleProvisioningStateActivitiesStored ),
134- string (module .ModuleProvisioningStateConnectionTypeImported ),
135- string (module .ModuleProvisioningStateContentDownloaded ),
136- string (module .ModuleProvisioningStateContentRetrieved ),
137- string (module .ModuleProvisioningStateContentStored ),
138- string (module .ModuleProvisioningStateContentValidated ),
139- string (module .ModuleProvisioningStateCreated ),
140- string (module .ModuleProvisioningStateCreating ),
141- string (module .ModuleProvisioningStateModuleDataStored ),
142- string (module .ModuleProvisioningStateModuleImportRunbookComplete ),
143- string (module .ModuleProvisioningStateRunningImportModuleRunbook ),
144- string (module .ModuleProvisioningStateStartingImportModuleRunbook ),
145- string (module .ModuleProvisioningStateUpdating ),
146- },
147- Target : []string {
148- string (module .ModuleProvisioningStateSucceeded ),
149- },
150- MinTimeout : 30 * time .Second ,
151- Refresh : func () (interface {}, string , error ) {
152- resp , err2 := client .Get (ctx , id )
153- if err2 != nil {
154- return resp , "Error" , fmt .Errorf ("retrieving %s: %+v" , id , err2 )
155- }
132+ d .SetId (id .ID ())
156133
157- provisioningState := "Unknown"
158- if model := resp .Model ; model != nil {
159- if props := model .Properties ; props != nil {
160- if props .ProvisioningState != nil {
161- provisioningState = string (* props .ProvisioningState )
162- }
163- if props .Error != nil && props .Error .Message != nil && * props .Error .Message != "" {
164- return resp , provisioningState , errors .New (* props .Error .Message )
165- }
166- return resp , provisioningState , nil
167- }
168- }
169- return resp , provisioningState , nil
170- },
134+ return resourceAutomationModuleRead (d , meta )
135+ }
136+
137+ func resourceAutomationModuleUpdate (d * pluginsdk.ResourceData , meta interface {}) error {
138+ client := meta .(* clients.Client ).Automation .Module
139+ ctx , cancel := timeouts .ForUpdate (meta .(* clients.Client ).StopContext , d )
140+ defer cancel ()
141+
142+ id , err := module .ParseModuleID (d .Id ())
143+ if err != nil {
144+ return err
171145 }
172- if d . IsNewResource () {
173- stateConf . Timeout = d . Timeout ( pluginsdk . TimeoutCreate )
174- } else {
175- stateConf . Timeout = d . Timeout ( pluginsdk . TimeoutUpdate )
146+
147+ existing , err := client . Get ( ctx , * id )
148+ if err != nil {
149+ return fmt . Errorf ( "retrieving existing %s: %+v" , * id , err )
176150 }
177151
178- if _ , err := stateConf .WaitForStateContext (ctx ); err != nil {
179- return fmt .Errorf ("waiting for %s to finish provisioning: %+v" , id , err )
152+ // Start from the existing content link so that fields managed via
153+ // ignore_changes retain their server-side value.
154+ var contentLink * module.ContentLink
155+ if existing .Model != nil && existing .Model .Properties != nil {
156+ contentLink = existing .Model .Properties .ContentLink
180157 }
181158
182- d .SetId (id .ID ())
159+ if d .HasChange ("module_link" ) {
160+ expanded := expandModuleLink (d )
161+ contentLink = & expanded
162+ }
163+
164+ parameters := module.ModuleUpdateParameters {
165+ Name : & id .ModuleName ,
166+ Properties : & module.ModuleUpdateProperties {
167+ ContentLink : contentLink ,
168+ },
169+ }
170+
171+ if _ , err := client .Update (ctx , * id , parameters ); err != nil {
172+ return fmt .Errorf ("updating %s: %+v" , * id , err )
173+ }
174+
175+ if err := waitForModuleProvisioningCompletion (ctx , client , * id , d .Timeout (pluginsdk .TimeoutUpdate )); err != nil {
176+ return err
177+ }
183178
184179 return resourceAutomationModuleRead (d , meta )
185180}
@@ -255,3 +250,57 @@ func expandModuleLink(d *pluginsdk.ResourceData) module.ContentLink {
255250 Uri : & uri ,
256251 }
257252}
253+
254+ // waitForModuleProvisioningCompletion polls the module until it reaches the Succeeded
255+ // provisioning state. The API returns 'done' before provisioning is actually complete.
256+ // Tracking issue: https://github.com/Azure/azure-rest-api-specs/pull/25435
257+ func waitForModuleProvisioningCompletion (ctx context.Context , client * module.ModuleClient , id module.ModuleId , timeout time.Duration ) error {
258+ stateConf := & pluginsdk.StateChangeConf {
259+ Pending : []string {
260+ string (module .ModuleProvisioningStateActivitiesStored ),
261+ string (module .ModuleProvisioningStateConnectionTypeImported ),
262+ string (module .ModuleProvisioningStateContentDownloaded ),
263+ string (module .ModuleProvisioningStateContentRetrieved ),
264+ string (module .ModuleProvisioningStateContentStored ),
265+ string (module .ModuleProvisioningStateContentValidated ),
266+ string (module .ModuleProvisioningStateCreated ),
267+ string (module .ModuleProvisioningStateCreating ),
268+ string (module .ModuleProvisioningStateModuleDataStored ),
269+ string (module .ModuleProvisioningStateModuleImportRunbookComplete ),
270+ string (module .ModuleProvisioningStateRunningImportModuleRunbook ),
271+ string (module .ModuleProvisioningStateStartingImportModuleRunbook ),
272+ string (module .ModuleProvisioningStateUpdating ),
273+ },
274+ Target : []string {
275+ string (module .ModuleProvisioningStateSucceeded ),
276+ },
277+ MinTimeout : 30 * time .Second ,
278+ Timeout : timeout ,
279+ Refresh : func () (interface {}, string , error ) {
280+ resp , err := client .Get (ctx , id )
281+ if err != nil {
282+ return resp , "Error" , fmt .Errorf ("retrieving %s: %+v" , id , err )
283+ }
284+
285+ provisioningState := "Unknown"
286+ if model := resp .Model ; model != nil {
287+ if props := model .Properties ; props != nil {
288+ if props .ProvisioningState != nil {
289+ provisioningState = string (* props .ProvisioningState )
290+ }
291+ if props .Error != nil && props .Error .Message != nil && * props .Error .Message != "" {
292+ return resp , provisioningState , errors .New (* props .Error .Message )
293+ }
294+ return resp , provisioningState , nil
295+ }
296+ }
297+ return resp , provisioningState , nil
298+ },
299+ }
300+
301+ if _ , err := stateConf .WaitForStateContext (ctx ); err != nil {
302+ return fmt .Errorf ("waiting for %s to finish provisioning: %+v" , id , err )
303+ }
304+
305+ return nil
306+ }
0 commit comments