#AzureIoT – Creating a folder 📂 in the docker definition in an ☁ Azure IoT Edge

Buy Me A Coffee

Hi !

In my (spanish) podcast I often complain that we always speak about JavaScript and Docker. And, it also applies to my blog. After my yesterday’s post, someone asked

How we can create a folder on the creation process of the Azure IoT module?

This is not an Azure IoT question, is mostly a docker one. And the answer is easy, just add these lines to create and grant permissions to a folder named [fsexchange]

# create folder for file exchange
RUN mkdir /fsexchange
RUN chown 1000 /fsexchange
RUN chmod 700 /fsexchange

RUN chown 1000 /.
RUN chmod 700 /.

And, you need to do this in each one of the docker definition files that you may use. Like

  • Dockerfile.amd64
  • Dockerfile.arm32v7
  • Dockerfile.arm64v8
  • Dockerfile.windows-amd64
  • Dockerfile.amd64.debug
  • Dockerfile.arm32v7.debug
  • Dockerfile.arm64v8.debug
docker definition files to azure Iot Modules

And that’s it! super easy!

Happy coding!

Greetings

El Bruno


References

My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#AzureIoT – Mapping a local ☁ Azure IoT Edge folder module with an Edge device folder 📁

Buy Me A Coffee

Hi !

Another quick post in [Brain backup mode]. This one for a very simple scenario when working with Azure IoT modules:

Mapping a local folder in an Azure IoT Edge module with an Edge device folder

There is a full post on Azure IoT Documentation (see references). And I’ll resume the post here.

Add this configuration to

{
    "HostConfig": {
        "Binds": [
            "[EdgeDeviceFolder]:[AzureIoTModuleFolder]"
        ]
    }
}

Where:

  • EdgeDeviceFolder is the folder on the edge device
  • AzureIoTModuleFolder is the folder on the module, defined the docker image

Of course, you need to grant access to the EdgeDeviceFolder. 2 commands for this

sudo chown 1000 <EdgeDeviceFolder>
sudo chmod 700 <EdgeDeviceFolder>

And that’s it! super easy!

Happy coding!

Greetings

El Bruno


References

My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#AzureIoT – ☁ Azure IoT Edge 1.1.0 release is now GA, experiences upgrading #Ubuntu

Buy Me A Coffee

Hi !

There is a new version of the Azure IoT Edge module: 1.1.0 (see references). So, I decided to upgrade my 2 test devices and maybe share some lessons learned in the process.

Upgrade on Ubuntu 18.04 on a Raspberry Pi 4

As usual I’ll start with a full update

# Update
sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

Similar to the Raspberry Pi OS upgrade process, I found a nice surprise on the update log.

Setting up iotedge (1.1.0-1) ...
===============================================================================

                              Azure IoT Edge

  IMPORTANT: Please update the configuration file located at:

    /etc/iotedge/config.yaml

  with your device's provisioning information. You will need to restart the
  'iotedge' service for these changes to take effect.

  To restart the 'iotedge' service, use:

    'systemctl restart iotedge'

    - OR -

    /etc/init.d/iotedge restart

  These commands may need to be run with sudo depending on your environment.

===============================================================================
Setting up friendly-recovery (0.2.38ubuntu1.2) ...
Setting up moby-engine (20.10.3+azure-1) ...
Processing triggers for systemd (237-3ubuntu10.44) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for ureadahead (0.100.0-21) ...
ureadahead will be reprofiled on next reboot
Processing triggers for libc-bin (2.27-3ubuntu1.4) ...
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
  u-boot-tools
1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 139 kB of archives.
After this operation, 7168 B of additional disk space will be used.
Get:1 http://ports.ubuntu.com/ubuntu-ports bionic-updates/main arm64 u-boot-tools arm64 2020.10+dfsg-1ubuntu0~18.04.2 [139 kB]

So, the module is updated. However I need to setup the device provisioning information again and restart the service. In the references section I shared the officials docs for these steps.

Let’s edit the provisioning information

# Update provisioning information (if necessary)
sudo nano /etc/iotedge/config.yaml

My file still have all the valid information (again, like the Raspberry Pi upgrade process)

No changes here, let’s restart IoT Edge

# restart iotedge service
systemctl restart iotedge

