terraform-cdk
terraform-cdk copied to clipboard
CDKTF convert Errors on array on Nutanix Provider/Virtual Machne Resource, Synthing manually made python cdktf leaves array fields blank
Expected Behavior
TL;DR
After converting the Nutanix Provider, following the provided docs, converting a vm resource from terraform using the cdktf convert tool errors on conversion, it's expected to convert the provider and convert the main.tf
file
cat main.tf | cdktf convert --provider 'nutanix/nutanix' --language python > test.py
results in a array error from the provider-generator/ .../resource-parser.js
This lines up with the behavior experienced if one converts the provider the themselves and constructs a Terraform Stack in Python code themselves. Using the Terraform stack in this way, results in array fields on the Nutanix VM array parameter fields being empty in the cdktf.out.json.
Read
The cdktf conversion process should be able to convert this Nutanix VM Terraform manifest. Following instructions from the cdktf convert subcommand.
The cdktf being used via Python, manually writing the code to create a Nutanix Terraform stack should convert all fields of a VM without blank fields.
These two different yet connected scenarios we believe incorrectly convert types of arrays for disk_list, and nic_list.
Actual Behavior
See steps to reproduce.
Steps to Reproduce
Manual Conversion of Provider and Running Created Stack code:
The manual conversion process is documented in a issue in the nutanix github space:
https://github.com/nutanix/terraform-provider-nutanix/issues/624
After contacting Nutanix they stated to go through Hashicorp Support due to them not supporting CDKTF. They are a trusted partner according to their registry page.
Automated Conversion Steps
- cdktf installed
- With files installed at root level below execute:
cat main.tf | cdktf convert --provider 'nutanix/nutanix' --language python --stack > test.py
Output
──> cat main.tf | cdktf convert --provider 'nutanix/nutanix' --language python > test.py 7 ↵ ──()─┘
Internal Error: unexpected array
Error: unexpected array
at Parser.renderAttributeType (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:182:23)
at Parser.renderAttributesForBlock (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:241:31)
at Parser.resourceFrom (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:102:33)
at ResourceParser.parse (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:431:33)
at /Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/provider-generator.js:65:121
at Array.map (<anonymous>)
at TerraformProviderGenerator.buildResourceModels (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/provider-generator.js:65:75)
at /Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/hcl2cdk/lib/index.js:92:31
at Array.reduce (<anonymous>)
at convertToTypescript (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/hcl2cdk/lib/index.js:90:79)
Collecting Debug Information...
/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/yoga-layout-prebuilt/yoga-layout/build/Release/nbind.js:53
throw ex;
^
Error: unexpected array
at Parser.renderAttributeType (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:182:23)
at Parser.renderAttributesForBlock (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:241:31)
at Parser.resourceFrom (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:102:33)
at ResourceParser.parse (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/resource-parser.js:431:33)
at /Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/provider-generator.js:65:121
at Array.map (<anonymous>)
at TerraformProviderGenerator.buildResourceModels (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/provider-generator/lib/get/generator/provider-generator.js:65:75)
at /Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/hcl2cdk/lib/index.js:92:31
at Array.reduce (<anonymous>)
at convertToTypescript (/Users/billyjenkins/.nvm/versions/node/v20.2.0/lib/node_modules/cdktf-cli/node_modules/@cdktf/hcl2cdk/lib/index.js:90:79) {
language: 'python',
__type: 'Internal'
}
Node.js v20.2.0
Note
I tested this on node 18 to node 20 using NVM.
Files:
cdktf.json
{
"language": "python",
"app": "python test.py",
"projectId": "50bac325-efb8-4839-b885-242d94bf5c43",
"sendCrashReports": "false",
"terraformProviders": [
"nutanix/nutanix"
],
"terraformModules": [],
"codeMakerOutput": "imports",
"context": {
"excludeStackIdFromLogicalIds": "true",
"allowSepCharsInLogicalIds": "true"
}
}
main.tf
#############################################################################
# Example main.tf for Nutanix + Terraform
#
# Author: [email protected]
#
# This script is a quick demo of how to use the following provider objects:
# - providers
# - terraform-provider-nutanix
# - resources
# - nutanix_virtual_machine
# - nutanix_subnet
# - nutanix_image
# - data sources
# - nutanix_clusters
# - script Variables
# - clusterid's for targeting clusters within prism central
#
# Feel free to reuse, comment, and contribute, so that others may learn.
#
#############################################################################
### Define Provider Info for terraform-provider-nutanix
### This is where you define the credentials for ** Prism Central **
###
### NOTE:
### While it may be possible to use Prism Element directly, Nutanix's
### provider is not structured or tested for this. Using Prism Central will
### give the broadest capabilities across the board
terraform{
required_providers{
nutanix = {
source = "nutanix/nutanix"
version = "1.9.3"
}
}
}
provider "nutanix" {
username = "terraform_admin"
password = ""
endpoint = "10.6.100.20"
insecure = true
port = 9440
}
data "nutanix_clusters" "clusters" {
}
##########################
### Data Sources
##########################
### These are "lookups" to simply define an already existing object as a
### plain text name
### This is useful when managing a nutanix prism central instance from multiple
### state files, or deploying terraform into an existing / brownfield environment
### Virtual Machine Data Sources
# data "nutanix_virtual_machine" "nutanix_virtual_machine" {
# vm_id = nutanix_virtual_machine.vm1.id
# }
## Image Data Sources
data "nutanix_image" "s2019" {
image_id = "e"
}
### Subnet Data Sources
data "nutanix_subnet" "VLAN_618" {
subnet_id = "cd38c43c-fa7a-****-9bf0-***********"
}
### Cluster Data Sources
data "nutanix_cluster" "cluster1" {
cluster_id = "0005f342-2700-****-467a-*********"
}
### Virtual Machine Resources
## Related Product Docs:
## https://portal.nutanix.com/#/page/docs/details?targetId=Prism-Central-Guide-Prism-v58:mul-vm-create-manage-pc-c.html
## Related Developer Docs:
## http://developer.nutanix.com/reference/prism_central/v3/#vms
## Implementation Notes on VMs
# These are VMs managed by Prism Central, which could span across AHV, ESXi, or
# Prism Self Service Portal. That said, if you're doing ESXi, it is most likely
# that would deploy them via a VMware provider against vCenter APIs.
resource "nutanix_virtual_machine" "ggtest01" {
# General Information
name = "test01"
description = "demo Frontend Web Server"
num_vcpus_per_socket = 2
num_sockets = 1
memory_size_mib = 16000
# What cluster will this VLAN live on?
cluster_uuid = data.nutanix_cluster.cluster1.cluster_id
# What networks will this be attached to?
nic_list {
# subnet_reference is saying, which VLAN/network do you want to attach here?
subnet_uuid = data.nutanix_subnet.VLAN_618.subnet_id
# Used to set static IP.
# ip_endpoint_list {
# ip = "10.xx.xx.xx"
# type = "ASSIGNED"
# }
}
# What disk/cdrom configuration will this have?
disk_list {
# data_source_reference in the Nutanix API refers to where the source for
# the disk device will come from. Could be a clone of a different VM or a
# image like we're doing here
data_source_reference = {
kind = "image"
uuid = data.nutanix_image.s2019.image_id}
device_properties {
disk_address = {
device_index = 0
adapter_type = "SCSI"
}
device_type = "DISK"
}
storage_config {
storage_container_reference {
kind = "storage_container"
uuid = "17a6c666-db20-4179-9a7c-*********"
}
}
}
}
# Show IP address
#output "ip_address" {
# value = nutanix_virtual_machine.demo-01-web.nic_list_status[0].ip_endpoint_list[0].ip
#}
Versions
cdktf debug language: python cdktf-cli: 0.18.0 node: v20.2.0 terraform: 1.5.6 arch: arm64 os: darwin 21.3.0
Providers
┌─────────────────┬──────────────────┬───────┬────────────┬──────────────┬─────────────────┐ │ Provider Name │ Provider Version │ CDKTF │ Constraint │ Package Name │ Package Version │ ├─────────────────┼──────────────────┼───────┼────────────┼──────────────┼─────────────────┤ │ nutanix/nutanix │ 1.9.3 │ │ │ │ │
Gist
No response
Possible Solutions
We believe the struct that nutanix has defined in their provider is incorrectly setting type, or that sequence is not being picked up by jsii.
Converted code using cdktf get
:
class VirtualMachineConfig(_cdktf_9a9027ec.TerraformMetaArguments):
def __init__(
self,
*,
connection: typing.Optional[typing.Union[typing.Union[_cdktf_9a9027ec.SSHProvisionerConnection, typing.Dict[builtins.str, typing.Any]], typing.Union[_cdktf_9a9027ec.WinrmProvisionerConnection, typing.Dict[builtins.str, typing.Any]]]] = None,
count: typing.Optional[typing.Union[jsii.Number, _cdktf_9a9027ec.TerraformCount]] = None,
depends_on: typing.Optional[typing.Sequence[_cdktf_9a9027ec.ITerraformDependable]] = None,
for_each: typing.Optional[_cdktf_9a9027ec.ITerraformIterator] = None,
lifecycle: typing.Optional[typing.Union[_cdktf_9a9027ec.TerraformResourceLifecycle, typing.Dict[builtins.str, typing.Any]]] = None,
provider: typing.Optional[_cdktf_9a9027ec.TerraformProvider] = None,
provisioners: typing.Optional[typing.Sequence[typing.Union[typing.Union[_cdktf_9a9027ec.FileProvisioner, typing.Dict[builtins.str, typing.Any]], typing.Union[_cdktf_9a9027ec.LocalExecProvisioner, typing.Dict[builtins.str, typing.Any]], typing.Union[_cdktf_9a9027ec.RemoteExecProvisioner, typing.Dict[builtins.str, typing.Any]]]]] = None,
cluster_uuid: builtins.str,
name: builtins.str,
availability_zone_reference: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
boot_device_disk_address: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
boot_device_mac_address: typing.Optional[builtins.str] = None,
boot_device_order_list: typing.Optional[typing.Sequence[builtins.str]] = None,
boot_type: typing.Optional[builtins.str] = None,
categories: typing.Optional[typing.Union[_cdktf_9a9027ec.IResolvable, typing.Sequence[typing.Union[VirtualMachineCategories, typing.Dict[builtins.str, typing.Any]]]]] = None,
cloud_init_cdrom_uuid: typing.Optional[builtins.str] = None,
description: typing.Optional[builtins.str] = None,
disk_list: typing.Optional[typing.Union[_cdktf_9a9027ec.IResolvable, typing.Sequence[typing.Union["VirtualMachineDiskListStruct", typing.Dict[builtins.str, typing.Any]]]]] = None, <<< Sequence
enable_cpu_passthrough: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
enable_script_exec: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
gpu_list: typing.Optional[typing.Union[_cdktf_9a9027ec.IResolvable, typing.Sequence[typing.Union["VirtualMachineGpuListStruct", typing.Dict[builtins.str, typing.Any]]]]] = None,
guest_customization_cloud_init_custom_key_values: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
guest_customization_cloud_init_meta_data: typing.Optional[builtins.str] = None,
guest_customization_cloud_init_user_data: typing.Optional[builtins.str] = None,
guest_customization_is_overridable: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
guest_customization_sysprep: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
guest_customization_sysprep_custom_key_values: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
guest_os_id: typing.Optional[builtins.str] = None,
hardware_clock_timezone: typing.Optional[builtins.str] = None,
id: typing.Optional[builtins.str] = None,
is_vcpu_hard_pinned: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
machine_type: typing.Optional[builtins.str] = None,
memory_size_mib: typing.Optional[jsii.Number] = None,
ngt_credentials: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
ngt_enabled_capability_list: typing.Optional[typing.Sequence[builtins.str]] = None,
nic_list: typing.Optional[typing.Union[_cdktf_9a9027ec.IResolvable, typing.Sequence[typing.Union["VirtualMachineNicListStruct", typing.Dict[builtins.str, typing.Any]]]]] = None, <<<<<< Sequence not converting correctly it appears
num_sockets: typing.Optional[jsii.Number] = None,
num_vcpus_per_socket: typing.Optional[jsii.Number] = None,
num_vnuma_nodes: typing.Optional[jsii.Number] = None,
nutanix_guest_tools: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
owner_reference: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
parent_reference: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
power_state_mechanism: typing.Optional[builtins.str] = None,
project_reference: typing.Optional[typing.Mapping[builtins.str, builtins.str]] = None,
serial_port_list: typing.Optional[typing.Union[_cdktf_9a9027ec.IResolvable, typing.Sequence[typing.Union["VirtualMachineSerialPortListStruct", typing.Dict[builtins.str, typing.Any]]]]] = None,
should_fail_on_script_failure: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
timeouts: typing.Optional[typing.Union["VirtualMachineTimeouts", typing.Dict[builtins.str, typing.Any]]] = None,
use_hot_add: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
vga_console_enabled: typing.Optional[typing.Union[builtins.bool, _cdktf_9a9027ec.IResolvable]] = None,
) -> None:
Workarounds
There's none, we can't convert this module and use it using the provided conversion tools, and trying to create the python code manually results in unexpected blank Sequence
fields.
Anything Else?
We also tested conversion using javascript instead of python with VERY similar errors.
References
No response
Help Wanted
- [ ] I'm interested in contributing a fix myself
Community Note
- Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
- Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request
- If you are interested in working on this issue or have submitted a pull request, please leave a comment
I'll leave it for the rest of the team to comment on the technical details, but FWIW this looks like a duplicate of #3111 (though this one has a lot more detail and should help the team with debugging; thank you!).
We are willing to work with whoever to get this resolved, we are using the CDKTF extensively and this is a blocker for us. Thank you @xiehan
@DanielMSchmidt could we schedule a time to meetup? I'm available whenever you are.
This has been sitting quite a while @xiehan can we get a status update by chance?
I tried to reproduce this and for me it worked fine with 0.20.1
. For me it resulted in this cdk code (i made it a stack and copied it into a fresh project):
#!/usr/bin/env python
from constructs import Construct
from cdktf import App, TerraformStack
from cdktf import Token
#
# Provider bindings are generated by running `cdktf get`.
# See https://cdk.tf/provider-generation for more details.
#
from imports.nutanix.data_nutanix_cluster import DataNutanixCluster
from imports.nutanix.data_nutanix_clusters import DataNutanixClusters
from imports.nutanix.data_nutanix_image import DataNutanixImage
from imports.nutanix.data_nutanix_subnet import DataNutanixSubnet
from imports.nutanix.provider import NutanixProvider
from imports.nutanix.virtual_machine import VirtualMachine
class MyStack(TerraformStack):
def __init__(self, scope, name):
super().__init__(scope, name)
NutanixProvider(self, "nutanix",
endpoint="10.6.100.20",
insecure=True,
password="",
port=Token.as_string(9440),
username="terraform_admin"
)
cluster1 = DataNutanixCluster(self, "cluster1",
cluster_id="0005f342-2700-****-467a-*********"
)
DataNutanixClusters(self, "clusters")
s2019 = DataNutanixImage(self, "s2019",
image_id="e"
)
vlan618 = DataNutanixSubnet(self, "VLAN_618",
subnet_id="cd38c43c-fa7a-****-9bf0-***********"
)
VirtualMachine(self, "ggtest01",
cluster_uuid=Token.as_string(cluster1.cluster_id),
description="demo Frontend Web Server",
disk_list=[{
"data_source_reference": {
"kind": "image",
"uuid": Token.as_string(s2019.image_id)
},
"device_properties": {
"device_type": "DISK",
"disk_address": {
"adapter_type": "SCSI",
"device_index": Token.as_string(0)
}
},
"storage_config": {
"storage_container_reference": [{
"kind": "storage_container",
"uuid": "17a6c666-db20-4179-9a7c-*********"
}
]
}
}
],
memory_size_mib=16000,
name="test01",
nic_list=[{
"subnet_uuid": Token.as_string(vlan618.subnet_id)
}
],
num_sockets=1,
num_vcpus_per_socket=2
)
app = App()
MyStack(app, "tmp.axnj99KS6U")
app.synth()
And I got this synthed JSON which to me looks also correct
{
"//": {
"metadata": {
"backend": "local",
"stackName": "tmp.axnj99KS6U",
"version": "0.20.1"
},
"outputs": {
}
},
"data": {
"nutanix_cluster": {
"cluster1": {
"//": {
"metadata": {
"path": "tmp.axnj99KS6U/cluster1",
"uniqueId": "cluster1"
}
},
"cluster_id": "0005f342-2700-****-467a-*********"
}
},
"nutanix_clusters": {
"clusters": {
"//": {
"metadata": {
"path": "tmp.axnj99KS6U/clusters",
"uniqueId": "clusters"
}
}
}
},
"nutanix_image": {
"s2019": {
"//": {
"metadata": {
"path": "tmp.axnj99KS6U/s2019",
"uniqueId": "s2019"
}
},
"image_id": "e"
}
},
"nutanix_subnet": {
"VLAN_618": {
"//": {
"metadata": {
"path": "tmp.axnj99KS6U/VLAN_618",
"uniqueId": "VLAN_618"
}
},
"subnet_id": "cd38c43c-fa7a-****-9bf0-***********"
}
}
},
"provider": {
"nutanix": [
{
"endpoint": "10.6.100.20",
"insecure": true,
"password": "",
"port": 9440,
"username": "terraform_admin"
}
]
},
"resource": {
"nutanix_virtual_machine": {
"ggtest01": {
"//": {
"metadata": {
"path": "tmp.axnj99KS6U/ggtest01",
"uniqueId": "ggtest01"
}
},
"cluster_uuid": "${data.nutanix_cluster.cluster1.cluster_id}",
"description": "demo Frontend Web Server",
"disk_list": [
{
}
],
"memory_size_mib": 16000,
"name": "test01",
"nic_list": [
{
}
],
"num_sockets": 1,
"num_vcpus_per_socket": 2
}
}
},
"terraform": {
"backend": {
"local": {
"path": "/private/var/folders/m4/673s3vwn1_g7c72bmvq521g00000gn/T/tmp.axnj99KS6U/terraform.tmp.axnj99KS6U.tfstate"
}
},
"required_providers": {
"nutanix": {
"source": "nutanix/nutanix",
"version": "1.9.5"
}
}
}
}
Could you try again with the current cdktf version?
@DanielMSchmidt if you notice your synthesized json output has a empty disk_list
and nic_list
that is under resource.nutanix_virtual_machine.ggtest01
. The issue is present in your example.
Those fields should not be empty, as you try to run that via a apply
or deploy
it will throw a error.
Looking for a status update as it's been months @DanielMSchmidt
I'm going to lock this issue because it has been closed for 30 days. This helps our maintainers find and focus on the active issues. If you've found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.