All skills
josiahsiegel avatar

/adf-ml-analytics

@9ae4e4a

ADF + Azure ML and analytics integrations. PROACTIVELY activate for: (1) Azure ML batch endpoints invoked from ADF, (2) Azure OpenAI Batch API pipeline patterns, (3) ADF ML scoring orchestration, (4) SQL to Storage archival pipelines, (5) AI Services integration via REST connector, (6) Databricks notebook execution from ADF, (7) Data Flow feature engineering, (8) Synapse / Fabric integration from ADF, (9) Cognitive Search indexer triggers, (10) Power BI dataset refresh via REST. Provides: REST connector recipes, Databricks linked service setup, Data Flow templates, and end-to-end ML scoring pipelines.

Use this Skill: https://skilld.dev/gh/josiahsiegel/claude-plugin-marketplace/adf-ml-analytics

This session only. Nothing lands on disk.

referencessql-archival-patterns.md

≈3.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Azure SQL Scoring and Storage Archival Patterns

Azure SQL In-Database ML Scoring

T-SQL PREDICT Function

For fast, in-database scoring without leaving Azure SQL. Supports ONNX models loaded into the database.

Stored Procedure Activity calling PREDICT:

{
  "name": "InDatabaseMLScoring",
  "type": "SqlServerStoredProcedure",
  "linkedServiceName": {
    "referenceName": "LS_AzureSql",
    "type": "LinkedServiceReference"
  },
  "typeProperties": {
    "storedProcedureName": "dbo.usp_ScoreWithPredict",
    "storedProcedureParameters": {
      "ModelName": { "value": "@pipeline().parameters.ModelName", "type": "String" },
      "InputTable": { "value": "@pipeline().parameters.InputTable", "type": "String" },
      "OutputTable": { "value": "@pipeline().parameters.OutputTable", "type": "String" }
    }
  }
}

SQL Stored Procedure using PREDICT:

CREATE PROCEDURE dbo.usp_ScoreWithPredict
    @ModelName NVARCHAR(100),
    @InputTable NVARCHAR(128),
    @OutputTable NVARCHAR(128)
AS
BEGIN
    DECLARE @model VARBINARY(MAX) = (
        SELECT model_data FROM dbo.MLModels WHERE model_name = @ModelName
    );

    -- T-SQL PREDICT scores data in-place without external calls
    DECLARE @sql NVARCHAR(MAX) = N'
        INSERT INTO ' + QUOTENAME(@OutputTable) + '
        SELECT d.*, p.Score
        FROM PREDICT(MODEL = @mdl, DATA = ' + QUOTENAME(@InputTable) + ' AS d)
        WITH (Score FLOAT) AS p';

    EXEC sp_executesql @sql, N'@mdl VARBINARY(MAX)', @mdl = @model;
END

Benefits of T-SQL PREDICT:

  • No data movement required (scoring happens in-database)
  • Low latency (milliseconds per batch)
  • Supports ONNX models (exported from scikit-learn, PyTorch, TensorFlow)

Availability -- PREDICT is NOT available in Azure SQL Database:

Platform PREDICT Support
SQL Server 2017+ RevoScaleR/revoscalepy models
Azure SQL Managed Instance RevoScaleR/revoscalepy models
Azure Synapse Analytics ONNX models
Azure SQL Database Not supported
Azure SQL Edge Retired September 2025

For Azure SQL Database users: Use ADF to extract data, then score externally via Databricks, Azure ML batch endpoints, or Azure OpenAI Batch API, and write predictions back.

sp_execute_external_script (SQL Managed Instance)

For running Python/R scripts directly inside SQL. Available on Azure SQL Managed Instance with Machine Learning Services enabled.

Stored Procedure Activity:

{
  "name": "RunPythonInSql",
  "type": "SqlServerStoredProcedure",
  "linkedServiceName": {
    "referenceName": "LS_SqlManagedInstance",
    "type": "LinkedServiceReference"
  },
  "typeProperties": {
    "storedProcedureName": "dbo.usp_PythonMLScoring",
    "storedProcedureParameters": {
      "BatchDate": { "value": "@formatDateTime(utcnow(), 'yyyy-MM-dd')", "type": "String" }
    }
  }
}