Important, authentication is required in this step:

So, I may missed something here. I run the update command for the module, and it’s already on the 1.1 version. However the iotedge list of modules is still using the 1.0

Update runtime settings on Azure IoT Portal

I now I can fix this in the Azure IoT Portal. In the device definition, I need to update the Runtime settings to use the version 1.1. (image is just for reference)

A couple of seconds later, everything works as expected !

iotedge module updated to v1.1.0

Recap

So, the steps to update were these commands

# Update
sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

# Update provisioning information (if necessary)
sudo nano /etc/iotedge/config.yaml

# restart iotedge service
systemctl restart iotedge

Then update the Runtime Settings on the Azure IoT Portal.

And restart iotedge one more time !

Happy coding!

Greetings

El Bruno


References

My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#AzureIoT – ☁ Azure IoT Edge 1.1.0 release is now GA, experiences upgrading #RaspberryPi OS

Buy Me A Coffee

Hi !

There is a new version of the Azure IoT Edge module: 1.1.0 (see references). So, I decided to upgrade my 2 test devices and maybe share some lessons learned in the process.

Upgrade on Raspberry Pi OS

As usual I’ll start with a full update

# Update
sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

And I found a nice surprise on the update log.

Setting up iotedge (1.1.0-1) ...
===============================================================================

                              Azure IoT Edge

  IMPORTANT: Please update the configuration file located at:

    /etc/iotedge/config.yaml

  with your device's provisioning information. You will need to restart the
  'iotedge' service for these changes to take effect.

  To restart the 'iotedge' service, use:

    'systemctl restart iotedge'

    - OR -

    /etc/init.d/iotedge restart

  These commands may need to be run with sudo depending on your environment.

===============================================================================

So, the module is updated. However I need to setup the device provisioning information again and restart the service. In the references section I shared the officials docs for these steps.

Let’s edit the provisioning information

# Update provisioning information (if necessary)
sudo nano /etc/iotedge/config.yaml

My file still have all the valid information

No changes here, let’s restart IoT Edge

# restart iotedge service
systemctl restart iotedge

Important, authentication is required in this step

So, I may missed something here. I run the update command for the module, and it’s already on the 1.1 version. However the iotedge list of modules is still using the 1.0

Update runtime settings on Azure IoT Portal

I now I can fix this in the Azure IoT Portal. In the device definition, I need to update the Runtime settings to use the version 1.1.

A couple of seconds later, everything works as expected !

Recap

So, the steps to update were these commands

# Update
sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

# Update provisioning information (if necessary)
sudo nano /etc/iotedge/config.yaml

# restart iotedge service
systemctl restart iotedge

Then update the Runtime Settings on the Azure IoT Portal.

And restart iotedge one more time !

I’ll test this on Ubuntu and share my experiences also there.

Happy coding!

Greetings

El Bruno


References

My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#Event – “Let’s code a drone to follow faces syncing everything with Azure IoT” at the Codegen 2021, Verona, Italy. Session Resources

Buy Me A Coffee

Hi !

I had a great time early in the day with some friend from Italy.

drone telemetry in azure IoT

As usual, time for slides and code:

Slides

Code

https://github.com/elbruno/events/tree/main/2021%2002%2013%20CodeGen%20Verona%20Italy%20Drone%20Azure%20IoT

My Session – Video

All Sessions – Videos

All +40 sessions here http://codegen2021.azurewebsites.net/videos

Resources

Happy coding!

Greetings

El Bruno


#AzureIoT – ☁ Azure IoT Explorer, in preview and awesome

Buy Me A Coffee

Hi !

I found this tool sometime ago, and it was a great moment. The Azure IoT explorer is a graphical tool for interacting with and devices connected to your IoT hub. You can check references for the official installation process.

And just in case, here is a small preview of the tool showing the live telemetry events on a specific device. At the right there is a console app sending messages to a Azure IoT Device. The device processes the messages and send some specific telemetry.

The app is very straight forward. First, just add an IoTHub using a Connection String.

Once connected, we can inspect the devices on the Hub

And in each device we have some actions available. During my current days, Telemetry and Direct method are the 2 most useful ones !

Again, it was a great surprise find this tool. !

Bonus: supports dark theme !

Happy coding!

Greetings

