Search code examples
pythonterraformterraform-provider-azureterraform-cdk

Azurerm provider Python CDKTF import issues


I am currently trying to figure out why my python cdktf package is not pulling all the correct azurerm provider imports.

Here is my project structure:

azure_vd
| > imports
| .gitignore
| cdktf.json
| help
| main-test.py
| main.py
| Pipfile
| Pipfile.lock

The steps I have completed:

mkdir cdktf-azure-vd
cd cdktf-azure-vd/
cdktf init --template=python

cdktf provider add "azurerm@~>3.63.0"

Added to the cdktf.json file "terraformProviders": ["azurerm@~> 3.63.0"],

I then ran cdktf get which generated the imports dir. However, I did not see the following classes here:

...
from imports.azurerm import (
    AzurermProvider,
    VirtualDesktopWorkspace,
    VirtualDesktopHostPool,
    VirtualDesktopApplicationGroup,
    VirtualNetwork,
)
...

So I followed up with pipenv install cdktf-cdktf-provider-azurerm.

I am not sure what else I am missing, unless the provider class names have changed? Am I setting up this cdktf python project incorrectly? Is there a Pipenv piece I am missing? This is my first time using cdktf, so apologies for any naivety.

The error when I run cdk synth is as follows:


⠹  Synthesizing
[2023-07-04T18:01:23.074] [ERROR] default - Traceback (most recent call last):
  File "/home/mason/Documents/CODE/mason_azure_vd/main.py", line 4, in <module>