SQL Procedure with External Script:

CREATE PROCEDURE dbo.usp_PythonMLScoring @BatchDate NVARCHAR(10)
AS
BEGIN
    EXEC sp_execute_external_script
        @language = N'Python',
        @script = N'
import pandas as pd
import pickle

# InputDataSet is auto-populated from @input_data_1
model = pickle.loads(model_bytes)
predictions = model.predict(InputDataSet[["feature1", "feature2", "feature3"]])
OutputDataSet = InputDataSet.copy()
OutputDataSet["prediction"] = predictions
OutputDataSet["score_date"] = batch_date
',
        @input_data_1 = N'SELECT * FROM dbo.ScoringData WHERE batch_date = @dt',
        @params = N'@dt NVARCHAR(10), @model_bytes VARBINARY(MAX)',
        @dt = @BatchDate,
        @model_bytes = (SELECT model_data FROM dbo.MLModels WHERE is_active = 1);
END

When to Use In-Database ML vs External ML:

Scenario Use In-Database (PREDICT/sp_execute) Use External (Databricks/Azure ML)
Data volume Small-medium (<1M rows) Large (>1M rows)
Latency Real-time scoring needed Batch is acceptable
Model complexity Simple models (regression, trees) Deep learning, ensemble
Data movement Must avoid (compliance, perf) Acceptable
Compute SQL is sufficient GPU/distributed compute needed

Azure SQL Database to Storage Account (Archival & Analysis)

Pattern: Ephemeral SQL to Long-Term Storage

For Azure SQL databases with ephemeral data, archive to Azure Storage before the data is lost, then analyze from storage.

Complete Archive Pipeline:

{
  "name": "PL_SqlArchive_ForAnalysis",
  "properties": {
    "activities": [
      {
        "name": "GetTablesToArchive",
        "type": "Lookup",
        "typeProperties": {
          "source": {
            "type": "AzureSqlSource",
            "sqlReaderQuery": "SELECT TABLE_SCHEMA, TABLE_NAME, (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = t.TABLE_NAME AND c.TABLE_SCHEMA = t.TABLE_SCHEMA) as ColumnCount FROM INFORMATION_SCHEMA.TABLES t WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('sys') ORDER BY TABLE_SCHEMA, TABLE_NAME"
          },
          "dataset": {
            "referenceName": "DS_AzureSql_Source",
            "type": "DatasetReference"
          },
          "firstRowOnly": false
        }
      },
      {
        "name": "ForEach_ArchiveTable",
        "type": "ForEach",
        "dependsOn": [
          { "activity": "GetTablesToArchive", "dependencyConditions": ["Succeeded"] }
        ],
        "typeProperties": {
          "items": {
            "value": "@activity('GetTablesToArchive').output.value",
            "type": "Expression"
          },
          "isSequential": false,
          "batchCount": 10,
          "activities": [
            {
              "name": "ArchiveTableToParquet",
              "type": "Copy",
              "typeProperties": {
                "source": {
                  "type": "AzureSqlSource",
                  "sqlReaderQuery": {
                    "value": "@concat('SELECT * FROM [', item().TABLE_SCHEMA, '].[', item().TABLE_NAME, ']')",
                    "type": "Expression"
                  }
                },
                "sink": {
                  "type": "ParquetSink",
                  "storeSettings": {
                    "type": "AzureBlobStorageWriteSettings"
                  },
                  "formatSettings": {
                    "type": "ParquetWriteSettings"
                  }
                },
                "enableStaging": false,
                "parallelCopies": 4,
                "dataIntegrationUnits": 4
              },
              "inputs": [
                {
                  "referenceName": "DS_AzureSql_Parameterized",
                  "type": "DatasetReference",
                  "parameters": {
                    "SchemaName": "@item().TABLE_SCHEMA",
                    "TableName": "@item().TABLE_NAME"
                  }
                }
              ],
              "outputs": [
                {
                  "referenceName": "DS_Blob_Parquet_Partitioned",
                  "type": "DatasetReference",
                  "parameters": {
                    "FolderPath": {
                      "value": "@concat('archive/', pipeline().parameters.SourceDatabase, '/', item().TABLE_SCHEMA, '/', item().TABLE_NAME, '/snapshot_date=', formatDateTime(utcnow(), 'yyyy-MM-dd'))",
                      "type": "Expression"
                    },
                    "FileName": {
                      "value": "@concat(item().TABLE_NAME, '_', formatDateTime(utcnow(), 'yyyyMMddHHmmss'), '.parquet')",
                      "type": "Expression"
                    }
                  }
                }
              ]
            }
          ]
        }
      },
      {
        "name": "LogArchiveCompletion",
        "type": "WebActivity",
        "dependsOn": [
          { "activity": "ForEach_ArchiveTable", "dependencyConditions": ["Succeeded"] }
        ],
        "typeProperties": {
          "url": "@pipeline().parameters.LoggingEndpoint",
          "method": "POST",
          "body": {
            "status": "ARCHIVED",
            "database": "@pipeline().parameters.SourceDatabase",
            "tablesArchived": "@activity('GetTablesToArchive').output.count",
            "archivePath": "@concat('archive/', pipeline().parameters.SourceDatabase)",
            "timestamp": "@utcnow()"
          }
        }
      }
    ],
    "parameters": {
      "SourceDatabase": { "type": "string" },
      "LoggingEndpoint": { "type": "string" }
    }
  }
}