El Bruno


References


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Install ☁ Azure IoT Edge in a Raspberry Pi with Ubuntu 18.04, lessons learned and some tips

Buy Me A Coffee

Hi !

With the new Raspberry Pi Imager, the installation of an OS for a Raspberry Pi is much more easier than earlier days. We even have the chance to install several different Ubuntu versions, Desktop, Server and Core.

raspberry pi imager with ubuntu versions

This is cool, however it does not fit with the Azure IoT Edge prerequisites for an Ubuntu/Linux installation. I make some tests with Ubuntu Server 20.10 and Azure IoT, and they didn’t work. The current supported version is Ubuntu Server 18.04.

Install Ubuntu 18.04.5

After a quick search I found a working version in the Ubuntu repository:

Ubuntu 18.04.5 LTS (Bionic Beaver) (see references)

After the version is installed, it comes out of the box with SSH enabled. So, once I detected the device IP in my network, I SSHed with this device.

Note: there is no raspi-config here. So simple actions like Wifi configuration are tricky. Lucky us, internet have answers for almost everything.

The command [] also works to display current OS information.

ubuntu@ubuntu:~$ hostnamectl
   Static hostname: ubuntu
         Icon name: computer
        Machine ID: 6fcd625f39104d699f50b8de729f34ba
           Boot ID: 712910e5c2df43969db5384756225408
  Operating System: Ubuntu 18.04.5 LTS
            Kernel: Linux 5.4.0-1028-raspi
      Architecture: arm64

Finally, as a GUI person, a cooler option is to use [neofetch]. You may need to install it with

sudo apt install neofetch

And later, some magic !

Update Ubuntu

Now it was time to update the OS. Standard command

sudo -- sh -c 'apt-get update; apt-get upgrade -y; apt-get dist-upgrade -y; apt-get autoremove -y; apt-get autoclean -y'

and it took a lot !

And now all the steps from the official Install Azure IoT Edge for Linux works!

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Connect to ☁ Azure Blob Storage on IoT Edge using Microsoft Azure Storage Explorer

Buy Me A Coffee

Hi !

This is an easy one, however I may write these down to complete the series of posts on Azure Storage Blob on IoT Edge.

Azure Storage Explorer is a free tool to easily manage your Azure cloud storage resources anywhere, from Windows, macOS, or Linux (see references).

You can add your Azure Credentials to access to your cloud resources, and if you are using a Storage in the Edge, you can create a connection string based on this template

DefaultEndpointsProtocol=http;BlobEndpoint=http://<device IP>:11002/<account name>;AccountName=<account name>;AccountKey=<account key>;

Where the 2 of 3 main values comes from the Azure Storage Blob on IoT Edge configuration (See previous post)

  • Device IP
  • Account Name
  • Account Key

