github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_template_deployment.go (about)

     1  package azurerm
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceArmTemplateDeployment() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceArmTemplateDeploymentCreate,
    20  		Read:   resourceArmTemplateDeploymentRead,
    21  		Update: resourceArmTemplateDeploymentCreate,
    22  		Delete: resourceArmTemplateDeploymentDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": {
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"resource_group_name": {
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  				ForceNew: true,
    35  			},
    36  
    37  			"template_body": {
    38  				Type:      schema.TypeString,
    39  				Optional:  true,
    40  				Computed:  true,
    41  				StateFunc: normalizeJson,
    42  			},
    43  
    44  			"parameters": {
    45  				Type:     schema.TypeMap,
    46  				Optional: true,
    47  			},
    48  
    49  			"outputs": {
    50  				Type:     schema.TypeMap,
    51  				Computed: true,
    52  			},
    53  
    54  			"deployment_mode": {
    55  				Type:     schema.TypeString,
    56  				Required: true,
    57  			},
    58  		},
    59  	}
    60  }
    61  
    62  func resourceArmTemplateDeploymentCreate(d *schema.ResourceData, meta interface{}) error {
    63  	client := meta.(*ArmClient)
    64  	deployClient := client.deploymentsClient
    65  
    66  	name := d.Get("name").(string)
    67  	resGroup := d.Get("resource_group_name").(string)
    68  	deploymentMode := d.Get("deployment_mode").(string)
    69  
    70  	log.Printf("[INFO] preparing arguments for Azure ARM Template Deployment creation.")
    71  	properties := resources.DeploymentProperties{
    72  		Mode: resources.DeploymentMode(deploymentMode),
    73  	}
    74  
    75  	if v, ok := d.GetOk("parameters"); ok {
    76  		params := v.(map[string]interface{})
    77  
    78  		newParams := make(map[string]interface{}, len(params))
    79  		for key, val := range params {
    80  			newParams[key] = struct {
    81  				Value interface{}
    82  			}{
    83  				Value: val,
    84  			}
    85  		}
    86  
    87  		properties.Parameters = &newParams
    88  	}
    89  
    90  	if v, ok := d.GetOk("template_body"); ok {
    91  		template, err := expandTemplateBody(v.(string))
    92  		if err != nil {
    93  			return err
    94  		}
    95  
    96  		properties.Template = &template
    97  	}
    98  
    99  	deployment := resources.Deployment{
   100  		Properties: &properties,
   101  	}
   102  
   103  	_, error := deployClient.CreateOrUpdate(resGroup, name, deployment, make(chan struct{}))
   104  	err := <-error
   105  	if err != nil {
   106  		return fmt.Errorf("Error creating deployment: %s", err)
   107  	}
   108  
   109  	read, err := deployClient.Get(resGroup, name)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	if read.ID == nil {
   114  		return fmt.Errorf("Cannot read Template Deployment %s (resource group %s) ID", name, resGroup)
   115  	}
   116  
   117  	d.SetId(*read.ID)
   118  
   119  	log.Printf("[DEBUG] Waiting for Template Deployment (%s) to become available", name)
   120  	stateConf := &resource.StateChangeConf{
   121  		Pending: []string{"creating", "updating", "accepted", "running"},
   122  		Target:  []string{"succeeded"},
   123  		Refresh: templateDeploymentStateRefreshFunc(client, resGroup, name),
   124  		Timeout: 40 * time.Minute,
   125  	}
   126  	if _, err := stateConf.WaitForState(); err != nil {
   127  		return fmt.Errorf("Error waiting for Template Deployment (%s) to become available: %s", name, err)
   128  	}
   129  
   130  	return resourceArmTemplateDeploymentRead(d, meta)
   131  }
   132  
   133  func resourceArmTemplateDeploymentRead(d *schema.ResourceData, meta interface{}) error {
   134  	client := meta.(*ArmClient)
   135  	deployClient := client.deploymentsClient
   136  
   137  	id, err := parseAzureResourceID(d.Id())
   138  	if err != nil {
   139  		return err
   140  	}
   141  	resGroup := id.ResourceGroup
   142  	name := id.Path["deployments"]
   143  	if name == "" {
   144  		name = id.Path["Deployments"]
   145  	}
   146  
   147  	resp, err := deployClient.Get(resGroup, name)
   148  	if err != nil {
   149  		if resp.StatusCode == http.StatusNotFound {
   150  			d.SetId("")
   151  			return nil
   152  		}
   153  		return fmt.Errorf("Error making Read request on Azure RM Template Deployment %s: %s", name, err)
   154  	}
   155  
   156  	var outputs map[string]string
   157  	if resp.Properties.Outputs != nil && len(*resp.Properties.Outputs) > 0 {
   158  		outputs = make(map[string]string)
   159  		for key, output := range *resp.Properties.Outputs {
   160  			log.Printf("[DEBUG] Processing deployment output %s", key)
   161  			outputMap := output.(map[string]interface{})
   162  			outputValue, ok := outputMap["value"]
   163  			if !ok {
   164  				log.Printf("[DEBUG] No value - skipping")
   165  				continue
   166  			}
   167  			outputType, ok := outputMap["type"]
   168  			if !ok {
   169  				log.Printf("[DEBUG] No type - skipping")
   170  				continue
   171  			}
   172  
   173  			var outputValueString string
   174  			switch strings.ToLower(outputType.(string)) {
   175  			case "bool":
   176  				outputValueString = strconv.FormatBool(outputValue.(bool))
   177  
   178  			case "string":
   179  				outputValueString = outputValue.(string)
   180  
   181  			case "int":
   182  				outputValueString = fmt.Sprint(outputValue)
   183  
   184  			default:
   185  				log.Printf("[WARN] Ignoring output %s: Outputs of type %s are not currently supported in azurerm_template_deployment.",
   186  					key, outputType)
   187  				continue
   188  			}
   189  			outputs[key] = outputValueString
   190  		}
   191  	}
   192  
   193  	return d.Set("outputs", outputs)
   194  }
   195  
   196  func resourceArmTemplateDeploymentDelete(d *schema.ResourceData, meta interface{}) error {
   197  	client := meta.(*ArmClient)
   198  	deployClient := client.deploymentsClient
   199  
   200  	id, err := parseAzureResourceID(d.Id())
   201  	if err != nil {
   202  		return err
   203  	}
   204  	resGroup := id.ResourceGroup
   205  	name := id.Path["deployments"]
   206  	if name == "" {
   207  		name = id.Path["Deployments"]
   208  	}
   209  
   210  	_, error := deployClient.Delete(resGroup, name, make(chan struct{}))
   211  	err = <-error
   212  
   213  	return err
   214  }
   215  
   216  func expandTemplateBody(template string) (map[string]interface{}, error) {
   217  	var templateBody map[string]interface{}
   218  	err := json.Unmarshal([]byte(template), &templateBody)
   219  	if err != nil {
   220  		return nil, fmt.Errorf("Error Expanding the template_body for Azure RM Template Deployment")
   221  	}
   222  	return templateBody, nil
   223  }
   224  
   225  func normalizeJson(jsonString interface{}) string {
   226  	if jsonString == nil || jsonString == "" {
   227  		return ""
   228  	}
   229  	var j interface{}
   230  	err := json.Unmarshal([]byte(jsonString.(string)), &j)
   231  	if err != nil {
   232  		return fmt.Sprintf("Error parsing JSON: %s", err)
   233  	}
   234  	b, _ := json.Marshal(j)
   235  	return string(b[:])
   236  }
   237  
   238  func templateDeploymentStateRefreshFunc(client *ArmClient, resourceGroupName string, name string) resource.StateRefreshFunc {
   239  	return func() (interface{}, string, error) {
   240  		res, err := client.deploymentsClient.Get(resourceGroupName, name)
   241  		if err != nil {
   242  			return nil, "", fmt.Errorf("Error issuing read request in templateDeploymentStateRefreshFunc to Azure ARM for Template Deployment '%s' (RG: '%s'): %s", name, resourceGroupName, err)
   243  		}
   244  
   245  		return res, strings.ToLower(*res.Properties.ProvisioningState), nil
   246  	}
   247  }