Pattern: Incremental Archive with Watermark

For databases where data changes over time, use watermark-based incremental extraction.

{
  "name": "PL_IncrementalArchive",
  "properties": {
    "activities": [
      {
        "name": "GetLastWatermark",
        "type": "Lookup",
        "typeProperties": {
          "source": {
            "type": "AzureSqlSource",
            "sqlReaderQuery": "SELECT MAX(ArchiveTimestamp) as LastArchive FROM dbo.ArchiveWatermark WHERE TableName = '@{pipeline().parameters.TableName}'"
          },
          "dataset": { "referenceName": "DS_AzureSql_Control", "type": "DatasetReference" },
          "firstRowOnly": true
        }
      },
      {
        "name": "GetCurrentWatermark",
        "type": "Lookup",
        "dependsOn": [
          { "activity": "GetLastWatermark", "dependencyConditions": ["Succeeded"] }
        ],
        "typeProperties": {
          "source": {
            "type": "AzureSqlSource",
            "sqlReaderQuery": {
              "value": "@concat('SELECT MAX(ModifiedDate) as CurrentWatermark FROM ', pipeline().parameters.TableName)",
              "type": "Expression"
            }
          },
          "dataset": { "referenceName": "DS_AzureSql_Source", "type": "DatasetReference" },
          "firstRowOnly": true
        }
      },
      {
        "name": "CopyIncrementalToStorage",
        "type": "Copy",
        "dependsOn": [
          { "activity": "GetCurrentWatermark", "dependencyConditions": ["Succeeded"] }
        ],
        "typeProperties": {
          "source": {
            "type": "AzureSqlSource",
            "sqlReaderQuery": {
              "value": "@concat('SELECT * FROM ', pipeline().parameters.TableName, ' WHERE ModifiedDate > ''', activity('GetLastWatermark').output.firstRow.LastArchive, ''' AND ModifiedDate <= ''', activity('GetCurrentWatermark').output.firstRow.CurrentWatermark, '''')",
              "type": "Expression"
            }
          },
          "sink": {
            "type": "ParquetSink",
            "storeSettings": { "type": "AzureBlobStorageWriteSettings" }
          }
        },
        "inputs": [{ "referenceName": "DS_AzureSql_Source", "type": "DatasetReference" }],
        "outputs": [{
          "referenceName": "DS_Blob_Parquet_Partitioned",
          "type": "DatasetReference",
          "parameters": {
            "FolderPath": "@concat('incremental/', pipeline().parameters.TableName, '/', formatDateTime(utcnow(), 'yyyy/MM/dd'))",
            "FileName": "@concat(pipeline().parameters.TableName, '_incremental_', formatDateTime(utcnow(), 'yyyyMMddHHmmss'), '.parquet')"
          }
        }]
      },
      {
        "name": "UpdateWatermark",
        "type": "SqlServerStoredProcedure",
        "dependsOn": [
          { "activity": "CopyIncrementalToStorage", "dependencyConditions": ["Succeeded"] }
        ],
        "linkedServiceName": { "referenceName": "LS_AzureSql_Control", "type": "LinkedServiceReference" },
        "typeProperties": {
          "storedProcedureName": "dbo.usp_UpdateWatermark",
          "storedProcedureParameters": {
            "TableName": { "value": "@pipeline().parameters.TableName", "type": "String" },
            "NewWatermark": { "value": "@activity('GetCurrentWatermark').output.firstRow.CurrentWatermark", "type": "DateTime" }
          }
        }
      }
    ],
    "parameters": {
      "TableName": { "type": "string" }
    }
  }
}