Once you created your connection string, you can add a new connection in the tool. Right click on [Local and Attached // Storage Accounts], select [Connect to Azure Storage] and select the use Connection String option.

In the previous image we can see the access to 2 edge devices a Raspbian and an Ubuntu device. The 2nd one deserves a new post, there are some know errors here.

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Deploy Azure Blob Storage on IoT Edge, lessons learned #AzureIoT

Buy Me A Coffee

Hi !

I’ve been using Azure Blob Storage for a very long time. I won’t share details so you won’t laugh a lot. And now, in the amazing Azure IoT world, we have the chance to use a custom module in out Edge devices. As the documentation mention, this module is useful in scenarios:

  • where data needs to be stored locally until it can be processed or transferred to the cloud. This data can be videos, images, finance data, hospital data, or any other unstructured data.
  • when devices are located in a place with limited connectivity.
  • when you want to efficiently process the data locally to get low latency access to the data, such that you can respond to emergencies as quickly as possible.
  • when you want to reduce bandwidth costs and avoid transferring terabytes of data to the cloud. You can process the data locally and send only the processed data to the cloud.

In the references section I shared the official documentation. So, while you are reading the doc and following the steps, I’ll share some insights.

Install module from the Marketplace

As usual, we can deploy the module using a custom JSON definition. For 1-to-1 devices, install the module from the market place is a quick option. Just go your IoT Edge device panel, select [Set Modules], and in the IoT Edge Modules section, select [+ Marketplace Module].

azure iot install module from marketplace

On the search panel, search for “storage” and check the results.

azure iot install module from marketplace azure blob storage on Iot edge

2 clicks later, the module is in your definition. However, some config is needed. I’ll go on this for next section.

Settings / Config for the Azure Blob Storage on IoT Edge module

Time for module configuration. In the official documentation describes the 2 main sections to work on.

The main values that we will need are these ones, so you may need to create / define them in advance.

  • Storage account name. Account names should be 3 to 24 characters long, with lowercase letters and numbers. No spaces.
  • Storage account key. Replace with a 64-byte base64 key. You can generate a key with tools like GeneratePlus. You’ll use these credentials to access the blob storage from other modules.

Environment Variables

You need to complete these values in the [Environment Variables] section.

azure iot azure blob storage set environment variables

Module Name needs to be lowecase

At some point the documentation mention / suggest to change the name to lowercase. Please do this, I had several issues on my Raspberry Pi, and somehow a lowercase name fixed some deployment issues.

Container Create Options

This section is mostly straight forward. I used the Account Name and Account Key defined in previous steps and I also created a specific folder to store the blob [/home/pi/azurestorageiot]. Again, here the documentation makes everything easy to configure.

{
  "Env":[
    "LOCAL_STORAGE_ACCOUNT_NAME=<your storage account name>",
    "LOCAL_STORAGE_ACCOUNT_KEY=<your storage account key>"
  ],
  "HostConfig":{
    "Binds":[
            "/home/pi/azurestorageiot"
    ],
    "PortBindings":{
      "11002/tcp":[{"HostPort":"11002"}]
    }
  }
}

Module Twin Settings options

Main remark here: while you are in testing mode, set the uploadOn to false (line 8). Otherwise the module will

  • be installed with default options and no containers
  • Search for a local container [cont1] and will log tons of errors, because the container does not exist

And the lines 11 to 15 were also tricky to understand. I was a expecting a source / target pair to define source and target containers, however the definition works different.

For each source container (the ones in our IoT Edge device), we need to create a section (lines 12 to 14) with the name of the IoT Edge container (cont1), and the target definition with the name of the Azure container (line 13).

Again, I spend sometime here, mostly because I was with a different mindset before understanding how this works.

And that’s it! I deployed and tested this module in a Raspberry Pi and it works like a charm!

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Build #docker 🐳 images from Visual Studio @Code remotely using a Raspberry Pi. #AzureIoT

Buy Me A Coffee

Hi !

The post title is very descriptive, however let me share some more context on why I needed this scenario.

I’m currently working in an Azure IoT custom module. I got installed the Azure IoT extensions on Visual Studio Code, so everything works great on the Hello World mode. Once I wrote my code logic in C#, I can easily build my IoT Edge Solution.

My default configuration focus to an amd64 platform. I got this defined in my settings.json

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "amd64",
        "alias": null
    }
}

This is a sample log output, using my local docker in Windows 10. Everything works fine.

[+] Building 0.7s (16/16) FINISHED
 => [internal] load build definition from Dockerfile.amd64                                                                                                                                                                                  0.1s
 => => transferring dockerfile: 38B                                                                                                                                                                                                         0.1s 
 => [internal] load .dockerignore                                                                                                                                                                                                           0.1s 
 => => transferring context: 34B                                                                                                                                                                                                            0.1s 
 => [internal] load metadata for mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim                                                                                                                                                      0.5s
 => [internal] load metadata for mcr.microsoft.com/dotnet/core/sdk:3.1-buster                                                                                                                                                               0.5s 
 => [build-env 1/6] FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster@sha256:0fece15a102530aa2dad9d247bc0d05db6790917696377fc56a8465604ef1aff                                                                                               0.0s
 => [internal] load build context                                                                                                                                                                                                           0.0s 
 => => transferring context: 446B                                                                                                                                                                                                           0.0s 
 => [stage-1 1/4] FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim@sha256:e84300e54209cad66611b0b483cfe8c007d3bb5450388ff58e7f15d025917817                                                                                        0.0s 
 => CACHED [stage-1 2/4] WORKDIR /app                                                                                                                                                                                                       0.0s
 => CACHED [build-env 2/6] WORKDIR /app                                                                                                                                                                                                     0.0s 
 => CACHED [build-env 3/6] COPY *.csproj ./                                                                                                                                                                                                 0.0s 
 => CACHED [build-env 4/6] RUN dotnet restore                                                                                                                                                                                               0.0s 
 => CACHED [build-env 5/6] COPY . ./                                                                                                                                                                                                        0.0s 
 => CACHED [build-env 6/6] RUN dotnet publish -c Release -o out                                                                                                                                                                             0.0s 
 => CACHED [stage-1 3/4] COPY --from=build-env /app/out ./                                                                                                                                                                                  0.0s 
 => CACHED [stage-1 4/4] RUN useradd -ms /bin/bash moduleuser                                                                                                                                                                               0.0s 
 => exporting to image                                                                                                                                                                                                                      0.0s 
 => => exporting layers                                                                                                                                                                                                                     0.0s 
 => => writing image sha256:8d38fb01640fb566e761231c1a646df35bb83f24dfbb0fdea92bc4db62576e12                                                                                                                                                0.0s 
 => => naming to <...>