[2023-07-04T18:01:23.075] [ERROR] default -     from imports.azurerm import (
ERROR: cdktf encountered an error while synthesizing

Synth command: pipenv run python main.py
Error:         non-zero exit code 1

Command output on stderr:

    Traceback (most recent call last):
      File "/home/mason/Documents/CODE/mason_azure_vd/main.py", line 4, in <module>
        from imports.azurerm import (
    ImportError: cannot import name 'AzurermProvider' from 'imports.azurerm' (/home/mason/Documents/CODE/mason_azure_vd/imports/azurerm/__init__.py)

This makes sense, because I am not seeing AzurermProvider, VirtualDesktopWorkspace, VirtualDesktopHostPool, VirtualDesktopApplicationGroup, VirtualNetwork in the imports directory. However, everything I have searched the past two days, including online examples from Terraform on github, point to these class names being correct ?

For posterity here are is some of my code:

main.py

from constructs import Construct
from cdktf import App, TerraformStack, CloudBackend, NamedCloudWorkspace
from imports.azurerm import (
    AzurermProvider,
    VirtualDesktopWorkspace,
    VirtualDesktopHostPool,
    VirtualDesktopApplicationGroup,
    VirtualNetwork,
)


class MyStack(TerraformStack):
    def __init__(self, scope: Construct, id: str):
        super().__init__(scope, id)

        # Initialize the Azure provider
        AzurermProvider(self, "azure", features=[{}])

        # Create a Virtual Network
        virtual_network = VirtualNetwork(
            self,
            "virtual-network",
            name="my-vnet",
            location="westus2",
            resource_group_name="virtual-desktop-resource-group",
            address_space=["10.0.0.0/16"],
            subnets=[{"name": "default", "address_prefix": "10.0.0.0/24"}],
        )

        # Create an Azure Virtual Desktop Workspace
        workspace = VirtualDesktopWorkspace(
            self,
            "workspace",
            name="virtual-desktop-workspace",
            location="westus2",
            resource_group_name="virtual-desktop-resource-group",
        )

        # Create an Azure Virtual Desktop Host Pool
        host_pool = VirtualDesktopHostPool(
            self,
            "host-pool",
            name="virtual-desktop-host-pool",
            location="westus2",
            resource_group_name="virtual-desktop-resource-group",
            workspace_id=workspace.id,
            friendly_name="Virtual Desktop Host Pool",
            type="Pooled",
            vm_template="Windows-11",
            vm_disk_type="Premium",
            vm_disk_size_gb=128,
        )

        # Create an Azure Virtual Desktop Application Group

        application_group = VirtualDesktopApplicationGroup(
            self,
            "application-group",
            name="virtual-desktop-application-group",
            location="westus2",
            resource_group_name="virtual-desktop-application-group-resource-group",
            workspace_id=workspace.id,
            host_pool_id=host_pool.id,
            friendly_name="Virtual Desktop Application Group",
            application_group_type="RemoteApp",
            allow_public_access=True,
        )


app = App()
stack = MyStack(app, "azure_vd")
CloudBackend(
    stack,
    hostname="app.terraform.io",
    organization="redacted-org",
    workspaces=NamedCloudWorkspace("azure_vd_poc"),
)

app.synth()

cdktf.json

{
  "language": "python",
  "app": "pipenv run python main.py",
  "projectId": "redacted project id",
  "sendCrashReports": "true",
  "terraformProviders": ["azurerm@~> 3.63.0"],
  "terraformModules": [],
  "codeMakerOutput": "imports",
  "context": {

  }
}

pipfile:

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[requires]
python_version = "3.11"

[dev-packages]
black = "*"

[packages]
cdktf-cdktf-provider-azurerm = "~=9.0.2"

output from pipenv graph:

cdktf-cdktf-provider-azurerm==9.0.2
  - cdktf [required: >=0.17.0,<0.18.0, installed: 0.17.0]
    - constructs [required: >=10.0.25,<11.0.0, installed: 10.2.68]
      - jsii [required: >=1.84.0,<2.0.0, installed: 1.84.0]
        - attrs [required: >=21.2,<24.0, installed: 23.1.0]
        - cattrs [required: >=1.8,<23.2, installed: 23.1.2]
          - attrs [required: >=20, installed: 23.1.0]
        - importlib-resources [required: >=5.2.0, installed: 5.12.0]
        - publication [required: >=0.0.3, installed: 0.0.3]
        - python-dateutil [required: Any, installed: 2.8.2]
          - six [required: >=1.5, installed: 1.16.0]
        - typeguard [required: ~=2.13.3, installed: 2.13.3]
        - typing-extensions [required: >=3.7,<5.0, installed: 4.7.1]
      - publication [required: >=0.0.3, installed: 0.0.3]
      - typeguard [required: ~=2.13.3, installed: 2.13.3]
    - jsii [required: >=1.79.0,<2.0.0, installed: 1.84.0]
      - attrs [required: >=21.2,<24.0, installed: 23.1.0]
      - cattrs [required: >=1.8,<23.2, installed: 23.1.2]
        - attrs [required: >=20, installed: 23.1.0]
      - importlib-resources [required: >=5.2.0, installed: 5.12.0]
      - publication [required: >=0.0.3, installed: 0.0.3]
      - python-dateutil [required: Any, installed: 2.8.2]
        - six [required: >=1.5, installed: 1.16.0]
      - typeguard [required: ~=2.13.3, installed: 2.13.3]
      - typing-extensions [required: >=3.7,<5.0, installed: 4.7.1]
    - publication [required: >=0.0.3, installed: 0.0.3]
    - typeguard [required: ~=2.13.3, installed: 2.13.3]
  - constructs [required: >=10.0.0,<11.0.0, installed: 10.2.68]
    - jsii [required: >=1.84.0,<2.0.0, installed: 1.84.0]
      - attrs [required: >=21.2,<24.0, installed: 23.1.0]
      - cattrs [required: >=1.8,<23.2, installed: 23.1.2]
        - attrs [required: >=20, installed: 23.1.0]
      - importlib-resources [required: >=5.2.0, installed: 5.12.0]
      - publication [required: >=0.0.3, installed: 0.0.3]
      - python-dateutil [required: Any, installed: 2.8.2]
        - six [required: >=1.5, installed: 1.16.0]
      - typeguard [required: ~=2.13.3, installed: 2.13.3]
      - typing-extensions [required: >=3.7,<5.0, installed: 4.7.1]
    - publication [required: >=0.0.3, installed: 0.0.3]
    - typeguard [required: ~=2.13.3, installed: 2.13.3]
  - jsii [required: >=1.84.0,<2.0.0, installed: 1.84.0]
    - attrs [required: >=21.2,<24.0, installed: 23.1.0]
    - cattrs [required: >=1.8,<23.2, installed: 23.1.2]
      - attrs [required: >=20, installed: 23.1.0]
    - importlib-resources [required: >=5.2.0, installed: 5.12.0]
    - publication [required: >=0.0.3, installed: 0.0.3]
    - python-dateutil [required: Any, installed: 2.8.2]
      - six [required: >=1.5, installed: 1.16.0]
    - typeguard [required: ~=2.13.3, installed: 2.13.3]
    - typing-extensions [required: >=3.7,<5.0, installed: 4.7.1]
  - publication [required: >=0.0.3, installed: 0.0.3]
  - typeguard [required: ~=2.13.3, installed: 2.13.3]
packaging==23.1

Solution

  • For posterity I needed to change how my imports were being called. I was able to manually look through the imports dir and Terraform docs to determine the correct imports and methods.

    Here is an example of my update imports and code in main.py

    from constructs import Construct
    from cdktf import App, TerraformStack, CloudBackend, NamedCloudWorkspace
    from imports.azurerm import (
        provider,
        virtual_desktop_workspace,
        virtual_desktop_host_pool,
        virtual_desktop_application_group,
        virtual_network,
        subnet,
    )
    
    
    class MyStack(TerraformStack):
        def __init__(self, scope: Construct, id: str):
            super().__init__(scope, id)
    
            # Initialize the Azure provider
            provider.AzurermProvider(self, "azure", features={})
    
            # Create a Virtual Network
            virtual_net = virtual_network.VirtualNetwork(
                self,
                "virtual-network",
                name="my-vnet",
                location="westus2",
                resource_group_name="virtual-desktop-resource-group",
                address_space=["10.0.0.0/16"],
            )
    
            # Create the virtual subnet for the virtual network
            virtual_subnet = subnet.Subnet(
                self,
                name="azure-vd-subnet",
                resource_group_name="virtual-desktop-resource-group",
                virtual_network_name=virtual_net.name,
                address_prefixes=["10.0.1.0/24"],
                id_="azure-vd-subnet-id",
            )
    
            # Create an Azure Virtual Desktop Workspace
            workspace = virtual_desktop_workspace.VirtualDesktopWorkspace(
                self,
                "workspace",
                name="virtual-desktop-workspace",
                location="westus2",
                resource_group_name="virtual-desktop-resource-group",
            )
    
            # Create an Azure Virtual Desktop Host Pool
            host_pool = virtual_desktop_host_pool.VirtualDesktopHostPool(
                self,
                "host-pool",
                name="virtual-desktop-host-pool",
                location="westus2",
                resource_group_name="virtual-desktop-resource-group",
                friendly_name="Virtual Desktop Host",
                type="Personal",
                load_balancer_type="Persistent",
            )
    
            # Create an Azure Virtual Desktop Application Group
            application_group = (
                virtual_desktop_application_group.VirtualDesktopApplicationGroup(
                    self,
                    "application-group",
                    name="virtual-desktop-application-group",
                    type="Desktop",
                    default_desktop_display_name="azure-vd",
                    location="westus2",
                    resource_group_name="virtual-desktop-application-group-resource-group",
                    host_pool_id=host_pool.id,
                    friendly_name="Virtual Desktop Application Group",
                )
            )
    
    
    app = App()
    stack = MyStack(app, "azure_vd")
    CloudBackend(
        stack,
        hostname="app.terraform.io",
        organization="redacted",
        workspaces=NamedCloudWorkspace("azure_vd_poc"),
    )
    
    app.synth()
    

    I am now successfully able to cdktf synth which is generating the cdktf.out dir, stacks dir and manifest.json correctly.

    EDIT:

    My initial answer works, but to my understanding it is not completely correct as it is just a "local" import.

    The correct way is calling the cdktf_cdktf_provider_azurerm.

    from constructs import Construct
    from cdktf import TerraformStack, AzurermBackend
    from cdktf_cdktf_provider_azurerm import (
        data_azurerm_client_config,
        nat_gateway,
        nat_gateway_public_ip_association,
        network_security_group,
        network_security_rule,
        provider,
        public_ip,
        resource_group,
        subnet,
        subnet_nat_gateway_association,
        subnet_network_security_group_association,
        virtual_network,
        virtual_network_gateway,
    )