From azure
Automates Azure IoT Edge module scaffolding, manifest configuration, and shared contract generation with intelligent project structure detection. Use when the user wants to create, scaffold, or set up a new IoT Edge module, or add a new module to an edge deployment.
npx claudepluginhub atc-net/atc-agentic-toolkit --plugin azureThis skill uses the workspace's default tool permissions.
Before scaffolding a module, gather the following information from the user:
assets/template-.dockerignoreassets/template-.gitignoreassets/template-Dockerfile.amd64assets/template-Dockerfile.amd64.debugassets/template-GlobalUsings.csassets/template-LoggingBuilderExtensions.csassets/template-ModuleConstants.csassets/template-Program.csassets/template-Service.csassets/template-ServiceLoggerMessages.csassets/template-base.deployment.manifest.jsonassets/template-launchSettings.jsonassets/template-module.jsonassets/template.csprojreferences/customizations.mdreferences/deployment-manifests.mdreferences/module-structure.mdscripts/detect_project_structure.pyscripts/manage_solution.pyscripts/scan_manifests.pyPrepares apps for Azure deployment by generating Bicep/Terraform infrastructure, azure.yaml configs, and Dockerfiles. Use for creating, modernizing, or deploying to App Service, Container Apps, functions.
Provides IoT edge computing patterns including gateway architecture, edge vs cloud processing, ML inference on edge hardware, K3s/Azure IoT Edge containers, data sync, and offline resilience.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Share bugs, ideas, or general feedback.
Before scaffolding a module, gather the following information from the user:
Follow these steps in order to scaffold a new IoT Edge module:
Run the project structure detection script to identify existing patterns:
python scripts/detect_project_structure.py --root .
If modules_base_path wasn't found or doesn't exist, ask the user via AskUserQuestion:
src/IoTEdgeModules/modules/Processing detection output:
config_source is "saved", use silentlyconfig_source is "detected", present findings and ask for confirmation via AskUserQuestion:
python scripts/detect_project_structure.py --root . --saveAsk the user for:
Required:
Optional Features:
A. Private NuGet Feed
B. Shared Contracts Project
Convert the user-provided module name to required formats:
ModuleName (PascalCase):
modulename (lowercase):
Confirm with user using AskUserQuestion tool:
Present the module details and ask for confirmation:
Display in the question description:
Module will be created as:
• C# class name: <ModuleName>
• Module ID: <modulename>
• Directory: <modules_base_path>/<modulename>/
Do NOT assume "Yes" or proceed without using AskUserQuestion tool and getting explicit user confirmation
Create the module directory:
<modules_base_path>/<modulename>/
Check if directory already exists (MUST use this exact bash syntax):
test -d "<modules_base_path>/<modulename>" && echo "EXISTS" || echo "NOT_EXISTS"
Note: Do NOT use Windows CMD syntax like if exist. Always use Unix bash syntax as shown above.
Use the template files in assets/ to generate module files with runtime substitutions. The skill generates 11 files total.
Placeholder substitutions:
| Placeholder | Value | Example |
|---|---|---|
{{ModuleName}} | PascalCase module name | DataProcessorModule |
{{modulename}} | Lowercase module name | dataprocessormodule |
{{ModuleDescription}} | User-provided description | Processes sensor data |
{{CONTAINER_REGISTRY}} | Detected or provided registry | myregistry.azurecr.io |
{{PROJECT_NAMESPACE}} | Detected or provided namespace | Company.IoT.EdgeAPI |
{{MODULE_CSPROJ_PATH}} | Calculated module csproj path: <modules_base_path>/<modulename>/<ModuleName>.csproj | src/IoTEdgeModules/modules/dataprocessormodule/DataProcessorModule.csproj |
{{MODULE_PUBLISH_PATH}} | Calculated publish path | src/IoTEdgeModules/modules/dataprocessormodule |
{{CONTRACTS_PROJECT_REFERENCE}} | Conditional contracts reference | See below |
{{CONTRACTS_CSPROJ_COPY}} | Conditional Dockerfile COPY | See below |
{{NUGET_CONFIG_SECTION}} | Conditional NuGet configuration | See below |
Conditional placeholder handling:
A. Contracts Project Reference ({{CONTRACTS_PROJECT_REFERENCE}})
Calculate relative path from module directory to contracts directory:
Example:
src/IoTEdgeModules/modules/mydemomodule/src/Company.IoT.Modules.Contracts/../../../Company.IoT.Modules.Contracts
mydemomodule/ → modules/ → IoTEdgeModules/ → src/Company.IoT.Modules.Contracts/If using shared contracts:
<ItemGroup>
<ProjectReference Include="<relative-path-to-contracts>/<contracts_project_name>.csproj" />
</ItemGroup>
If NOT using shared contracts:
<!-- No shared contracts project -->
B. Contracts Dockerfile COPY ({{CONTRACTS_CSPROJ_COPY}})
If using shared contracts:
COPY <contracts_project_path>/*.csproj ./src/
If NOT using shared contracts:
(empty - no COPY line)
C. NuGet Configuration ({{NUGET_CONFIG_SECTION}})
If using private NuGet feed:
ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS="{\"endpointCredentials\": [{\"endpoint\":\"<nuget_feed_url>\", \"username\":\"docker\", \"password\":\"${FEED_ACCESSTOKEN}\"}]}"
If NOT using private NuGet feed:
(empty - no ENV line)
Template file mappings:
| Template File | Target File | Notes |
|---|---|---|
template.csproj | <ModuleName>.csproj | Rename to match ModuleName |
template-module.json | module.json | - |
template-Program.cs | Program.cs | - |
template-Service.cs | <ModuleName>Service.cs | Rename to match ModuleName |
template-GlobalUsings.cs | GlobalUsings.cs | - |
template-ServiceLoggerMessages.cs | <ModuleName>ServiceLoggerMessages.cs | Rename to match ModuleName |
template-Dockerfile.amd64 | Dockerfile.amd64 | - |
template-Dockerfile.amd64.debug | Dockerfile.amd64.debug | - |
template-.dockerignore | .dockerignore | - |
template-.gitignore | .gitignore | - |
template-launchSettings.json | Properties/launchSettings.json | Create Properties/ first |
Processing workflow:
For each template file listed in the table above, process sequentially:
assets/Process all 11 files one at a time before proceeding to Step 6.
If using shared contracts project:
Directory: <contracts_project_path>/<ModuleName>/
File: <ModuleName>Constants.cs
Process:
template-ModuleConstants.csIf NOT using shared contracts:
Directory: <modules_base_path>/<modulename>/Contracts/
File: <ModuleName>Constants.cs
Process:
Contracts/ folder in module directorytemplate-ModuleConstants.cs{{PROJECT_NAMESPACE}}.Modules.Contracts with just {{ModuleName}}.ContractsThis extension method is required for AddModuleConsoleLogging() in Program.cs.
If using shared contracts project:
Check if <contracts_project_path>/Extensions/LoggingBuilderExtensions.cs exists:
Directory: <contracts_project_path>/Extensions/
File: LoggingBuilderExtensions.cs
Process:
Extensions/ directory if it doesn't existtemplate-LoggingBuilderExtensions.cs{{PROJECT_NAMESPACE}} placeholderIf NOT using shared contracts:
Directory: <modules_base_path>/<modulename>/Extensions/
File: LoggingBuilderExtensions.cs
Process:
Extensions/ folder in module directorytemplate-LoggingBuilderExtensions.cs{{PROJECT_NAMESPACE}}.Modules.Contracts with {{ModuleName}}Run the manifest scanning script:
python scripts/scan_manifests.py --root .
Process the output:
If 0 manifests found, ask user to create a base manifest or skip:
assets/template-base.deployment.manifest.json at <manifests_base_path>/{name}.deployment.manifest.json, then add module via the update scriptMulti-manifest selection: If multiple manifests found, present list with module counts and let user pick which to update (comma-separated numbers, 'all', or 'none').
For each selected manifest, run the update script:
python scripts/update_deployment_manifest.py \
"<manifest_path>" \
"<modulename>" \
--registry "<container_registry>"
Process the output:
"success": true in JSON outputIf the script fails, show the error and provide manual fallback instructions from references/deployment-manifests.md.
Search for "Solution project overview" or "IoTEdge modules" section in README.md:
If section exists:
- **<modulename>** (\<module_path>`) - `If section doesn't exist:
Detect solution file:
Run the solution detection script:
python scripts/manage_solution.py --root . --detect
Process detection results:
If .slnx file found:
Automatically add module to solution:
python scripts/manage_solution.py \
--root . \
--add-module "<module_csproj_path>" \
--module-name "<ModuleName>"
action: "added": Report "✓ Added to solution at position <insertion_index>"action: "already_exists": Report "Module already in solution"action: "error": Show error and continueIf .sln file found:
python scripts/manage_solution.py \
--root . \
--add-module "<module_csproj_path>" \
--module-name "<ModuleName>"
instructions field to userdotnet sln add "<module_csproj_path>"If no solution file found:
Summary of created files:
✓ Module scaffolding complete!
Created:
• Module directory: <module_full_path>/ (11 files)
• Constants file: <constants_full_path>
• LoggingBuilderExtensions: <"Created" or "Already exists - skipped">
• Updated manifests: <manifest_count> manifest(s) [or "Created base manifest" if first module]
• Solution integration: <"Added to .slnx" or "Manual instructions provided" or "Skipped">
Configuration:
• Container registry: <container_registry>
• Project namespace: <project_namespace>
• NuGet feed: <nuget_feed_url or "None">
• Shared contracts: <"Yes" or "No">
Next steps for the user:
<ModuleName>Service.cs → ExecuteAsync()dotnet run --project <module_path> (uses mock IoT Hub client)For post-scaffolding customizations (Quartz, config options, routing, direct methods, etc.), see references/customizations.md.
references/module-structure.md - Module structure, naming conventions, and notesreferences/deployment-manifests.md - Deployment manifest configurationreferences/customizations.md - Post-scaffolding customizations, error handling, and config file format