If I want to change the target platform to build an arm32 image, I change my settings.json file to:

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "arm32v7",
        "alias": null
    }
}

However I got this ugly error, trying to build this

=> CACHED [build-env 2/6] WORKDIR /app                                                                                                                                                                                                     0.0s 
 => CACHED [build-env 3/6] COPY *.csproj ./                                                                                                                                                                                                 0.0s 
 => ERROR [build-env 4/6] RUN dotnet restore                                                                                                                                                                                                0.5s
------
 > [build-env 4/6] RUN dotnet restore:
#10 0.450 A fatal error occurred, the folder [/usr/share/dotnet/host/fxr] does not contain any version-numbered child folders
------
executor failed running [/bin/sh -c dotnet restore]: exit code: 131

There seems to be an error here, outside of my docker knowledge.

Of course, a test path could involve the use of a remote ARM32 Docker Environment to build my Azure IoT Edge C# Module. I followed my previous posts steps to enable SSH password-less access to my Raspberry Pi and also manage docker as non-root users (see references).

Now I can define a specific DOCKER EXECUTOR to build my images. I changed my settings.json file to define this executor. Note that you must define a standard SSH connection, this may involve your device name or IP Address.

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "arm32v7",
        "alias": null
    },
    "azure-iot-edge.executor.env": {
         "DOCKER_HOST": "ssh://pi@<Raspberry PI Address>"
    }
}

Important: once you changed your settings.json configuration, you need to reload your VSCode environment. You can close and open the IDE, or even better, invoke the [Reload Window] command.

visual studio code reload window

Now we can sucesfully build the Azure IoT module. the output log will be similar to this one:

Sending build context to Docker daemon  24.06kB
Step 1/12 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster-arm32v7 AS build-env
 ---> 3cdfdcca7cd6
Step 2/12 : WORKDIR /app
 ---> Using cache
 ---> 8c3252bfdefb
Step 3/12 : COPY *.csproj ./
 ---> Using cache
 ---> e6fe11cb659e
Step 4/12 : RUN dotnet restore
 ---> Using cache
 ---> 12453c83de65
Step 5/12 : COPY . ./
 ---> Using cache
 ---> 107c1dfccfa3
Step 6/12 : RUN dotnet publish -c Release -o out
 ---> Using cache
 ---> 603a93c80b0e
Step 7/12 : FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim-arm32v7
 ---> 2bb8fc093aad
Step 8/12 : WORKDIR /app
 ---> Using cache
 ---> c8a93a2a7b90
Step 9/12 : COPY --from=build-env /app/out ./
 ---> Using cache
 ---> f72a492270ad
Step 10/12 : RUN useradd -ms /bin/bash moduleuser
 ---> Using cache
 ---> 082a4568867b
Step 11/12 : USER moduleuser
 ---> Using cache
 ---> 2c5e7b562289
Step 12/12 : ENTRYPOINT ["dotnet", "UDPFwder.dll"]
 ---> Using cache
 ---> f523123332f9
Successfully built f523123332f9
Successfully tagged <...>
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

And that’s it! Using a Raspberry Pi device to build Net Azure IoT modules seems to be the fix to build ARM32 platforms.

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware
Azure IoT


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría: