2016-03-21 16:49:48 +01:00
package azurerm
2016-06-02 02:32:25 +02:00
import (
"encoding/json"
"fmt"
"log"
"net/http"
2017-05-02 21:13:25 +02:00
"strconv"
2016-06-02 02:32:25 +02:00
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceArmTemplateDeployment ( ) * schema . Resource {
return & schema . Resource {
Create : resourceArmTemplateDeploymentCreate ,
Read : resourceArmTemplateDeploymentRead ,
Update : resourceArmTemplateDeploymentCreate ,
Delete : resourceArmTemplateDeploymentDelete ,
Schema : map [ string ] * schema . Schema {
"name" : {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
"resource_group_name" : {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
"template_body" : {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
StateFunc : normalizeJson ,
} ,
"parameters" : {
Type : schema . TypeMap ,
Optional : true ,
} ,
"outputs" : {
Type : schema . TypeMap ,
Computed : true ,
} ,
"deployment_mode" : {
Type : schema . TypeString ,
Required : true ,
} ,
} ,
}
}
func resourceArmTemplateDeploymentCreate ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * ArmClient )
deployClient := client . deploymentsClient
name := d . Get ( "name" ) . ( string )
resGroup := d . Get ( "resource_group_name" ) . ( string )
deploymentMode := d . Get ( "deployment_mode" ) . ( string )
log . Printf ( "[INFO] preparing arguments for Azure ARM Template Deployment creation." )
properties := resources . DeploymentProperties {
Mode : resources . DeploymentMode ( deploymentMode ) ,
}
if v , ok := d . GetOk ( "parameters" ) ; ok {
params := v . ( map [ string ] interface { } )
newParams := make ( map [ string ] interface { } , len ( params ) )
for key , val := range params {
newParams [ key ] = struct {
Value interface { }
} {
Value : val ,
}
}
properties . Parameters = & newParams
}
if v , ok := d . GetOk ( "template_body" ) ; ok {
template , err := expandTemplateBody ( v . ( string ) )
if err != nil {
return err
}
properties . Template = & template
}
deployment := resources . Deployment {
Properties : & properties ,
}
2017-06-01 11:18:22 +02:00
_ , error := deployClient . CreateOrUpdate ( resGroup , name , deployment , make ( chan struct { } ) )
err := <- error
2016-06-02 02:32:25 +02:00
if err != nil {
2016-07-14 17:06:58 +02:00
return fmt . Errorf ( "Error creating deployment: %s" , err )
2016-06-02 02:32:25 +02:00
}
read , err := deployClient . Get ( resGroup , name )
if err != nil {
return err
}
if read . ID == nil {
return fmt . Errorf ( "Cannot read Template Deployment %s (resource group %s) ID" , name , resGroup )
}
d . SetId ( * read . ID )
log . Printf ( "[DEBUG] Waiting for Template Deployment (%s) to become available" , name )
stateConf := & resource . StateChangeConf {
Pending : [ ] string { "creating" , "updating" , "accepted" , "running" } ,
Target : [ ] string { "succeeded" } ,
Refresh : templateDeploymentStateRefreshFunc ( client , resGroup , name ) ,
Timeout : 40 * time . Minute ,
}
if _ , err := stateConf . WaitForState ( ) ; err != nil {
return fmt . Errorf ( "Error waiting for Template Deployment (%s) to become available: %s" , name , err )
}
return resourceArmTemplateDeploymentRead ( d , meta )
}
func resourceArmTemplateDeploymentRead ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * ArmClient )
deployClient := client . deploymentsClient
id , err := parseAzureResourceID ( d . Id ( ) )
if err != nil {
return err
}
resGroup := id . ResourceGroup
name := id . Path [ "deployments" ]
if name == "" {
name = id . Path [ "Deployments" ]
}
resp , err := deployClient . Get ( resGroup , name )
provider/azurerm: Reordering the checks after an Azure API Get
We are receiving suggestions of a panic as follows:
```
2016/09/01 07:21:55 [DEBUG] plugin: terraform: panic: runtime error: invalid memory address or nil pointer dereference
2016/09/01 07:21:55 [DEBUG] plugin: terraform: [signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0xa3170f]
2016/09/01 07:21:55 [DEBUG] plugin: terraform:
2016/09/01 07:21:55 [DEBUG] plugin: terraform: goroutine 114 [running]:
2016/09/01 07:21:55 [DEBUG] plugin: terraform: panic(0x27f4e60, 0xc4200100e0)
2016/09/01 07:21:55 [DEBUG] plugin: terraform: /opt/go/src/runtime/panic.go:500 +0x1a1
2016/09/01 07:21:55 [DEBUG] plugin: terraform: github.com/hashicorp/terraform/builtin/providers/azurerm.resourceArmVirtualMachineRead(0xc4206d8060, 0x2995620, 0xc4204d0000, 0x0, 0x17)
2016/09/01 07:21:55 [DEBUG] plugin: terraform: /opt/gopath/src/github.com/hashicorp/terraform/builtin/providers/azurerm/resource_arm_virtual_machine.go:488 +0x1ff
2016/09/01 07:21:55 [DEBUG] plugin: terraform: github.com/hashicorp/terraform/helper/schema.(*Resource).Refresh(0xc420017a40, 0xc42040c780, 0x2995620, 0xc4204d0000, 0xc42019c990, 0x1, 0x0)
```
This is because the code is as follows:
```
resp, err := client.Get(resGroup, vnetName, name)
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("Error making Read request on Azure virtual network peering %s: %s", name, err)
}
```
When a request throws an error, the response object isn't valid. Therefore, we need to flip that code to check the error first
```
resp, err := client.Get(resGroup, vnetName, name)
if err != nil {
return fmt.Errorf("Error making Read request on Azure virtual network peering %s: %s", name, err)
}
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
```
2016-09-01 16:31:42 +02:00
if err != nil {
2016-09-30 18:57:59 +02:00
if resp . StatusCode == http . StatusNotFound {
d . SetId ( "" )
return nil
}
provider/azurerm: Reordering the checks after an Azure API Get
We are receiving suggestions of a panic as follows:
```
2016/09/01 07:21:55 [DEBUG] plugin: terraform: panic: runtime error: invalid memory address or nil pointer dereference
2016/09/01 07:21:55 [DEBUG] plugin: terraform: [signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0xa3170f]
2016/09/01 07:21:55 [DEBUG] plugin: terraform:
2016/09/01 07:21:55 [DEBUG] plugin: terraform: goroutine 114 [running]:
2016/09/01 07:21:55 [DEBUG] plugin: terraform: panic(0x27f4e60, 0xc4200100e0)
2016/09/01 07:21:55 [DEBUG] plugin: terraform: /opt/go/src/runtime/panic.go:500 +0x1a1
2016/09/01 07:21:55 [DEBUG] plugin: terraform: github.com/hashicorp/terraform/builtin/providers/azurerm.resourceArmVirtualMachineRead(0xc4206d8060, 0x2995620, 0xc4204d0000, 0x0, 0x17)
2016/09/01 07:21:55 [DEBUG] plugin: terraform: /opt/gopath/src/github.com/hashicorp/terraform/builtin/providers/azurerm/resource_arm_virtual_machine.go:488 +0x1ff
2016/09/01 07:21:55 [DEBUG] plugin: terraform: github.com/hashicorp/terraform/helper/schema.(*Resource).Refresh(0xc420017a40, 0xc42040c780, 0x2995620, 0xc4204d0000, 0xc42019c990, 0x1, 0x0)
```
This is because the code is as follows:
```
resp, err := client.Get(resGroup, vnetName, name)
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("Error making Read request on Azure virtual network peering %s: %s", name, err)
}
```
When a request throws an error, the response object isn't valid. Therefore, we need to flip that code to check the error first
```
resp, err := client.Get(resGroup, vnetName, name)
if err != nil {
return fmt.Errorf("Error making Read request on Azure virtual network peering %s: %s", name, err)
}
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
```
2016-09-01 16:31:42 +02:00
return fmt . Errorf ( "Error making Read request on Azure RM Template Deployment %s: %s" , name , err )
}
2016-06-02 02:32:25 +02:00
var outputs map [ string ] string
if resp . Properties . Outputs != nil && len ( * resp . Properties . Outputs ) > 0 {
outputs = make ( map [ string ] string )
for key , output := range * resp . Properties . Outputs {
2017-04-04 22:14:58 +02:00
log . Printf ( "[DEBUG] Processing deployment output %s" , key )
2016-06-02 02:32:25 +02:00
outputMap := output . ( map [ string ] interface { } )
outputValue , ok := outputMap [ "value" ]
if ! ok {
2017-04-21 01:27:36 +02:00
log . Printf ( "[DEBUG] No value - skipping" )
2016-06-02 02:32:25 +02:00
continue
}
2017-04-21 01:27:36 +02:00
outputType , ok := outputMap [ "type" ]
if ! ok {
log . Printf ( "[DEBUG] No type - skipping" )
continue
}
var outputValueString string
switch strings . ToLower ( outputType . ( string ) ) {
case "bool" :
2017-05-02 21:13:25 +02:00
outputValueString = strconv . FormatBool ( outputValue . ( bool ) )
2017-04-04 22:14:58 +02:00
2017-04-21 01:27:36 +02:00
case "string" :
outputValueString = outputValue . ( string )
case "int" :
outputValueString = fmt . Sprint ( outputValue )
2017-04-04 22:14:58 +02:00
default :
2017-04-21 01:27:36 +02:00
log . Printf ( "[WARN] Ignoring output %s: Outputs of type %s are not currently supported in azurerm_template_deployment." ,
2017-04-04 22:14:58 +02:00
key , outputType )
continue
}
2017-04-21 01:27:36 +02:00
outputs [ key ] = outputValueString
2016-06-02 02:32:25 +02:00
}
}
2017-04-04 22:14:58 +02:00
return d . Set ( "outputs" , outputs )
2016-06-02 02:32:25 +02:00
}
func resourceArmTemplateDeploymentDelete ( d * schema . ResourceData , meta interface { } ) error {
client := meta . ( * ArmClient )
deployClient := client . deploymentsClient
id , err := parseAzureResourceID ( d . Id ( ) )
if err != nil {
return err
}
resGroup := id . ResourceGroup
name := id . Path [ "deployments" ]
if name == "" {
name = id . Path [ "Deployments" ]
}
2017-06-01 11:18:22 +02:00
_ , error := deployClient . Delete ( resGroup , name , make ( chan struct { } ) )
err = <- error
return err
2016-06-02 02:32:25 +02:00
}
func expandTemplateBody ( template string ) ( map [ string ] interface { } , error ) {
var templateBody map [ string ] interface { }
err := json . Unmarshal ( [ ] byte ( template ) , & templateBody )
if err != nil {
return nil , fmt . Errorf ( "Error Expanding the template_body for Azure RM Template Deployment" )
}
return templateBody , nil
}
func normalizeJson ( jsonString interface { } ) string {
if jsonString == nil || jsonString == "" {
return ""
}
var j interface { }
err := json . Unmarshal ( [ ] byte ( jsonString . ( string ) ) , & j )
if err != nil {
return fmt . Sprintf ( "Error parsing JSON: %s" , err )
}
b , _ := json . Marshal ( j )
return string ( b [ : ] )
}
func templateDeploymentStateRefreshFunc ( client * ArmClient , resourceGroupName string , name string ) resource . StateRefreshFunc {
return func ( ) ( interface { } , string , error ) {
res , err := client . deploymentsClient . Get ( resourceGroupName , name )
if err != nil {
return nil , "" , fmt . Errorf ( "Error issuing read request in templateDeploymentStateRefreshFunc to Azure ARM for Template Deployment '%s' (RG: '%s'): %s" , name , resourceGroupName , err )
}
return res , strings . ToLower ( * res . Properties . ProvisioningState ) , nil
}
}