diff --git a/CHANGELOG.md b/CHANGELOG.md index 966f4932..76d2a000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,28 @@ -## 0.5.1 (Unreleased) +## 1.0.0 (Unreleased) + +BUG FIXES: +* Terraform gets only first VM with _number_of_instances or _cluster > 1 ([#39](https://github.com/terraform-providers/terraform-provider-vra7/issues/39)) +* Terraform refresh does not work as intended ([#38](https://github.com/terraform-providers/terraform-provider-vra7/issues/38)) +* VRA Provider deletes resources from state file before receiving any "SUCCESSFUL" status response from VRA during terraform destroy ([#33](https://github.com/terraform-providers/terraform-provider-vra7/issues/33)) +* Terraform apply returns empty ip address ([#27](https://github.com/terraform-providers/terraform-provider-vra7/issues/27)) +* IPAddress return is empty with latest release of vRealize Automation ([#16](https://github.com/terraform-providers/terraform-provider-vra7/issues/16)) +* Terraform adds resource in state file even though the request_status is "FAILED" ([#37](https://github.com/terraform-providers/terraform-provider-vra7/issues/37)) + +FEATURES: +* The resource schema is modified to have more attributes for the vra7_deployment resource +* Feature request: Day 2: Change the number of VMs created by the vRA blueprint ([#47](https://github.com/terraform-providers/terraform-provider-vra7/issues/47)) +* Does 'import' work? ([#29](https://github.com/terraform-providers/terraform-provider-vra7/issues/29)) +* Need to import existing VMs ([#43](https://github.com/terraform-providers/terraform-provider-vra7/issues/43)) +* Support Deployment Day 2 Change Lease action ([#54](https://github.com/terraform-providers/terraform-provider-vra7/issues/54)) +* Create a data source for vra7_deployment resource ([#55](https://github.com/terraform-providers/terraform-provider-vra7/issues/55)) + +IMPROVEMENTS: +* Cleanup README +* Show all the data in the state file that is returned from a deployment resource GET ([#41](https://github.com/terraform-providers/terraform-provider-vra7/issues/41)) +* Cannot pass array of values in element of deployment_configuration or resource_configuration ([#45](https://github.com/terraform-providers/terraform-provider-vra7/issues/45)) +* `ip_address` can be accessed from the resource_configuration schema as a first class attribute + + ## 0.5.0 (November 06, 2019) FEATURES: diff --git a/README.md b/README.md index 3f63e9e6..9d23733e 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,26 @@ Requirements * [Terraform 0.9 or above](https://www.terraform.io/downloads.html) * [Go Language 1.11.4 or above](https://golang.org/dl/) -* [vRealize Automation 7.4 or above](https://www.vmware.com/products/vrealize-automation.html) +* [vRealize Automation 7.5 or above](https://www.vmware.com/products/vrealize-automation.html) Using the provider ---------------------- See the [vra7 documentation](https://www.terraform.io/docs/providers/vra7/index.html) to get started using the vRealize Automation 7 provider. +See vra7_deployment resource examples [here] (examples/README.md) + +## Execution +These are the Terraform commands that can be used for the vRA plugin: +* `terraform init` - The init command is used to initialize a working directory containing Terraform configuration files. +* `terraform plan` - Plan command shows plan for resources like how many resources will be provisioned and how many will be destroyed. +* `terraform apply` - apply is responsible to execute actual calls to provision resources. +* `terraform refresh` - By using the refresh command you can check the status of the request. +* `terraform show` - show will set a console output for resource configuration and request status. +* `terraform destroy` - destroy command will destroy all the resources present in terraform configuration file. + +Navigate to the location where `main.tf` and binary are placed and use the above commands as needed. + Upgrading the provider ---------------------- @@ -36,150 +49,158 @@ terraform init -upgrade to upgrade to the latest stable version of the vra7 provider. See the [Terraform website](https://www.terraform.io/docs/configuration/providers.html#provider-versions) for more information on provider upgrades, and how to set version constraints on your provider. -## Configure -The VMware vRA terraform configuration file contains two objects. +Migrating from previous versions to version 1.0.0, issues fixed and enhancements +--------------------------------------------------------------------------------- -### Provider -This part contains service provider details. +There are some schema changes in the provider version 1.0.0. These changes are made to support vra7 deployment Day 2 actions, detailed information of the deployment in the state file, getting access to more deployment and resource level properties, esp. `ip_address`, etc. See the release notes for more detail. -Provider block contains four mandatory fields: -* `username` - vRA portal username -* `password` - vRA portal password -* `tenant` - vRA portal tenant -* `host` - End point of REST API -* `insecure` - In case of self-signed certificates, default value is false +### Previous main.tf file -**Example:** -``` - provider "vra7" { - username = "vRAUser1@vsphere.local" - password = "password123!" - tenant = "corp.local.tenant" - host = "http://myvra.example.com/" - insecure = false +```hcl +provider "vra7" { + username = var.username + password = var.password + tenant = var.tenant + host = var.host +} + +resource "vra7_deployment" "this" { + count = 1 + catalog_item_name = "multi_machine_catalog" + businessgroup_name = Development + wait_timeout = 20 + deployment_configuration = { + "_leaseDays" = "15" //number of lease days + "BPCustomProp" = "custom depl prop" //custom property in BP required while requesting a catalog item + "Container" = "App.Container" //property of a property group + "Container.Auth.User" = "var.container_user" //property of a property group + "Container.Auth.Password" = "var.container_pw" //property of a property group + "Container.Connection.Port" = "var.container_port" //property of a property group } + resource_configuration = { + "Windows.cpu" = "2" //Windows Machine CPU + "Windows.memory" = "1024" //Windows Machine memory + "Windows.vm_custom_prop" = "a custom prop" //Windows custom property called vm_custom_property + "Windows._cluster" = "2" //Windows cluster size + "Linux.cpu" = "2" //Linux Machine CPU + "http.hostname" = "xyz.com" //HTTP (apache) hostname + "http.network_mode" = "bridge" //HTTP (apache) network mode + } +} ``` +Migrating to the latest version would require to make the following changes in the TF confile(main.tf). -### Resource -This part contains any resource that can be deployed on that service provider. -For example, in our case machine blueprint, software blueprint, complex blueprint, network, etc. +* `_leaseDays` is moved out of of deployment_configuration and added as a property in the schema. The name of the property is `lease_days`. This can be changed for Change Lease Day 2 action. +* We create a `resource_configuration` block for each component. There are three components, Windows, Linux and http. +For instance, the resource_configuration for Windows component would look like this: -**Syntax:** -``` -resource "vra7_deployment" "" { +```hcl +resource_configuration { + component_name = "Windows" //This is the component name and need not be prefixed with all properties + cluster = 2 //cluster is added as a property in the schema. Modifying it will do the + // Scale In/Out Day 2 actions + configuration = { + cpu = 2 + memory = 1024 + vm_custom_prop = "a custom prop" + } } ``` +* `_cluster` is added as a property in the schema. It can be modified for Scale In/Scale Out Day 2 actions +* Support for deployemnt and resource properties of type array of strings in the blocks deployment_configuration as well as configuration under resource_configuration respectively as shown in the example below. +* `ip_address` need not be added in the main.tf. It can be accessed from the state file. It is added as a property in the resource_configuration schema. Please refer to the documentation. -The resource block contains mandatory and optional fields. - -**Mandatory:** - -One of catalog_item_name or catalog_item_id must be specified in the resource configuration. -* `catalog_item_name` - catalog_item_name is a field which contains valid catalog item name from your vRA -* `catalog_item_id` - catalog_item_id is a field which contains a valid catalog item id from your vRA - -**Optional:** -* `description` - This is an optional field. You can specify a description for your deployment. -* `reasons` - This is an optional field. You can specify the reasons for this deployment. -* `businessgroup_id` - This is an optional field. You can specify a different Business Group ID from what provided by default in the template request, provided that your account is allowed to do it. -* `businessgroup_name` - This is an optional field. You can specify a different Business Group name from what provided by default in the template request, provided that your account is allowed to do it. -* `count` - This field is used to create replicas of resources. If count is not provided then it will be considered as 1 by default. -* `deployment_configuration` - This is an optional field. It can be used to specify deployment level properties like _leaseDays, _number_of_instances or any custom properties of the deployment. Key is any field name of catalog item and value is any valid user input to the respective field.. -* `resource_configuration` - This is an optional field. If blueprint properties have default values or no mandatory property value is required then you can skip this field from terraform configuration file. This field contains user inputs to catalog services. Value of this field is in key value pair. Key is service.field_name and value is any valid user input to the respective field. -* `wait_timeout` - This is an optional field with a default value of 15. It defines the time to wait (in minutes) for a resource operation to complete successfully. +### The new main.tf file would look as follows: - -**Example 1:** -``` -resource "vra7_deployment" "example_machine1" { - catalog_item_name = "CentOS 6.3" - reasons = "I have some" - description = "deployment via terraform" - resource_configuration = { - "Linux.cpu" = "1" - "Windows2008R2SP1.cpu" = "2" - "Windows2012.cpu" = "4" - "Windows2016.cpu" = "2" - } - deployment_configuration = { - "_leaseDays" = "5" - } - count = 3 +```hcl +provider "vra7" { + username = var.username + password = var.password + tenant = var.tenant + host = var.host } -``` -**Example 2:** -``` -resource "vra7_deployment" "example_machine2" { - catalog_item_id = "e5dd4fba7f96239286be45ed" - resource_configuration = { - "Linux.cpu" = "1" - "Windows2008.cpu" = "2" - "Windows2012.cpu" = "4" - "Windows2016.cpu" = "2" - } - count = 4 -} +resource "vra7_deployment" "this" { + count = 1 + catalog_item_name = "multi_machine_catalog" + businessgroup_name = Development + wait_timeout = 20 + lease_days = 15 //number of lease days + + deployment_configuration = { + "BPCustomProp" = "custom depl prop" //custom property in BP required while requesting a catalog item + "Container" = "App.Container" //property of a property group + "Container.Auth.User" = "var.container_user" //property of a property group + "Container.Auth.Password" = "var.container_pw" //property of a property group + "Container.Connection.Port" = "var.container_port" //property of a property group + "businessGroups" = < -## Execution -These are the Terraform commands that can be used for the vRA plugin: -* `terraform init` - The init command is used to initialize a working directory containing Terraform configuration files. -* `terraform plan` - Plan command shows plan for resources like how many resources will be provisioned and how many will be destroyed. -* `terraform apply` - apply is responsible to execute actual calls to provision resources. -* `terraform refresh` - By using the refresh command you can check the status of the request. -* `terraform show` - show will set a console output for resource configuration and request status. -* `terraform destroy` - destroy command will destroy all the resources present in terraform configuration file. +## Data source vra7_deployment + +A data source for vra7_deployment can also be created using either deployment ID or catalog item request id. +Refer to the documentation [here](website/docs/d/vra7_deployment.html.markdown) -Navigate to the location where `main.tf` and binary are placed and use the above commands as needed. Building the provider --------------------- @@ -212,29 +233,6 @@ $ $GOPATH/bin/terraform-provider-vra7 ... ``` -# Scripts - -For some older installations prior to v0.2.0 the **update_resource_state.sh** may need to be run. - -There are few changes in the way the terraform config file is written. -1. The resource name is renamed to vra7_deployment from vra7_resource. -2. catalog_name is renamed to catalog_item_name and catalog_id is renamed to catalog_item_id. -3. General properties of deployment like description and reasons are to be specified at the resource level map instead of deployment_configuration. -4. catalog_configuration map is removed. -5. Custom/optional properties of deployment are to be specified in deployment_configuration instead of catalog_configuration. - -These changes in the config file will lead to inconsistency in the `terraform.tfstate` file of the existing resources provisioned using terraform. -The existing state files can be converted to the new format using the script, `update_resource_state.sh` under the scripts folder. - -Note: This script will only convert the state file. The changes to the config file(.tf file) still needs to be done manually. - -## How to use the script - -1. Copy the script, `script/update_resource_state.sh` in the same directory as your terraform.tfstate file. -2. Change the permission of the script, for example `chmod 0700 update_resource_state.sh`. -3. Run the script, `./update_resource_state.sh`. -4. The terraform.tfstate will be updated to the new format and a back-up of the old file is saved as terraform.tfstate_back - Contributing ------------ diff --git a/example/multi-machine/main.tf b/example/multi-machine/main.tf deleted file mode 100644 index a418e1fe..00000000 --- a/example/multi-machine/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -provider "vra7" { - username = var.username - password = var.password - tenant = var.tenant - host = var.host -} - -# Catalog "multi_machine_catalog" contains Linux, Windows and http (apache) designs. -resource "vra7_deployment" "resource_1" { - count = 1 - catalog_item_name = "multi_machine_catalog" - resource_configuration = { - "Windows.cpu" = "2" //Windows Machine CPU - "Linux.cpu" = "2" //Linux Machine CPU - "http.hostname" = "xyz.com" //HTTP (apache) hostname - "http.network_mode" = "bridge" //HTTP (apache) network mode - } -} - diff --git a/example/multi-machine/terraform.tfvars.sample b/example/multi-machine/terraform.tfvars.sample deleted file mode 100644 index e34fe510..00000000 --- a/example/multi-machine/terraform.tfvars.sample +++ /dev/null @@ -1,4 +0,0 @@ -username = "username" -password = "password" -tenant = "tenant" -host = "https://host" diff --git a/example/multi-machine/variables.tf b/example/multi-machine/variables.tf deleted file mode 100644 index 56d162bd..00000000 --- a/example/multi-machine/variables.tf +++ /dev/null @@ -1,12 +0,0 @@ -variable "username" { -} - -variable "password" { -} - -variable "tenant" { -} - -variable "host" { -} - diff --git a/example/multi-machine/versions.tf b/example/multi-machine/versions.tf deleted file mode 100644 index ac97c6ac..00000000 --- a/example/multi-machine/versions.tf +++ /dev/null @@ -1,4 +0,0 @@ - -terraform { - required_version = ">= 0.12" -} diff --git a/example/remote-execute/main.tf b/example/remote-execute/main.tf index 64bbf0ee..cce1b73b 100644 --- a/example/remote-execute/main.tf +++ b/example/remote-execute/main.tf @@ -5,7 +5,7 @@ provider "vra7" { host = var.host } -resource "vra7_deployment" "vm" { +resource "vra7_deployment" "this" { catalog_item_name = var.catalog_item_name reasons = var.description description = var.description @@ -17,10 +17,12 @@ resource "vra7_deployment" "vm" { } resource_configuration = { - "Machine.description" = var.description - "Machine.cpu" = var.cpu - "Machine.memory" = var.ram - "Machine.ip_address" = "" + component_name = "Machine" + configuration = { + description = var.description + cpu = var.cpu + memory = var.ram + } } wait_timeout = var.wait_timeout @@ -28,9 +30,10 @@ resource "vra7_deployment" "vm" { // Connection settings // Connection settings connection { - host = self.resource_configuration["Machine.ip_address"] + host = self.resource_configuration[*].ip_address user = var.ssh_user password = var.ssh_password + type = ssh } // Extend volume to second disk diff --git a/example/simple/main.tf b/example/simple/main.tf index 560c9383..13501d21 100644 --- a/example/simple/main.tf +++ b/example/simple/main.tf @@ -5,11 +5,46 @@ provider "vra7" { host = var.host } -resource "vra7_deployment" "machine" { +resource "vra7_deployment" "this" { count = 1 catalog_item_name = "CentOS 7.0 x64" - resource_configuration = { - "Linux.cpu" = "2" + description = "this description" + reason = "this reason" + lease_days = 10 + + deployment_configuration = { + "blueprint_custom_property" = "This is a blueprint custom property" + "businessGroups" = <key_list %v\n", componentNameList) - - // Arrange component names in descending order of text length. - // Component names are sorted this way because '.', which is used as a separator, may also occur within - // component names. In these situations, the longest name match that includes '.'s should win. - sort.Sort(byLength(componentNameList)) - - //Update request template field values with values from user configuration. - for configKey, configValue := range p.ResourceConfiguration { - for _, componentName := range componentNameList { - // User-supplied resource configuration keys are expected to be of the form: - // .. - // Extract the property names and values for each component in the blueprint, and add/update - // them in the right location in the request template. - if strings.HasPrefix(configKey, componentName) { - propertyName := strings.TrimPrefix(configKey, componentName+".") - if len(propertyName) == 0 { - return fmt.Errorf( - "resource_configuration key is not in correct format. Expected %s to start with %s", - configKey, componentName+".") - } - // Function call which changes request template field values with user-supplied values - requestTemplate.Data[componentName] = updateRequestTemplate( - requestTemplate.Data[componentName].(map[string]interface{}), - propertyName, - configValue) - break - } + requestTemplate.BusinessGroupID = p.BusinessGroupID + requestTemplate.Data["_leaseDays"] = p.Lease + for field, value := range p.DeploymentConfiguration { + requestTemplate.Data[field] = utils.UnmarshalJSONStringIfNecessary(field, value) + } + + for _, rConfig := range p.ResourceConfiguration { + rConfig.Configuration["_cluster"] = rConfig.Cluster + for propertyName, propertyValue := range rConfig.Configuration { + requestTemplate.Data[rConfig.ComponentName] = updateRequestTemplate( + requestTemplate.Data[rConfig.ComponentName].(map[string]interface{}), + propertyName, + propertyValue) } } - log.Info("Updated template - %v\n", requestTemplate.Data) + log.Info("The updated catalog item request template is %v\n", requestTemplate.Data) //Fire off a catalog item request to create a deployment. catalogRequest, err := vraClient.RequestCatalogItem(requestTemplate) if err != nil { - log.Errorf("Resource Machine Request Failed: %v", err) - return fmt.Errorf("Resource Machine Request Failed: %v", err) + return fmt.Errorf("The catalog item qequest failed with error %v", err) } _, err = waitForRequestCompletion(d, meta, catalogRequest.ID) if err != nil { return err } d.SetId(catalogRequest.ID) + log.Info("Finished creating the resource vra7_deployment with request id %s", d.Id()) return resourceVra7DeploymentRead(d, meta) } func updateRequestTemplate(templateInterface map[string]interface{}, field string, value interface{}) map[string]interface{} { - var replaced bool - templateInterface, replaced = utils.ReplaceValueInRequestTemplate(templateInterface, field, value) + replaced := utils.ReplaceValueInRequestTemplate(templateInterface, field, value) if !replaced { templateInterface["data"] = utils.AddValueToRequestTemplate(templateInterface["data"].(map[string]interface{}), field, value) @@ -215,127 +213,177 @@ func updateRequestTemplate(templateInterface map[string]interface{}, field strin return templateInterface } -// Terraform call - terraform apply // This function updates the state of a vRA 7 Deployment when changes to a Terraform file are applied. -// The update is performed on the Deployment using supported (day-2) actions. +//The update is performed on the Deployment using supported (day-2) actions. func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) error { - vraClient = meta.(*sdk.APIClient) + + log.Info("Updating the resource vra7_deployment with request id %s", d.Id()) + vraClient := meta.(*sdk.APIClient) // Get the ID of the catalog request that was used to provision this Deployment. catalogItemRequestID := d.Id() // Get client handle - p := readProviderConfiguration(d) - requestTemplate, validityErr := p.checkConfigValuesValidity(d) - if validityErr != nil { - return validityErr + p, err := readProviderConfiguration(d, vraClient) + if err != nil { + return err } - validityErr = p.checkResourceConfigValidity(requestTemplate) + _, validityErr := p.checkResourceConfigValidity(vraClient) if validityErr != nil { return validityErr } - resourceActions, err := vraClient.GetResourceActions(catalogItemRequestID) - if err != nil { - log.Errorf("Error while reading resource actions for the request %v: %v ", catalogItemRequestID, err.Error()) - return fmt.Errorf("Error while reading resource actions for the request %v: %v ", catalogItemRequestID, err.Error()) + //Change Lease Day 2 operation + if d.HasChange("lease_days") { + deploymentResourceActions, _ := vraClient.GetResourceActions(p.DeploymentID) + deploymentActionsMap := GetActionNameIDMap(deploymentResourceActions) + changeLeaseActionID := deploymentActionsMap["Change Lease"] + if changeLeaseActionID != "" { + resourceActionTemplate, _ := vraClient.GetResourceActionTemplate(p.DeploymentID, changeLeaseActionID) + currTime, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) // format 2020-04-06 00:15:44 -0700 PDT + newLeaseEnd := currTime.Add(time.Hour * 24 * time.Duration(int64(p.Lease))) // format 2020-04-06 00:15:44 -0700 PDT + extendLeaseTo := ConvertToISO8601(newLeaseEnd) + log.Info("Starting Change Lease action on the deployment with id %v. The lease will be extended by %v days.", p.DeploymentID, p.Lease) + _ = utils.ReplaceValueInRequestTemplate( + resourceActionTemplate.Data, "provider-ExpirationDate", extendLeaseTo) + requestID, err := vraClient.PostResourceAction(p.DeploymentID, changeLeaseActionID, resourceActionTemplate) + if err != nil { + log.Errorf("The change lease request failed with error: %v ", err) + return err + } + _, err = waitForRequestCompletion(d, meta, requestID) + if err != nil { + log.Errorf("The change lease request failed with error: %v ", err) + return err + } + log.Info("Successfully completed the Change Lease action for the deployment with id %v.", p.DeploymentID) + } } - // If any change made in resource_configuration. + // get the old and new resource_configuration data + old, new := d.GetChange("resource_configuration") + oldResourceConfigList := expandResourceConfiguration(old.(*schema.Set).List()) + newResourceConfigList := expandResourceConfiguration(new.(*schema.Set).List()) + if d.HasChange("resource_configuration") { - for _, resources := range resourceActions.Content { - if resources.ResourceTypeRef.ID == sdk.InfrastructureVirtual { - var reconfigureEnabled bool - var reconfigureActionID string - for _, op := range resources.Operations { - if op.Name == sdk.Reconfigure { - reconfigureEnabled = true - reconfigureActionID = op.OperationID - break + for _, newResourceConfig := range newResourceConfigList { + oldResourceConfig := GetResourceConfigurationByComponent(oldResourceConfigList, newResourceConfig.ComponentName) + if oldResourceConfig.ComponentName != "" { + deploymentResourceActions, _ := vraClient.GetResourceActions(p.DeploymentID) + deploymentActionsMap := GetActionNameIDMap(deploymentResourceActions) + if oldResourceConfig.Cluster != newResourceConfig.Cluster { + if oldResourceConfig.Cluster < newResourceConfig.Cluster { + // Scale Out Day 2 operation + scaleOutActionID := deploymentActionsMap[sdk.ScaleOut] + // get the action template for scale out + resourceActionTemplate, err := vraClient.GetResourceActionTemplate(p.DeploymentID, scaleOutActionID) + if err != nil { + return err + } + // get the map from the action template corresponding to the key which is the component name + actionTemplateDataMap := GetActionTemplateDataByComponent(resourceActionTemplate.Data, newResourceConfig.ComponentName) + // update the template with the new cluster size + log.Info("Starting Scale In action on the deployment with id %v for the component %v. The cluster size will be reduced from %v to %v.", + p.DeploymentID, oldResourceConfig.ComponentName, oldResourceConfig.Cluster, newResourceConfig.Cluster) + _ = utils.ReplaceValueInRequestTemplate( + actionTemplateDataMap, "_cluster", newResourceConfig.Cluster) + requestID, err := vraClient.PostResourceAction(p.DeploymentID, scaleOutActionID, resourceActionTemplate) + if err != nil { + log.Errorf("The scale out request failed with error: %v ", err) + return err + } + log.Info("The Scale Out operation for the component %v has been submitted", newResourceConfig.ComponentName) + _, err = waitForRequestCompletion(d, meta, requestID) + if err != nil { + log.Errorf("The scale out request failed with error: %v ", err) + return err + } + log.Info("Successfully completed the Scale In action for the deployment with id %v.", p.DeploymentID) + } else if oldResourceConfig.Cluster > newResourceConfig.Cluster { + // Scale In Day 2 operation + scaleInActionID := deploymentActionsMap[sdk.ScaleIn] + // get the action template for scale in + resourceActionTemplate, err := vraClient.GetResourceActionTemplate(p.DeploymentID, scaleInActionID) + if err != nil { + return err + } + // get the map from the action template corresponding to the key which is the component name + actionTemplateDataMap := GetActionTemplateDataByComponent(resourceActionTemplate.Data, newResourceConfig.ComponentName) + // update the template with the new cluster size + log.Info("Starting Scale Out action on the deployment with id %v for the component %v. The cluster size will be increased from %v to %v.", + p.DeploymentID, oldResourceConfig.ComponentName, oldResourceConfig.Cluster, newResourceConfig.Cluster) + _ = utils.ReplaceValueInRequestTemplate( + actionTemplateDataMap, "_cluster", newResourceConfig.Cluster) + requestID, err := vraClient.PostResourceAction(p.DeploymentID, scaleInActionID, resourceActionTemplate) + if err != nil { + log.Errorf("The scale in request failed with error: %v ", err) + return err + } + log.Info("The Scale In operation for the component %v has been submitted", newResourceConfig.ComponentName) + _, err = waitForRequestCompletion(d, meta, requestID) + if err != nil { + log.Errorf("The scale in request failed with error: %v ", err) + return err + } + log.Info("Successfully completed the Scale Out action for the deployment with id %v.", p.DeploymentID) } } - // if reconfigure action is not available for any resource of the deployment - // return with an error message - if !reconfigureEnabled { - return fmt.Errorf("Update is not allowed for resource %v, your entitlement has no Reconfigure action enabled", resources.ID) - } - resourceData := resources.ResourceData - entries := resourceData.Entries - for _, entry := range entries { - if entry.Key == sdk.Component { - entryValue := entry.Value - var componentName string - for k, v := range entryValue { - if k == "value" { - componentName = v.(string) + } + } + + resources, err := vraClient.GetRequestResources(catalogItemRequestID) + if err != nil { + return fmt.Errorf("Error while getting resources for the request %v: %v ", catalogItemRequestID, err.Error()) + } + // Reconfigure Day 2 operation + for _, resource := range resources.Content { + oldResourceConfig := GetResourceByID(oldResourceConfigList, resource.ID) + if oldResourceConfig.ComponentName != "" { + vmResourceActions, _ := vraClient.GetResourceActions(oldResourceConfig.ResourceID) + vmResourceActionsMap := GetActionNameIDMap(vmResourceActions) + if vmResourceActionsMap[sdk.Reconfigure] != "" { + reconfigureActionID := vmResourceActionsMap[sdk.Reconfigure] + resourceActionTemplate, _ := vraClient.GetResourceActionTemplate(oldResourceConfig.ResourceID, reconfigureActionID) + newResourceConfig := GetResourceConfigurationByComponent(newResourceConfigList, oldResourceConfig.ComponentName) + configChanged := false + actionTemplateDataMap := resourceActionTemplate.Data + for propertyName, propertyValue := range newResourceConfig.Configuration { + if oldResourceConfig.Configuration[propertyName] != propertyValue { + _ = utils.ReplaceValueInRequestTemplate( + actionTemplateDataMap, propertyName, propertyValue) + if !configChanged { + configChanged = true } } - log.Info("Retrieving reconfigure action template for the component: %v ", componentName) - - resourceActionTemplate, err := vraClient.GetResourceActionTemplate(resources.ID, reconfigureActionID) + } + if configChanged { + log.Info("Starting Reconfigure action on the component %v.", oldResourceConfig.ComponentName) + requestID, err := vraClient.PostResourceAction(oldResourceConfig.ResourceID, reconfigureActionID, resourceActionTemplate) if err != nil { - log.Errorf("Error retrieving reconfigure action template for the component %v: %v ", componentName, err.Error()) - return fmt.Errorf("Error retrieving reconfigure action template for the component %v: %v ", componentName, err.Error()) - } - configChanged := false - for configKey := range p.ResourceConfiguration { - var returnFlag bool - //compare resource list (resource_name) with user configuration fields - if strings.HasPrefix(configKey, componentName+".") { - //If user_configuration contains resource_list element - // then split user configuration key into resource_name and field_name - nameList := strings.Split(configKey, componentName+".") - //actionResponseInterface := actionResponse.(map[string]interface{}) - //Function call which changes the template field values with user values - //Replace existing values with new values in resource child template - resourceActionTemplate.Data, returnFlag = utils.ReplaceValueInRequestTemplate( - resourceActionTemplate.Data, - nameList[1], - p.ResourceConfiguration[configKey]) - if returnFlag { - configChanged = true - } - } + log.Errorf("The reconfigure request failed with error: %v ", err) + return err } - oldData, _ := d.GetChange("resource_configuration") - // If template value got changed then set post call and update resource child - if configChanged { - // This request id is for the reconfigure action on this machine and - // will be used to track the status of the reconfigure request for this resource. - // It will not replace the initial catalog item request id - requestID, err := vraClient.PostResourceAction(resources.ID, reconfigureActionID, resourceActionTemplate) - if err != nil { - err = d.Set("resource_configuration", oldData) - if err != nil { - return err - } - log.Errorf("The update request failed with error: %v ", err) - return err - } - status, err := waitForRequestCompletion(d, meta, requestID) - if err != nil { - // if the update request fails, go back to the old state and return the error - if status == sdk.Failed { - setErr := d.Set("resource_configuration", oldData) - if setErr != nil { - return setErr - } - } - return err - } + log.Info("The Scale In operation for the component %v has been submitted", oldResourceConfig.ComponentName) + _, err = waitForRequestCompletion(d, meta, requestID) + if err != nil { + log.Errorf("The reconfigure request for component %v failed with error: %v ", oldResourceConfig.ComponentName, err) + return err } + log.Info("Successfully completed the Reconfiguret action on the component %v.", oldResourceConfig.ComponentName) } } } } } + log.Info("Finished updating the resource vra7_deployment with request id %s", d.Id()) return resourceVra7DeploymentRead(d, meta) } -// Terraform call - terraform refresh // This function retrieves the latest state of a vRA 7 deployment. Terraform updates its state based on // the information returned by this function. func resourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) error { - vraClient = meta.(*sdk.APIClient) + log.Info("Reading the resource vra7_deployment with request id %s ", d.Id()) + vraClient := meta.(*sdk.APIClient) + // Get the ID of the catalog request that was used to provision this Deployment. This id // will remain the same for this deployment across any actions on the machines like reconfigure, etc. catalogItemRequestID := d.Id() @@ -347,123 +395,152 @@ func resourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("The resource cannot be found") } if errTemplate != nil || len(requestResourceView.Content) == 0 { - return fmt.Errorf("Resource view failed to load: %v", errTemplate) + return fmt.Errorf("Resource view failed to load with the error %v", errTemplate) } - resourceDataMap := make(map[string]map[string]interface{}) + var resourceConfigList []sdk.ResourceConfigurationStruct for _, resource := range requestResourceView.Content { - if resource.ResourceType == sdk.InfrastructureVirtual { - resourceData := resource.ResourcesData - log.Info("The resource data map of the resource %v is: \n%v", resourceData.Component, resource.ResourcesData) - dataVals := make(map[string]interface{}) - resourceDataMap[resourceData.Component] = dataVals - dataVals[sdk.MachineCPU] = resourceData.CPU - dataVals[sdk.MachineStorage] = resourceData.Storage - dataVals[sdk.IPAddress] = resourceData.IPAddress - dataVals[sdk.MachineMemory] = resourceData.Memory - dataVals[sdk.MachineName] = resourceData.MachineName - dataVals[sdk.MachineGuestOs] = resourceData.MachineGuestOperatingSystem - dataVals[sdk.MachineBpName] = resourceData.MachineBlueprintName - dataVals[sdk.MachineType] = resourceData.MachineType - dataVals[sdk.MachineReservationName] = resourceData.MachineReservationName - dataVals[sdk.MachineInterfaceType] = resourceData.MachineInterfaceType - dataVals[sdk.MachineID] = resourceData.MachineID - dataVals[sdk.MachineGroupName] = resourceData.MachineGroupName - dataVals[sdk.MachineDestructionDate] = resourceData.MachineDestructionDate - // Handle Network Info - for idx, netDetails := range resourceData.Networks { - log.Info("The Network list value is for idx %i = %+v", idx, netDetails) - networkIndexName := "Network" + strconv.Itoa(idx) - dataVals[networkIndexName+".IPAddress"] = netDetails.NetworkAddressInfo.IPAddress - dataVals[networkIndexName+".MACAddress"] = netDetails.NetworkAddressInfo.MACAddress - dataVals[networkIndexName+".Name"] = netDetails.NetworkAddressInfo.Name + rMap := resource.(map[string]interface{}) + resourceType := rMap["resourceType"].(string) + name := rMap["name"].(string) + dateCreated := rMap["dateCreated"].(string) + lastUpdated := rMap["lastUpdated"].(string) + resourceID := rMap["resourceId"].(string) + requestID := rMap["requestId"].(string) + requestState := rMap["requestState"].(string) + + // if the resource type is VMs, update the resource_configuration attribute + if resourceType == sdk.InfrastructureVirtual { + data := rMap["data"].(map[string]interface{}) + componentName := data["Component"].(string) + parentResourceID := rMap["parentResourceId"].(string) + var resourceConfigStruct sdk.ResourceConfigurationStruct + resourceConfigStruct.Configuration = data + resourceConfigStruct.ComponentName = componentName + resourceConfigStruct.Name = name + resourceConfigStruct.DateCreated = dateCreated + resourceConfigStruct.LastUpdated = lastUpdated + resourceConfigStruct.ResourceID = resourceID + resourceConfigStruct.ResourceType = resourceType + resourceConfigStruct.RequestID = requestID + resourceConfigStruct.RequestState = requestState + resourceConfigStruct.ParentResourceID = parentResourceID + resourceConfigStruct.IPAddress = data["ip_address"].(string) + + if rMap["description"] != nil { + resourceConfigStruct.Description = rMap["description"].(string) + } + if rMap["status"] != nil { + resourceConfigStruct.Status = rMap["status"].(string) } - } - } - resourceConfiguration, _ := d.Get("resource_configuration").(map[string]interface{}) - resourceConfiguration, changed := utils.UpdateResourceConfigurationMap(resourceConfiguration, resourceDataMap) + // the cluster value is fetched from scale out action template as the resourceViews API does not return that information + deploymentResourceActions, _ := vraClient.GetResourceActions(parentResourceID) + deploymentActionsMap := GetActionNameIDMap(deploymentResourceActions) + scaleOutActionID := deploymentActionsMap["Scale Out"] + resourceActionTemplate, _ := vraClient.GetResourceActionTemplate(parentResourceID, scaleOutActionID) + actionTemplateResourceDataMap := GetActionTemplateDataByComponent(resourceActionTemplate.Data, componentName) + rDataMap := actionTemplateResourceDataMap["data"].(map[string]interface{}) + var cluster int = int(rDataMap["_cluster"].(float64)) + resourceConfigStruct.Cluster = cluster + // end + + resourceConfigList = append(resourceConfigList, resourceConfigStruct) + + } else if resourceType == sdk.DeploymentResourceType { + + leaseMap := rMap["lease"].(map[string]interface{}) + leaseStart := leaseMap["start"].(string) + d.Set("lease_start", leaseStart) + // if the lease never expires, the end date will be null + if leaseMap["end"] != nil { + leaseEnd := leaseMap["end"].(string) + d.Set("lease_end", leaseEnd) + // the lease_days are calculated from the current time and lease_end dates as the resourceViews API does not return that information + currTime, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + endTime, _ := time.Parse(time.RFC3339, leaseEnd) + diff := endTime.Sub(currTime) + d.Set("lease_days", int(diff.Hours()/24)) + // end + } else { + d.Set("lease_days", nil) // set lease days to nil if lease_end is nil + } - if changed { - setError := d.Set("resource_configuration", resourceConfiguration) - if setError != nil { - return fmt.Errorf(setError.Error()) + d.Set("catalog_item_id", rMap["catalogItemId"].(string)) + d.Set("catalog_item_name", rMap["catalogItemLabel"].(string)) + d.Set("deployment_id", resourceID) + d.Set("date_created", dateCreated) + d.Set("last_updated", lastUpdated) + d.Set("tenant_id", rMap["tenantId"].(string)) + d.Set("owners", rMap["owners"].([]interface{})) + d.Set("name", name) + d.Set("businessgroup_id", rMap["businessGroupId"].(string)) + if rMap["description"] != nil { + d.Set("description", rMap["description"].(string)) + } + if rMap["status"] != nil { + d.Set("request_status", rMap["status"].(string)) + } } } + if err := d.Set("resource_configuration", flattenResourceConfigurations(resourceConfigList)); err != nil { + return fmt.Errorf("error setting resource configuration - error: %v", err) + } + + log.Info("Finished reading the resource vra7_deployment with request id %s", d.Id()) return nil } //Function use - To delete resources which are created by terraform and present in state file -//Terraform call - terraform destroy func resourceVra7DeploymentDelete(d *schema.ResourceData, meta interface{}) error { - vraClient = meta.(*sdk.APIClient) - // Get client handle - p := readProviderConfiguration(d) - //Get requester machine ID from schema.dataresource - catalogItemRequestID := d.Id() + log.Info("Deleting the resource vra7_deployment with request id %s", d.Id()) + vraClient := meta.(*sdk.APIClient) + // Throw an error if request ID has no value or empty value if len(d.Id()) == 0 { return fmt.Errorf("Resource not found") } - log.Info("Calling delete resource for the request id %v ", catalogItemRequestID) - - resourceView, err := vraClient.GetRequestResourceView(catalogItemRequestID) - if err != nil { - return fmt.Errorf("Resource view failed to load: %v", err) - } - if len(resourceView.Content) == 0 { - //If resource gets deleted then unset the resource ID from state file - d.SetId("") - return fmt.Errorf("The resource cannot be found") - } - resourceActions, err := vraClient.GetResourceActions(catalogItemRequestID) + p, err := readProviderConfiguration(d, vraClient) if err != nil { return err } - for _, resources := range resourceActions.Content { - if resources.ResourceTypeRef.ID == sdk.DeploymentResourceType { - deploymentName := resources.Name - var destroyEnabled bool - var destroyActionID string - for _, op := range resources.Operations { - if (p.DeploymentDestroy && op.Name == sdk.DeploymentDestroy) || (!p.DeploymentDestroy && op.Name == sdk.Destroy) { - destroyEnabled = true - destroyActionID = op.OperationID - break - } - } - // if destroy deployment action is not available for the deployment - // return with an error message - if !destroyEnabled { - return fmt.Errorf("The deployment %v cannot be destroyed, your entitlement has no Destroy Deployment action enabled", deploymentName) - } - resourceActionTemplate, err := vraClient.GetResourceActionTemplate(resources.ID, destroyActionID) - if err != nil { - log.Errorf(DestroyActionTemplateError, deploymentName, err.Error()) - return fmt.Errorf(DestroyActionTemplateError, deploymentName, err.Error()) - } - requestID, err := vraClient.PostResourceAction(resources.ID, destroyActionID, resourceActionTemplate) - if err != nil { - log.Errorf("The destroy deployment request failed with error: %v ", err) - return err - } - status, err := waitForRequestCompletion(d, meta, requestID) - if err != nil { - if status == sdk.Successful { - d.SetId("") - } - } + deploymentID := d.Get("deployment_id").(string) + deploymentResourceActions, _ := vraClient.GetResourceActions(deploymentID) + deploymentActionsMap := GetActionNameIDMap(deploymentResourceActions) + destroyActionID := deploymentActionsMap["Destroy"] + if p.DeploymentDestroy && destroyActionID != "" { + resourceActionTemplate, _ := vraClient.GetResourceActionTemplate(deploymentID, destroyActionID) + requestID, err := vraClient.PostResourceAction(deploymentID, destroyActionID, resourceActionTemplate) + if err != nil { + log.Errorf("The destroy request failed with error: %v ", err) + return err + } + status, err := waitForRequestCompletion(d, meta, requestID) + if err != nil { + log.Errorf("The destroy request failed with error: %v ", err) + return err + } + if status == sdk.Successful { + d.SetId("") } } + log.Info("Finished destroying the resource vra7_deployment with request id %s", d.Id()) return nil } // check if the resource configuration is valid in the terraform config file -func (p *ProviderSchema) checkResourceConfigValidity(requestTemplate *sdk.CatalogItemRequestTemplate) error { +func (p *ProviderSchema) checkResourceConfigValidity(client *sdk.APIClient) (*sdk.CatalogItemRequestTemplate, error) { log.Info("Checking if the terraform config file is valid") + // Get request template for catalog item. + requestTemplate, err := client.GetCatalogItemRequestTemplate(p.CatalogItemID) + if err != nil { + return nil, err + } + log.Info("The request template data corresponding to the catalog item %v is: \n %v\n", p.CatalogItemID, requestTemplate.Data) + // Get all component names in the blueprint corresponding to the catalog item. componentSet := make(map[string]bool) for field := range requestTemplate.Data { @@ -474,121 +551,96 @@ func (p *ProviderSchema) checkResourceConfigValidity(requestTemplate *sdk.Catalo log.Info("The component name(s) in the blueprint corresponding to the catalog item: %v\n", componentSet) var invalidKeys []string - // check if the keys in the resourceConfiguration map exists in the componentSet - // if the key in config is machine1.vsphere.custom.location, match every string after each dot - // until a matching string is found in componentSet. - // If found, it's a valid key else the component name is invalid - for k := range p.ResourceConfiguration { - var key = k - var isValid bool - for strings.LastIndex(key, ".") != -1 { - lastIndex := strings.LastIndex(key, ".") - key = key[0:lastIndex] - if _, ok := componentSet[key]; ok { - log.Info("The component name %s in the terraform config file is valid ", key) - isValid = true - break - } - } - if !isValid { - invalidKeys = append(invalidKeys, k) + // check if the component of resource_configuration map exists in the componentSet + // retrieved from catalog item request template + for _, k := range p.ResourceConfiguration { + if _, ok := componentSet[k.ComponentName]; !ok { + invalidKeys = append(invalidKeys, k.ComponentName) } } // there are invalid resource config keys in the terraform config file, abort and throw an error if len(invalidKeys) > 0 { - log.Error("The resource_configuration in the config file has invalid component name(s): %v ", strings.Join(invalidKeys, ", ")) - return fmt.Errorf(ConfigInvalidError, strings.Join(invalidKeys, ", ")) + return nil, fmt.Errorf(ConfigInvalidError, strings.Join(invalidKeys, ", ")) } - return nil + + return requestTemplate, nil } // check if the values provided in the config file are valid and set // them in the resource schema. Requires to call APIs -func (p *ProviderSchema) checkConfigValuesValidity(d *schema.ResourceData) (*sdk.CatalogItemRequestTemplate, error) { - // // If catalog_name and catalog_id both not provided then return an error - if len(p.CatalogItemName) <= 0 && len(p.CatalogItemID) <= 0 { - return nil, fmt.Errorf("Either catalog_name or catalog_id should be present in given configuration") - } - - var catalogItemIDFromName string - var catalogItemNameFromID string - var err error - // if catalog item id is provided, fetch the catalog item name - if len(p.CatalogItemName) > 0 { - catalogItemIDFromName, err = vraClient.ReadCatalogItemByName(p.CatalogItemName) - if err != nil || catalogItemIDFromName == "" { - return nil, fmt.Errorf("Error in finding catalog item id corresponding to the catlog item name %v: \n %v", p.CatalogItemName, err) - } - log.Info("The catalog item id provided in the config is %v\n", catalogItemIDFromName) - } +func checkConfigValuesValidity(d *schema.ResourceData) error { - // if catalog item name is provided, fetch the catalog item id - if len(p.CatalogItemID) > 0 { // else if both are provided and matches or just id is provided, use id - catalogItemNameFromID, err = vraClient.ReadCatalogItemNameByID(p.CatalogItemID) - if err != nil || catalogItemNameFromID == "" { - return nil, fmt.Errorf("Error in finding catalog item name corresponding to the catlog item id %v: \n %v", p.CatalogItemID, err) - } - log.Info("The catalog item name corresponding to the catalog item id in the config is: %v\n", catalogItemNameFromID) + catalogItemName := d.Get("catalog_item_name").(string) + catalogItemID := d.Get("catalog_item_id").(string) + businessgroupName := d.Get("businessgroup_name").(string) + businessgroupID := d.Get("businessgroup_id").(string) + + // If catalogItemID and catalogItemName both not provided then return an error + if catalogItemID == "" && catalogItemName == "" { + return fmt.Errorf("Provide either a catalog_item_name or a catalog_item_id in the configuration") } - // if both catalog item name and id are provided but does not belong to the same catalog item, throw an error - if len(p.CatalogItemName) > 0 && len(p.CatalogItemID) > 0 && (catalogItemIDFromName != p.CatalogItemID || catalogItemNameFromID != p.CatalogItemName) { - log.Error(CatalogItemIDNameNotMatchingErr, p.CatalogItemName, p.CatalogItemID) - return nil, fmt.Errorf(CatalogItemIDNameNotMatchingErr, p.CatalogItemName, p.CatalogItemID) - } else if len(p.CatalogItemID) > 0 { // else if both are provided and matches or just id is provided, use id - d.Set("catalog_item_id", p.CatalogItemID) - d.Set("catalog_item_name", catalogItemNameFromID) - } else if len(p.CatalogItemName) > 0 { // else if name is provided, use the id fetched from the name - d.Set("catalog_item_id", catalogItemIDFromName) - d.Set("catalog_item_name", p.CatalogItemName) + // If both catalog_item_name and catalogItemName return an error + if catalogItemID != "" && catalogItemName != "" { + return fmt.Errorf("Provide either a catalog_item_name or a catalog_item_id in the configuration") } - // update the catalogItemID var with the updated id - p.CatalogItemID = d.Get("catalog_item_id").(string) + // If businessgroupID and businessgroupName both not provided then return an error + if businessgroupID == "" && businessgroupName == "" { + return fmt.Errorf("Provide either a businessgroup_id or a businessgroup_name in the configuration") + } - // Get request template for catalog item. - requestTemplate, err := vraClient.GetCatalogItemRequestTemplate(p.CatalogItemID) - if err != nil { - return nil, err + // If both businessgroupID and businessgroupName return an error + if businessgroupID != "" && businessgroupName != "" { + return fmt.Errorf("Provide either a businessgroup_id or a businessgroup_name in the configuration") } - log.Info("The request template data corresponding to the catalog item %v is: \n %v\n", p.CatalogItemID, requestTemplate.Data) + return nil +} - updateRequestTemplateFromDeploymentConfiguration(p.DeploymentConfiguration, requestTemplate) +// read the config file +func readProviderConfiguration(d *schema.ResourceData, vraClient *sdk.APIClient) (*ProviderSchema, error) { - // get the business group id from name - var businessGroupIDFromName string - if len(p.BusinessGroupName) > 0 { - businessGroupIDFromName, err = vraClient.GetBusinessGroupID(p.BusinessGroupName, vraClient.Tenant) - if err != nil || businessGroupIDFromName == "" { - return nil, err - } + log.Info("Reading the provider configuration data.....") + providerSchema := ProviderSchema{ + CatalogItemName: strings.TrimSpace(d.Get("catalog_item_name").(string)), + CatalogItemID: strings.TrimSpace(d.Get("catalog_item_id").(string)), + Description: strings.TrimSpace(d.Get("description").(string)), + Reasons: strings.TrimSpace(d.Get("reasons").(string)), + BusinessGroupName: strings.TrimSpace(d.Get("businessgroup_name").(string)), + BusinessGroupID: strings.TrimSpace(d.Get("businessgroup_id").(string)), + Lease: d.Get("lease_days").(int), + DeploymentID: strings.TrimSpace(d.Get("deployment_id").(string)), + WaitTimeout: d.Get("wait_timeout").(int) * 60, + ResourceConfiguration: expandResourceConfiguration(d.Get("resource_configuration").(*schema.Set).List()), + DeploymentDestroy: d.Get("deployment_destroy").(bool), + DeploymentConfiguration: d.Get("deployment_configuration").(map[string]interface{}), } - //if both business group name and id are provided but does not belong to the same business group, throw an error - if len(p.BusinessGroupName) > 0 && len(p.BusinessGroupID) > 0 && businessGroupIDFromName != p.BusinessGroupID { - log.Error(BusinessGroupIDNameNotMatchingErr, p.BusinessGroupName, p.BusinessGroupID) - return nil, fmt.Errorf(BusinessGroupIDNameNotMatchingErr, p.BusinessGroupName, p.BusinessGroupID) - } else if len(p.BusinessGroupID) > 0 { // else if both are provided and matches or just id is provided, use id - log.Info("Setting business group id %s ", p.BusinessGroupID) - requestTemplate.BusinessGroupID = p.BusinessGroupID - } else if len(p.BusinessGroupName) > 0 { // else if name is provided, use the id fetched from the name - log.Info("Setting business group id %s for the group %s ", businessGroupIDFromName, p.BusinessGroupName) - requestTemplate.BusinessGroupID = businessGroupIDFromName + // if catalog item name is provided, fetch the catalog item id + if len(providerSchema.CatalogItemName) > 0 { + id, err := vraClient.ReadCatalogItemByName(providerSchema.CatalogItemName) + if err != nil { + return &providerSchema, err + } + providerSchema.CatalogItemID = id } - return requestTemplate, nil -} -func updateRequestTemplateFromDeploymentConfiguration(deploymentConfiguration map[string]interface{}, requestTemplate *sdk.CatalogItemRequestTemplate) { - for field := range deploymentConfiguration { - value := deploymentConfiguration[field] - - requestTemplate.Data[field] = utils.UnmarshalJsonStringIfNecessary(field, value) + // get the business group id from name + if len(providerSchema.BusinessGroupName) > 0 { + id, err := vraClient.GetBusinessGroupID(providerSchema.BusinessGroupName, vraClient.Tenant) + if err != nil { + return &providerSchema, err + } + providerSchema.BusinessGroupID = id } + + log.Info("The values provided in the TF config file is: \n %v ", providerSchema) + return &providerSchema, nil } -// check the request status on apply and update +// check the request status on apply update and destroy func waitForRequestCompletion(d *schema.ResourceData, meta interface{}, requestID string) (string, error) { - + vraClient := meta.(*sdk.APIClient) waitTimeout := d.Get("wait_timeout").(int) * 60 sleepFor := 30 status := "" @@ -603,7 +655,6 @@ func waitForRequestCompletion(d *schema.ResourceData, meta interface{}, requestI log.Info("Request is SUCCESSFUL.") return sdk.Successful, nil } else if status == sdk.Failed { - log.Error("Request Failed with message %v ", reqestStatusView.RequestCompletion.CompletionDetails) return sdk.Failed, fmt.Errorf("Request failed \n %v ", reqestStatusView.RequestCompletion.CompletionDetails) } else if status == sdk.InProgress { log.Info("The request is still IN PROGRESS.") @@ -616,24 +667,55 @@ func waitForRequestCompletion(d *schema.ResourceData, meta interface{}, requestI return "", fmt.Errorf("Request has timed out with status %s. \nRun terraform refresh to get the latest state of your request", status) } -// read the config file -func readProviderConfiguration(d *schema.ResourceData) *ProviderSchema { +// GetActionTemplateDataByComponent return the map corresponding the component name in the template data +func GetActionTemplateDataByComponent(actionTemplate map[string]interface{}, componentName string) map[string]interface{} { + actionTemplateDataByComponent := make(map[string]interface{}) + for key, value := range actionTemplate { + if key == componentName && reflect.ValueOf(value).Kind() == reflect.Map { + actionTemplateDataByComponent = value.(map[string]interface{}) + break + } + } + return actionTemplateDataByComponent +} - log.Info("Reading the provider configuration data.....") - providerSchema := ProviderSchema{ - CatalogItemName: strings.TrimSpace(d.Get("catalog_item_name").(string)), - CatalogItemID: strings.TrimSpace(d.Get("catalog_item_id").(string)), - Description: strings.TrimSpace(d.Get("description").(string)), - Reasons: strings.TrimSpace(d.Get("reasons").(string)), - BusinessGroupName: strings.TrimSpace(d.Get("businessgroup_name").(string)), - BusinessGroupID: strings.TrimSpace(d.Get("businessgroup_id").(string)), - WaitTimeout: d.Get("wait_timeout").(int) * 60, - FailedMessage: strings.TrimSpace(d.Get("failed_message").(string)), - ResourceConfiguration: d.Get("resource_configuration").(map[string]interface{}), - DeploymentConfiguration: d.Get("deployment_configuration").(map[string]interface{}), - DeploymentDestroy: d.Get("deployment_destroy").(bool), +// GetResourceConfigurationByComponent returns the resource_configuration corresponding the component +func GetResourceConfigurationByComponent(resourceConfigurationList []sdk.ResourceConfigurationStruct, component string) sdk.ResourceConfigurationStruct { + var resourceConfig sdk.ResourceConfigurationStruct + for _, rConfig := range resourceConfigurationList { + if rConfig.ComponentName == component { + resourceConfig = rConfig + } } + return resourceConfig +} - log.Info("The values provided in the TF config file is: \n %v ", providerSchema) - return &providerSchema +// GetActionNameIDMap returns a map of Action name and id +func GetActionNameIDMap(resourceActions []sdk.Operation) map[string]string { + actionNameIDMap := make(map[string]string) + for _, action := range resourceActions { + actionNameIDMap[action.Name] = action.ID + } + return actionNameIDMap +} + +// GetResourceByID return the resoirce config struct object filtered by ID +func GetResourceByID(resourceConfigStructList []sdk.ResourceConfigurationStruct, resourceID string) sdk.ResourceConfigurationStruct { + var resourceConfigStruct sdk.ResourceConfigurationStruct + for _, resourceStruct := range resourceConfigStructList { + if resourceStruct.ResourceID == resourceID { + resourceConfigStruct = resourceStruct + } + } + return resourceConfigStruct +} + +// ConvertToISO8601 converts time to the format ISO8601 (2020-04-16T00:15:44.700Z) that vRA 7.x understands +func ConvertToISO8601(givenTime time.Time) string { + rfc339Time, _ := time.Parse(time.RFC3339, givenTime.Format(time.RFC3339)) // 2020-04-06 00:15:44 -0700 PDT + testArray := strings.Fields(rfc339Time.String()) // ["2020-04-06", "00:15:44", "-0700", "PDT"] + length := len(testArray[2]) // length of -0700 + last3 := testArray[2][length-3 : length] // 700 + iso8601Time := testArray[0] + "T" + testArray[1] + "." + last3 + "Z" // 2020-04-16T00:15:44.700Z + return iso8601Time } diff --git a/vra7/resource_vra7_deployment_test.go b/vra7/resource_vra7_deployment_test.go index e95bcd7e..17571731 100644 --- a/vra7/resource_vra7_deployment_test.go +++ b/vra7/resource_vra7_deployment_test.go @@ -1,13 +1,13 @@ package vra7 import ( - "encoding/json" "fmt" "strconv" "strings" "testing" "github.com/hashicorp/terraform/terraform" + httpmock "gopkg.in/jarcoal/httpmock.v1" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -23,78 +23,74 @@ func init() { func TestConfigValidityFunction(t *testing.T) { - mockRequestTemplate, err := GetMockRequestTemplate() - utils.AssertNilError(t, err) + httpmock.ActivateNonDefault(client.Client) + defer httpmock.DeactivateAndReset() - // a resource_configuration map is created with valid components - // all combinations of components name and properties are created with dots - mockConfigResourceMap := make(map[string]interface{}) - mockConfigResourceMap["mock.test.machine1.cpu"] = 2 - mockConfigResourceMap["mock.test.machine1.mock.storage"] = 8 + catalogItemID := "dhbh-jhdv-ghdv-dhvdd" - resourceSchema := resourceVra7Deployment().Schema + path := fmt.Sprintf(sdk.RequestTemplateAPI, catalogItemID) + url := client.BuildEncodedURL(path, nil) - resourceDataMap := map[string]interface{}{ - "catalog_item_id": "abcdefghijklmn", - "resource_configuration": mockConfigResourceMap, - } + httpmock.RegisterResponder("GET", url, + httpmock.NewStringResponder(200, mockRequestTemplate)) - mockResourceData := schema.TestResourceDataRaw(t, resourceSchema, resourceDataMap) + resourceConfigList := make([]map[string]interface{}, 0) + resourceConfigurationObject1 := make(map[string]interface{}) - p := readProviderConfiguration(mockResourceData) + configMap := make(map[string]interface{}) + configMap["cpu"] = 2 + configMap["memory"] = 1024 - readProviderConfiguration(mockResourceData) - err = p.checkResourceConfigValidity(mockRequestTemplate) - utils.AssertNilError(t, err) + resourceConfigurationObject1["configuration"] = configMap + resourceConfigurationObject1["component_name"] = "mock.test.machine1" + + resourceConfigList = append(resourceConfigList, resourceConfigurationObject1) - mockConfigResourceMap["machine2.mock.cpu"] = 2 - mockConfigResourceMap["machine2.storage"] = 2 + resourceSchema := resourceVra7Deployment().Schema - resourceDataMap = map[string]interface{}{ - "catalog_item_id": "abcdefghijklmn", - "resource_configuration": mockConfigResourceMap, + resourceSchemaMap := map[string]interface{}{ + "catalog_item_id": catalogItemID, + "wait_timeout": 20, + "resource_configuration": resourceConfigList, } - mockResourceData = schema.TestResourceDataRaw(t, resourceSchema, resourceDataMap) - readProviderConfiguration(mockResourceData) + mockResourceData := schema.TestResourceDataRaw(t, resourceSchema, resourceSchemaMap) + + p, _ := readProviderConfiguration(mockResourceData, &client) - err = p.checkResourceConfigValidity(mockRequestTemplate) + mockRequestTemplateStruct, err := p.checkResourceConfigValidity(&client) utils.AssertNilError(t, err) + utils.AssertNotNil(t, mockRequestTemplateStruct) + utils.AssertNotNil(t, mockRequestTemplateStruct.Data["mock.test.machine1"]) + utils.AssertEqualsString(t, catalogItemID, mockRequestTemplateStruct.CatalogItemID) + + resourceConfigurationObject2 := make(map[string]interface{}) + resourceConfigurationObject2["configuration"] = configMap + resourceConfigurationObject2["component_name"] = "mock.test.machine2" - mockConfigResourceMap["mock.machine3.vSphere.mock.cpu"] = 2 - resourceDataMap = map[string]interface{}{ - "catalog_item_id": "abcdefghijklmn", - "resource_configuration": mockConfigResourceMap, + resourceConfigList = append(resourceConfigList, resourceConfigurationObject2) + + resourceSchemaMap = map[string]interface{}{ + "catalog_item_id": catalogItemID, + "resource_configuration": resourceConfigList, } - mockResourceData = schema.TestResourceDataRaw(t, resourceSchema, resourceDataMap) - p = readProviderConfiguration(mockResourceData) + mockResourceData = schema.TestResourceDataRaw(t, resourceSchema, resourceSchemaMap) + p, _ = readProviderConfiguration(mockResourceData, &client) var mockInvalidKeys []string - mockInvalidKeys = append(mockInvalidKeys, "mock.machine3.vSphere.mock.cpu") + mockInvalidKeys = append(mockInvalidKeys, "mock.test.machine2") validityErr := fmt.Sprintf(ConfigInvalidError, strings.Join(mockInvalidKeys, ", ")) - err = p.checkResourceConfigValidity(mockRequestTemplate) - // this should throw an error as none of the string combinations (mock, mock.machine3, mock.machine3.vsphere, etc) - // matches the component names(mock.test.machine1 and machine2) in the request template + mockRequestTemplateStruct, err = p.checkResourceConfigValidity(&client) + // this should throw an error mock.test.machine2 does not match + // with the component names(mock.test.machine1 and machine2) in the request template utils.AssertNotNilError(t, err) utils.AssertEqualsString(t, validityErr, err.Error()) + utils.AssertNil(t, mockRequestTemplateStruct) } -// creates a mock request template from a request template template json file -func GetMockRequestTemplate() (*sdk.CatalogItemRequestTemplate, error) { - - mockRequestTemplateStruct := sdk.CatalogItemRequestTemplate{} - err := json.Unmarshal([]byte(mockRequestTemplate), &mockRequestTemplateStruct) - if err != nil { - return nil, err - } - - return &mockRequestTemplateStruct, nil - -} - -func TestAccVra7DeploymentCreate_basic(t *testing.T) { +func TestAccVra7Deployment(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -103,95 +99,25 @@ func TestAccVra7DeploymentCreate_basic(t *testing.T) { { Config: testAccCheckVra7DeploymentConfig(), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckVra7DeploymentExists("vra7_deployment.my_vra7_deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "description", "Test deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "reasons", "Testing the vRA 7 Terraform plugin"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "businessgroup_name", "Development"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.%", "2"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration._leaseDays", "15"), + testAccCheckVra7DeploymentExists("vra7_deployment.this"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.deployment_property", "custom deployment property"), + "vra7_deployment.this", "description", "Terraform deployment"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.businessGroups", "[\"bg1\"]"), + "vra7_deployment.this", "reasons", "Testing the vRA 7 Terraform provider"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.%", "3"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.cpu", "1"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.memory", "2048"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.machine_property", "machine custom property"), - ), - }, - }, - }) -} - -func TestAccVra7DeploymentUpdate_basic(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckVra7DeploymentDestroy, - Steps: []resource.TestStep{ - { - Config: testAccCheckVra7DeploymentConfig(), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckVra7DeploymentExists("vra7_deployment.my_vra7_deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "description", "Test deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "reasons", "Testing the vRA 7 Terraform plugin"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "businessgroup_name", "Development"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.%", "2"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration._leaseDays", "15"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.deployment_property", "custom deployment property"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.businessGroups", "[\"bg1\"]"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.%", "3"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.cpu", "1"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.memory", "2048"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.machine_property", "machine custom property"), + "vra7_deployment.this", "businessgroup_name", "Terraform-BG"), ), }, { Config: testAccCheckVra7DeploymentUpdateConfig(), Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckVra7DeploymentExists("vra7_deployment.my_vra7_deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "description", "Test deployment"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "reasons", "Testing the vRA 7 Terraform plugin"), + testAccCheckVra7DeploymentExists("vra7_deployment.this"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "businessgroup_name", "Development"), + "vra7_deployment.this", "description", "Terraform deployment"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.%", "2"), + "vra7_deployment.this", "reasons", "Testing the vRA 7 Terraform provider"), resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration._leaseDays", "15"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.deployment_property", "updated custom deployment property"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "deployment_configuration.businessGroups", "[\"bg1\"]"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.%", "3"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.cpu", "1"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.memory", "1024"), - resource.TestCheckResourceAttr( - "vra7_deployment.my_vra7_deployment", "resource_configuration.vSphereVM1.machine_property", "updated machine custom property"), + "vra7_deployment.this", "businessgroup_name", "Terraform-BG"), ), }, }, @@ -204,11 +130,9 @@ func testAccCheckVra7DeploymentExists(n string) resource.TestCheckFunc { if !ok { return fmt.Errorf("Not found: %s", n) } - if rs.Primary.ID == "" { return fmt.Errorf("No resource request ID is set") } - return nil } } @@ -218,7 +142,6 @@ func testAccCheckVra7DeploymentDestroy(s *terraform.State) error { if rs.Type != "vra7_deployment" { continue } - _, err := client.GetRequestResourceView(rs.Primary.ID) if err == nil { return err @@ -227,52 +150,69 @@ func testAccCheckVra7DeploymentDestroy(s *terraform.State) error { return nil } -func testAccCheckVra7DeploymentUpdateConfig() string { - return fmt.Sprintf(` -resource "vra7_deployment" "my_vra7_deployment" { -count = 1 -catalog_item_name = "Basic Single Machine" -description = "Test deployment" -reasons = "Testing the vRA 7 Terraform plugin" - -deployment_configuration = { - _leaseDays = "15" - deployment_property = "updated custom deployment property" - businessGroups = << EOF - ["bg1"] - EOF -} -resource_configuration = { - "vSphereVM1.cpu" = 1 - "vSphereVM1.memory" = 1024 - "vSphereVM1.machine_property" = "updated machine custom property" -} -wait_timeout = 20 -businessgroup_name = "Development" -}`) -} - func testAccCheckVra7DeploymentConfig() string { - return fmt.Sprintf(` -resource "vra7_deployment" "my_vra7_deployment" { -count = 1 -catalog_item_name = "Basic Single Machine" -description = "Test deployment" -reasons = "Testing the vRA 7 Terraform plugin" - -deployment_configuration = { - _leaseDays = "15" - deployment_property = "custom deployment property" - businessGroups = << EOF - ["bg1"] - EOF -} -resource_configuration = { - "vSphereVM1.cpu" = 1 - "vSphereVM1.memory" = 2048 - "vSphereVM1.machine_property" = "machine custom property" + return ` +resource "vra7_deployment" "this" { + catalog_item_name = "Terraform-Simple-BP" + description = "Terraform deployment" + reasons = "Testing the vRA 7 Terraform provider" + lease_days = 20 + deployment_configuration = { + "BPCustomProperty" = "custom deployment property" + } + + resource_configuration { + component_name = "vSphere1" + cluster = 2 + configuration = { + cpu = 2 + memory = 2048 + vSphere1CustomProperty = "custom machine property" + } + } + + resource_configuration { + cluster = 2 + component_name = "vSphere2" + configuration = { + cpu = 3 + memory = 2048 + } + } + wait_timeout = 20 + businessgroup_name = "Terraform-BG" +}` } -wait_timeout = 20 -businessgroup_name = "Development" -}`) + +func testAccCheckVra7DeploymentUpdateConfig() string { + return ` +resource "vra7_deployment" "this" { + catalog_item_name = "Terraform-Simple-BP" + description = "Terraform deployment" + reasons = "Testing the vRA 7 Terraform provider" + lease_days = 20 + deployment_configuration = { + "BPCustomProperty" = "custom deployment property" + } + + resource_configuration { + component_name = "vSphere1" + cluster = 1 + configuration = { + cpu = 4 + memory = 2048 + vSphere1CustomProperty = "custom machine property" + } + } + resource_configuration { + cluster = 1 + component_name = "vSphere2" + configuration = { + cpu = 2 + memory = 2048 + } + } + wait_timeout = 20 + businessgroup_name = "Terraform-BG" +}` } diff --git a/website/docs/d/vra7_deployment.html.markdown b/website/docs/d/vra7_deployment.html.markdown new file mode 100644 index 00000000..8422fc74 --- /dev/null +++ b/website/docs/d/vra7_deployment.html.markdown @@ -0,0 +1,84 @@ +--- +layout: "vra7" +page_title: "VMware vRA7: vra7_deployment" +sidebar_current: "docs-vra7-resource-deployment" +description: |- + Provides a VMware vRA7 deployment data source. This can be used to get a vra7_deployment +--- + +# Data Source vra7\_deployment + +Provides a VMware vRA7 deployment data source. This can be used to get a vra7_deployment + +## Example Usages + +### Filter by deployment id + +```hcl +data "vra7_deployment" "this" { + deployment_id = "a0967161-d80f-220c-9c7a-5892025bc3ce" +} +``` +### Filter by catalog item request id + +```hcl +data "vra7_deployment" "this" { + id = "a0967161-d80f-220c-9c7a-5892025bc3ce" +} +``` + +## Argument Reference + +The following arguments are supported: +* `id` - The catalog item request id. +* `deployment_id` - The resource id of the deployment. + +## Attribute Reference + +* `businessgroup_id` - The id of the vRA business group to use for this deployment. +* `businessgroup_name` - The name of the vRA business group to use for this deployment. +* `catalog_item_id` - The id of the catalog item to deploy into vRA. +* `catalog_item_name` - The name of the catalog item to deploy into vRA. +* `description` - Description of the deployment. +* `reasons` - Reasons for requesting the deployment. +* `deployment_configuration` - The configuration of the deployment from the catalog item. All blueprint custom properties including property groups can be added to this block. This property is discussed in detail below. +* `resource_configuration` - The configuration of the individual components from the catalog item. This property is discussed in detail below. +* `lease_days` - Number of lease days remaining for the deployment. NOTE: lease_days 0 means the lease never expires. +* `name` - The name of the deployment. +* `lease_start` - Start date of the lease. +* `lease_end` - End date of the lease. +* `request_status` - The status of the catalog item request. +* `date_created` - The date when the deployment was created. +* `last_updated` - The date when the deployment was last updated after Day 2 operations. +* `tenant_id` - The id of the tenant. +* `owners` - The owners of the deployment. + +## Nested Blocks + +### resource_configuration ### + +This is a list of blocks that contains the machine resource level properties including the custom properties. Each resource_configuration block corresponds to a component in the blueprint/catalog_item. The sample blueprint has one vSphere machine resource/component called vSphereVM1. Properties of this machine can be specified in the config as shown in the example above. The properties like cpu, memory, storage, etc are generic machine properties and their is a custom property as well, called machine_property in the sample blueprint which is required at request time. There can be any number of machines and same format has to be followed to specify properties of other machines as well.All the properties that are required during request, must be specified in the config file. + +The following arguments for resource_configuration block are supported: + +#### Attribute Reference + +* `component_name` - The name of the component/machine resource as in the blueprint/catalog_item +* `configuration` - The machine resource level properties like cpu, memory, storage, custom properties, etc. can be added here. When fetching the state of the machine, this will be populated with a lot of information in the state file. +* `cluster` - Cluster size for this machine resource +* `resource_id` - ID of the machine resource +* `name` - Name of the resource +* `description` - Description of the resource +* `parent_resource_id` - ID of the deployment of which this machine is a part of +* `ip_address` - IP address of the machine +* `request_id` - ID of the catalog item request +* `request_state` - Current state of the request. It can be FAILED, IN_PROGRESS, SUCCESSFUL, etc. +* `resource_type` - Type of resource. It can be a machine resource type (Infrastructure.Virtual) or a deployment type (composition.resource.type.deployment), etc. +* `status` - Status of the machine. It can be On, Off, Unprovisioned, etc. +* `date_created` - The date when the resource was created. +* `last_updated` - The date when the resource was last updated after Day 2 operations. + + +### deployment_configuration ### + +This block contains the deployment level properties including the custom properties and proprty groups. These are not a fixed set of properties but referred from the blueprint. From the example of the BasicSingleMachine blueprint, their is one custom property, called deployment_property which is required at request time. All the properties that are required during request, must be specified in the config file. diff --git a/website/docs/r/deployment.html.markdown b/website/docs/r/deployment.html.markdown index e2f3f205..dcd7bca6 100644 --- a/website/docs/r/deployment.html.markdown +++ b/website/docs/r/deployment.html.markdown @@ -17,24 +17,41 @@ Provides a VMware vRA7 deployment resource. This can be used to deploy vRA7 cata You can refer to the sample blueprint ([here](https://github.com/terraform-providers/terraform-provider-vra7/tree/master/website/docs/r)) to understand how it is translated to following the terraform config ```hcl -resource "vra7_deployment" "my_vra7_deployment" { - count = 1 - catalog_item_name = "Basic Single Machine" - description = "Test deployment" - reasons = "Testing the vRA 7 Terraform plugin" +resource "vra7_deployment" "this" { + count = 1 + catalog_item_name = "CentOS 7.0 x64" + description = "this description" + reason = "this reason" + lease_days = 10 deployment_configuration = { - _leaseDays = "15" - _number_of_instances = 2 - deployment_property = "custom deployment property" + "blueprint_custom_property" = "This is a blueprint custom property" + } + + resource_configuration { + component_name = "Linux 1" + cluster = 2 + configuration = { + cpu = 2 + memory = 2048 + custom_property = "VM custom property" + security_tag = <