Use Visual Studio Code to develop and debug modules for Azure IoT Edge
Applies to: IoT Edge 1.4
This article shows you how to use Visual Studio Code to develop and debug IoT Edge modules in multiple languages and multiple architectures. On your development computer, you can use Visual Studio Code to attach and debug your module in a local or remote module container.
You can choose either the Azure IoT Edge Dev Tool CLI or the Azure IoT Edge tools for Visual Studio Code extension as your IoT Edge development tool. Use the tool selector button at the beginning to choose your tool option for this article.
Visual Studio Code supports writing IoT Edge modules in the following programming languages:
- C# and C# Azure Functions
- C
- Python
- Node.js
- Java
Azure IoT Edge supports the following device architectures:
- X64
- ARM32
- ARM64
For more information about supported operating systems, languages, and architectures, see Language and architecture support.
When using the Visual Studio Code IoT Edge extension, you can also launch and debug your module code in the IoT Edge Simulator.
You can also use a Windows development computer and debug modules in a Linux container using IoT Edge for Linux on Windows (EFLOW). For more information about using EFLOW for developing modules, see Tutorial: Develop IoT Edge modules with Linux containers using IoT Edge for Linux on Windows.
If you aren't familiar with the debugging capabilities of Visual Studio Code, see Visual Studio Code debugging.
Prerequisites
You can use a computer or a virtual machine running Windows, macOS, or Linux as your development machine. On Windows computers, you can develop either Windows or Linux modules. To develop Linux modules, use a Windows computer that meets the requirements for Docker Desktop.
Install Visual Studio Code first and then add the following extensions:
- Azure IoT Edge extension.
- Azure IoT Hub extension.
To build and deploy your module image, you need Docker to build the module image and a container registry to hold the module image:
Download and install a Docker compatible container management system on your development machine to build and run your module images. For example, install Docker Community Edition.
Create an Azure Container Registry or Docker Hub to store your module images.
Tip
You can use a local Docker registry for prototype and testing purposes instead of a cloud registry.
Install the Azure CLI.
Install the Python-based Azure IoT Edge Dev Tool with the following command to enable you to debug, run, and test your IoT Edge solution. Python (3.6/3.7) and Pip3 are required.
pip3 install iotedgedev
Note
If you have multiple Python versions, including pre-installed Python 2.7 (for example, on Ubuntu or macOS), make sure you use
pip3
to install IoT Edge Dev Tool (iotedgedev).For more information setting up your development machine, see iotedgedev development setup.
To stay current on the testing environment for IoT Edge, see the test-coverage list.
Install prerequisites specific to the language you're developing in:
- Install .NET Core SDK
- Install C# Visual Studio Code extension
To test your module on a device, you need:
- An active IoT Hub with at least one IoT Edge device.
- A physical IoT Edge device or a virtual device. To create a virtual device in Azure, follow the steps in the quickstart for Linux or Windows.
Create IoT Edge module
The following steps show you how to create an IoT Edge module in your preferred development language. You start by creating a solution, and then generating the first module in that solution. Each solution can contain multiple modules.
The IoT Edge Dev Tool simplifies Azure IoT Edge development to simple commands driven by environment variables. It gets you started with IoT Edge development with the IoT Edge Dev Container and IoT Edge solution scaffolding that contains a default module and all the required configuration files.
Create a directory for your solution with the filepath of your choice. Change into your
iotedgesolution
directory.mkdir c:\dev\iotedgesolution
Use the iotedgedev solution init command to create a solution and set up your Azure IoT Hub in the development language of your choice.
The iotedgedev solution init script prompts you to complete several steps including:
- Authenticate to Azure
- Choose an Azure subscription
- Choose or create a resource group
- Choose or create an Azure IoT Hub
- Choose or create an Azure IoT Edge device
After solution creation, these main files are in the solution:
A .vscode folder contains configuration file launch.json.
A modules folder that has subfolders for each module. Within the subfolder for each module, the module.json file controls how modules are built and deployed.
An .env file lists your environment variables. The environment variable for the container registry is localhost:5000 by default. If Azure Container Registry is your registry, set an Azure Container Registry username and password. Get these values from your container registry's Settings > Access keys menu in the Azure portal. The CONTAINER_REGISTRY_SERVER is the Login server of your registry.
For example:
CONTAINER_REGISTRY_SERVER="myacr.azurecr.io" CONTAINER_REGISTRY_USERNAME="myacr" CONTAINER_REGISTRY_PASSWORD="<registry_password>"
In production scenarios, you should use service principals to provide access to your container registry instead of the .env file. For more information, see Manage access to your container registry.
Note
The environment file is only created if you provide an image repository for the module. If you accepted the localhost defaults to test and debug locally, then you don't need to declare environment variables.
Two module deployment files named deployment.template.json and deployment.debug.template.json list the modules to deploy to your device. By default, the list includes the IoT Edge system modules (edgeAgent and edgeHub) and sample modules such as:
- filtermodule is a sample module that implements a simple filter function.
- SimulatedTemperatureSensor module that simulates data you can use for testing. For more information about how deployment manifests work, see Learn how to use deployment manifests to deploy modules and establish routes. For more information on how the simulated temperature module works, see the SimulatedTemperatureSensor.csproj source code.
Note
The exact modules installed may depend on your language of choice.
Use Visual Studio Code and the Azure IoT Edge extension. You start by creating a solution, and then generating the first module in that solution. Each solution can contain multiple modules.
Select View > Command Palette.
In the command palette, enter and run the command Azure IoT Edge: New IoT Edge Solution.
Browse to the folder where you want to create the new solution and then select Select folder.
Enter a name for your solution.
Select a module template for your preferred development language to be the first module in the solution.
Enter a name for your module. Choose a name that's unique within your container registry.
Provide the name of the module's image repository. Visual Studio Code autopopulates the module name with localhost:5000/<your module name>. Replace it with your own registry information. Use localhost if you use a local Docker registry for testing. If you use Azure Container Registry, then use Login server from your registry's settings. The sign-in server looks like <registry name>.azurecr.io. Only replace the localhost:5000 part of the string so that the final result looks like <registry name>.azurecr.io/<your module name>.
Visual Studio Code takes the information you provided, creates an IoT Edge solution, and then loads it in a new window.
There are four items within the solution:
- A .vscode folder contains debug configurations.
- A modules folder has subfolders for each module. Within the folder for each module, there's a file called module.json that controls how modules are built and deployed. You need to modify this file to change the module deployment container registry from a localhost to a remote registry. At this point, you only have one module. But you can add more if needed.
- An .env file lists your environment variables. The environment variable for the container registry is localhost by default. If Azure Container Registry is your registry, set an Azure Container Registry username and password. Get these values from your container registry's Settings > Access keys menu in the Azure portal. The CONTAINER_REGISTRY_SERVER is the Login server of your registry.
For example:
CONTAINER_REGISTRY_SERVER="myacr.azurecr.io"
CONTAINER_REGISTRY_USERNAME="myacr"
CONTAINER_REGISTRY_PASSWORD="<my_acr_password>"
In production scenarios, you should use service principals to provide access to your container registry instead of the .env file. For more information, see Manage access to your container registry.
Note
The environment file is only created if you provide an image repository for the module. If you accepted the localhost defaults to test and debug locally, then you don't need to declare environment variables.
Two module deployment files named deployment.template.json and deployment.debug.template list the modules to deploy to your device. By default, the list includes the IoT Edge system modules and sample modules including the SimulatedTemperatureSensor module that simulates data you can use for testing.
For more information about deployment manifests, see Learn how to use deployment manifests to deploy modules and establish routes. For more information about the simulated temperature module, see the SimulatedTemperatureSensor.csproj source code.
Add more modules
To add more modules to your solution, change to the modules directory and add them there.
cd modules
Run the command Azure IoT Edge: Add IoT Edge Module from the command palette. You can also right-click the modules folder or the deployment.debug.template.json
file in the Visual Studio Code Explorer view and then select Add IoT Edge Module.
Install the modules using your language of choice.
Install the .NET IoT Edge C# template.
dotnet new -i Microsoft.Azure.IoT.Edge.Module
Create a new directory folder in the modules folder and change directory to the new folder. For example,
mkdir SampleModule
thencd SampleModule
.Use the IoT Edge .NET template to add a new C# module. For example, the following command adds a new module named SampleModule and configures module.json to use myacr.azurecr.io/samplemodule as the image repository.
dotnet new aziotedgemodule --name SampleModule --repository myacr.azurecr.io/samplemodule
The command creates the module in the current directory and configures the module.json file with the repository information.
Develop your module
The default module code that comes with the solution is located at the following location:
The sample modules allow you to build the solution, push to your container registry, and deploy to a device. This process lets you start testing without modifying any code. The sample module takes input from a source (in this case, the SimulatedTemperatureSensor module that simulates data) and pipes it to IoT Hub.
When you're ready to customize the template with your own code, use the Azure IoT Hub SDKs to build modules that address the key needs for IoT solutions such as security, device management, and reliability.
Debug without a container using IoT Edge simulator
Your module requires use of a ModuleClient object in the default module code so that it can start, run, and route messages. You'll also use the default input channel input1 to take action when the module receives messages.
Set up IoT Edge simulator
IoT Edge modules need an IoT Edge environment to run and debug. You can use an IoT Edge simulator on your development machine instead of running the full IoT Edge security subsystem and runtime. You can either simulate a device to debug solutions with multiple modules, or simulate a single module application.
Option 1: Simulate an IoT Edge solution:
- In the Explorer tab on the left side, expand the Azure IoT Hub section. Right-click on your IoT Edge device ID, and then select Setup IoT Edge Simulator to start the simulator with the device connection string.
- You can see the IoT Edge Simulator has been successfully set up by reading the progress detail in the integrated terminal.
Option 2: Simulate a single IoT Edge module:
In the Visual Studio Code command palette, run the command Azure IoT Edge: Start IoT Edge Hub Simulator for Single Module.
Provide the names of any inputs that you want to test with your module. If you're using the default sample code, use the value input1.
The command triggers the iotedgehubdev CLI and then starts the IoT Edge simulator and a testing utility module container. You can see the output similar to the following in the integrated terminal if the simulator has been started in single module mode successfully. The output includes an example
curl
command to send a message to the simulator. You'll use thecurl
command later.D:\Workspaces\EdgeSolution>iotedgehubdev start -i "input1" ...
You can use the Docker Explorer view in Visual Studio Code to see the module's running status.
The edgeHubDev container is the core of the local IoT Edge simulator. It can run on your development machine without the IoT Edge security daemon and provides environment settings for your native module app or module containers. The input container exposes REST APIs to help bridge messages to the target input channel on your module.
Debug module in launch mode
After the simulator has been started successfully, you can debug your module code.
Prepare your environment for debugging, set a breakpoint in your module, and select the debug configuration to use.
In the Visual Studio Code integrated terminal, change the directory to the <your module name> folder, and then run the following command to build .NET Core application.
dotnet build
Open the file ModuleBackgroundService.cs
and add a breakpoint.
Navigate to the Visual Studio Code Debug view by selecting the debug icon from the menu on the left or by typing Ctrl+Shift+D
. Select the debug configuration <your module name> Local Debug (.NET Core) from the dropdown.
Note
If your .NET Core TargetFramework
is not consistent with your program path in launch.json
, you'll need to manually update the program path in launch.json
to match the TargetFramework
in your .csproj file so that Visual Studio Code can successfully launch this program.
Select Start Debugging or press F5 to start the debug session.
In the Visual Studio Code integrated terminal, run the following command to send a Hello World message to your module. This is the command shown in previous steps when you set up IoT Edge simulator.
curl --header "Content-Type: application/json" --request POST --data '{"inputName": "input1","data":"hello world"}' http://localhost:53000/api/v1/messages
Note
If you are using Windows, making sure the shell of your Visual Studio Code integrated terminal is Git Bash or WSL Bash. You cannot run the
curl
command from a PowerShell or command prompt.Tip
You can also use PostMan or other API tools to send messages through instead of
curl
.In the Visual Studio Code Debug view, you'll see the variables in the left panel.
To stop your debugging session, select the Stop button or press Shift + F5, and then run Azure IoT Edge: Stop IoT Edge Simulator in the command palette to stop the simulator and clean up.
Debug in attach mode using IoT Edge simulator
Your default solution contains two modules, one is a simulated temperature sensor module and the other is the pipe module. The simulated temperature sensor sends messages to the pipe module and then the messages go to the IoT Hub. In the module folder you created, there are several Docker files for different container types. Use any of the files that end with the extension .debug to build your module for testing.
Currently, debugging in attach mode is supported only as follows:
- C# modules, including modules for Azure Functions, support debugging in Linux amd64 containers
- Node.js modules support debugging in Linux amd64 and arm32v7 containers, and Windows amd64 containers
- Java modules support debugging in Linux amd64 and arm32v7 containers
Tip
You can switch among options for the default platform for your IoT Edge solution by clicking the item in the Visual Studio Code status bar.
Set up IoT Edge simulator for IoT Edge solution
On your development machine, you can start an IoT Edge simulator instead of installing the IoT Edge security daemon so that you can run your IoT Edge solution.
In the Explorer tab on the left side, expand the Azure IoT Hub section. Right-click on your IoT Edge device ID, and then select Setup IoT Edge Simulator to start the simulator with the device connection string.
You can see the successful set up of the IoT Edge Simulator by reading the progress detail in the integrated terminal.
Build and run container for debugging and debug in attach mode
Open your module file (
ModuleBackgroundService.cs
,app.js
,App.java
, or<your module name>.cs
) and add a breakpoint.In the Visual Studio Code Explorer view, right-click the
deployment.debug.template.json
file for your solution and then select Build and Run IoT Edge solution in Simulator. You can watch all the module container logs in the same window. You can also navigate to the Docker view to watch container status.Navigate to the Visual Studio Code Debug view and select the debug configuration file for your module. The debug option name should be similar to <your module name> Remote Debug
Select Start Debugging or press F5. Select the process to attach to.
In Visual Studio Code Debug view, you see the variables in the left panel.
To stop the debugging session, first select the Stop button or press Shift + F5, and then select Azure IoT Edge: Stop IoT Edge Simulator from the command palette.
Note
The preceding example shows how to debug IoT Edge modules on containers. It added exposed ports to your module's container createOptions
settings. After you finish debugging your modules, we recommend you remove these exposed ports for production-ready IoT Edge modules.
For modules written in C#, including Azure Functions, this example is based on the debug version of Dockerfile.amd64.debug
, which includes the .NET Core command-line debugger (VSDBG) in your container image while building it. After you debug your C# modules, we recommend that you directly use the Dockerfile without VSDBG for production-ready IoT Edge modules.
Debug a module with the IoT Edge runtime
In each module folder, there are several Docker files for different container types. Use any of the files that end with the extension .debug to build your module for testing.
When you debug modules using this method, your modules are running on top of the IoT Edge runtime. The IoT Edge device and your Visual Studio Code can be on the same machine, or more typically, Visual Studio Code is on the development machine and the IoT Edge runtime and modules are running on another physical machine. In order to debug from Visual Studio Code, you must:
- Set up your IoT Edge device, build your IoT Edge modules with the .debug Dockerfile, and then deploy to the IoT Edge device.
- Update
launch.json
so that Visual Studio Code can attach to the process in a container on the remote machine. You can find this file in the.vscode
folder in your workspace, and it updates each time you add a new module that supports debugging. - Use Remote SSH debugging to attach to the container on the remote machine.
Build and deploy your module to an IoT Edge device
In Visual Studio Code, open the deployment.debug.template.json deployment manifest file. The deployment manifest describes the modules to be configured on the targeted IoT Edge device. Before deployment, you need to update your Azure Container Registry credentials and your module images with the proper createOptions
values. For more information about createOption values, see How to configure container create options for IoT Edge modules.
If you're using an Azure Container Registry to store your module image, add your credentials to the edgeAgent > settings > registryCredentials section in deployment.debug.template.json. Replace myacr with your own registry name in both places and provide your password and Login server address. For example:
"modulesContent": { "$edgeAgent": { "properties.desired": { "schemaVersion": "1.1", "runtime": { "type": "docker", "settings": { "minDockerVersion": "v1.25", "loggingOptions": "", "registryCredentials": { "myacr": { "username": "myacr", "password": "<your_acr_password>", "address": "myacr.azurecr.io" } } } }, ...
Add or replace the following stringified content to the createOptions value for each system (edgeHub and edgeAgent) and custom module (for example, tempSensor) listed. Change the values if necessary.
"createOptions": "{\"HostConfig\":{\"PortBindings\":{\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}],\"443/tcp\":[{\"HostPort\":\"443\"}]}}}"
For example, the filtermodule configuration should be similar to:
"filtermodule": { "version": "1.0", "type": "docker", "status": "running", "restartPolicy": "always", "settings": { "image": "myacr.azurecr.io/filtermodule:0.0.1-amd64", "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}],\"443/tcp\":[{\"HostPort\":\"443\"}]}}}" }
- In the Visual Studio Code command palette, run the command Azure IoT Edge: Build and Push IoT Edge solution.
- Select the
deployment.debug.template.json
file for your solution. - In the Azure IoT Hub > Devices section of the Visual Studio Code Explorer view, right-click the IoT Edge device name for deployment and then choose Create Deployment for Single Device.
Tip
To confirm that the device you've chosen is an IoT Edge device, select it to expand the list of modules and verify the presence of $edgeHub and $edgeAgent. Every IoT Edge device includes these two modules.
- Navigate to your solution's config folder, select the
deployment.debug.amd64.json
file, and then select Select Edge Deployment Manifest.
You can check your container status from your device or virtual machine by running the docker ps
command in a terminal. You should see your container listed after running the command. If your Visual Studio Code and IoT Edge runtime are running on the same machine, you can also check the status in the Visual Studio Code Docker view.
Important
If you're using a private registry like Azure Container Registry (ACR) for your images, you may need to authenticate to push images. Use docker login <ACR login server>
or az acr login --name <ACR name>
to authenticate.
Build module Docker image
Use the module's Dockerfile to build the module Docker image.
docker build --rm -f "<DockerFilePath>" -t <ImageNameAndTag> "<ContextPath>"
For example, to build the image for the local registry or an Azure container registry, use the following commands:
# Build the image for the local registry
docker build --rm -f "./modules/filtermodule/Dockerfile.amd64.debug" -t localhost:5000/filtermodule:0.0.1-amd64 "./modules/filtermodule"
# Or build the image for an Azure Container Registry
docker build --rm -f "./modules/filtermodule/Dockerfile.amd64.debug" -t myacr.azurecr.io/filtermodule:0.0.1-amd64 "./modules/filtermodule"
Push module Docker image
Push your module image to the local registry or a container registry.
docker push <ImageName>
For example:
# Push the Docker image to the local registry
docker push localhost:5000/filtermodule:0.0.1-amd64
# Or push the Docker image to an Azure Container Registry
az acr login --name myacr
docker push myacr.azurecr.io/filtermodule:0.0.1-amd64
Deploy the module to the IoT Edge device.
Use the IoT Edge Azure CLI set-modules command to deploy the modules to the Azure IoT Hub. For example, to deploy the modules defined in the deployment.debug.amd64.json file to IoT Hub my-iot-hub for the IoT Edge device my-device, use the following command:
az iot edge set-modules --hub-name my-iot-hub --device-id my-device --content ./deployment.debug.template.json --login "HostName=my-iot-hub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=<SharedAccessKey>"
Tip
You can find your IoT Hub connection string in the Azure portal in your IoT Hub > Security settings > Shared access policies > iothubowner.
Debug your module
Open the module file for your development language and add a breakpoint:
Add your breakpoint to the file ModuleBackgroundService.cs
.
To debug modules on a remote device, you can use Remote SSH debugging in Visual Studio Code.
To enable Visual Studio Code remote debugging, install the Remote Development extension. For more information about Visual Studio Code remote debugging, see Visual Studio Code Remote Development.
For details on how to use Remote SSH debugging in Visual Studio Code, see Remote Development using SSH
In the Visual Studio Code Debug view, select the debug configuration file for your module. By default, the .debug Dockerfile, module's container createOptions
settings, and the launch.json
file use localhost.
Select Start Debugging or select F5. Select the process to attach to. In the Visual Studio Code Debug view, you see variables in the left panel.
Debug using Docker Remote SSH
The Docker and Moby engines support SSH connections to containers allowing you to debug in Visual Studio Code connected to a remote device. You need to meet the following prerequisites before you can use this feature.
Configure Docker SSH tunneling
Follow the steps in Docker SSH tunneling to configure SSH tunneling on your development computer. SSH tunneling requires public/private key pair authentication and a Docker context defining the remote device endpoint.
Connecting to Docker requires root-level privileges. Follow the steps in Manage docker as a non-root user to allow connection to the Docker daemon on the remote device. When you finish debugging, you may want to remove your user from the Docker group.
In Visual Studio Code, use the Command Palette (Ctrl+Shift+P) to issue the Docker Context: Use command to activate the Docker context pointing to the remote machine. This command causes both Visual Studio Code and Docker CLI to use the remote machine context.
Tip
All Docker commands use the current context. Remember to change context back to default when you are done debugging.
To verify the remote Docker context is active, list the running containers on the remote device:
docker ps
The output should list the containers running on the remote device similar:
PS C:\> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a317b8058786 myacr.azurecr.io/filtermodule:0.0.1-amd64 "dotnet filtermodule…" 24 hours ago Up 6 minutes filtermodule d4d949f8dfb9 mcr.microsoft.com/azureiotedge-hub:1.4 "/bin/sh -c 'echo \"$…" 24 hours ago Up 6 minutes 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:5671->5671/tcp, :::5671->5671/tcp, 0.0.0.0:8883->8883/tcp, :::8883->8883/tcp, 1883/tcp edgeHub 1f0da9cfe8e8 mcr.microsoft.com/azureiotedge-simulated-temperature-sensor:1.0 "/bin/sh -c 'echo \"$…" 24 hours ago Up 6 minutes tempSensor 66078969d843 mcr.microsoft.com/azureiotedge-agent:1.4 "/bin/sh -c 'exec /a…" 24 hours ago Up 6 minutes edgeAgent
In the .Visual Studio Code directory, add a new configuration to launch.json by opening the file in Visual Studio Code. Select Add configuration then choose the matching remote attach template for your module. For example, the following configuration is for .NET Core. Change the value for the -H parameter in PipeArgs to your device DNS name or IP address.
"configurations": [ { "name": "Remote Debug IoT Edge Module (.NET Core)", "type": "coreclr", "request": "attach", "processId": "${command:pickRemoteProcess}", "pipeTransport": { "pipeProgram": "docker", "pipeArgs": [ "-H", "ssh://user@my-device-vm.eastus.cloudapp.azure.com:22", "exec", "-i", "filtermodule", "sh", "-c" ], "debuggerPath": "~/vsdbg/vsdbg", "pipeCwd": "${workspaceFolder}", "quoteArgs": true }, "sourceFileMap": { "/app": "${workspaceFolder}/modules/filtermodule" }, "justMyCode": true },
Remotely debug your module
In Visual Studio Code Debug view, select the debug configuration Remote Debug IoT Edge Module (.NET Core).
Select Start Debugging or select F5. Select the process to attach to.
In the Visual Studio Code Debug view, you see the variables in the left panel.
In Visual Studio Code, set breakpoints in your custom module.
When a breakpoint is hit, you can inspect variables, step through code, and debug your module.
Note
The preceding example shows how to debug IoT Edge modules on remote containers. The example adds a remote Docker context and changes to the Docker privileges on the remote device. After you finish debugging your modules, set your Docker context to default and remove privileges from your user account.
See this IoT Developer blog entry for an example using a Raspberry Pi device.
Next steps
After you've built your module, learn how to deploy Azure IoT Edge modules from Visual Studio Code.
To develop modules for your IoT Edge devices, understand and use Azure IoT Hub SDKs.
Feedback
Submit and view feedback for