ADLS Gen2 Alternative for ML Data Lake

For larger-scale ML workloads, use Azure Data Lake Storage Gen2 instead of Blob Storage. ADLS Gen2 provides hierarchical namespace, better performance for analytics, and native integration with Databricks and Synapse.

ADLS Gen2 Linked Service:

{
  "name": "LS_ADLS_ML",
  "properties": {
    "type": "AzureBlobFS",
    "typeProperties": {
      "url": "https://mldatalake.dfs.core.windows.net"
    }
  }
}

ADLS Gen2 Parquet Dataset:

{
  "name": "DS_ADLS_Parquet_ML",
  "properties": {
    "type": "Parquet",
    "linkedServiceName": {
      "referenceName": "LS_ADLS_ML",
      "type": "LinkedServiceReference"
    },
    "typeProperties": {
      "location": {
        "type": "AzureBlobFSLocation",
        "fileSystem": "ml",
        "folderPath": {
          "value": "@dataset().FolderPath",
          "type": "Expression"
        }
      },
      "compressionCodec": "snappy"
    },
    "parameters": {
      "FolderPath": { "type": "String" }
    }
  }
}

ADLS Gen2 path format for Databricks:

abfss://ml@mldatalake.dfs.core.windows.net/training-data/model-name/version=1/

When to use Blob Storage vs ADLS Gen2:

Feature Blob Storage ADLS Gen2
Cost Lower for simple storage Slightly higher
Hierarchical namespace No (flat) Yes
Databricks integration Wasbs:// Abfss:// (preferred)
Analytics performance Good Better (optimized for Spark)
ACL-level permissions Container level File/folder level

Storage Account Organization for ML Analysis

Recommended folder structure for archived data and ML artifacts:

storage-account/
  archive/                          # Archived SQL data
    <database-name>/
      <schema>/<table>/
        snapshot_date=YYYY-MM-DD/
          table_YYYYMMDDHHMMSS.parquet
  incremental/                      # Incremental extracts
    <table>/
      YYYY/MM/DD/
        table_incremental_*.parquet
  ml/                               # ML artifacts
    training-data/                  # Curated training datasets
      <model-name>/
        version=<N>/
          train.parquet
          test.parquet
          validation.parquet
    models/                         # Serialized models
      <model-name>/
        version=<N>/
          model.pkl
          metadata.json
    scores/                         # Scoring results
      <model-name>/
        YYYY/MM/DD/
          predictions.parquet
    feature-store/                  # Engineered features
      <feature-set>/
        features.parquet

Source: SKILL.md on GitHub

2 warnings11d4 checks · Risk SAFE
  • Gen Agent Trust Hub11d

    The skill is a technical reference for integrating Azure Data Factory with Machine Learning services. It contains templates for data orchestration, batch scoring, and archival. Security analysis identified that the provided templates create surfaces for indirect prompt injection when processing untrusted data and utilize dynamic code execution patterns within SQL environments, including potentially unsafe deserialization with the Python pickle library.

  • Socket11d

    1 alert: gptSecurity

  • Snyk11d

    Risk: LOW · No issues

  • Runlayer6mo

    1/1 file flagged

Signed by skilld at 9ae4e4a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 4 months ago

README badge

README badge for josiahsiegel/claude-plugin-marketplace/adf-ml-analytics