AgentSkillsCN

Variables

当您需要管理Datto RMM变量时,可使用此技能:管理账户级与站点级变量,用于存储配置数据。本技能涵盖变量的增删改查操作、在作业/脚本中使用变量、命名规范,以及变量管理的最佳实践。

SKILL.md
--- frontmatter
description: >
  Use this skill when working with Datto RMM variables - account-level and
  site-level variables for storing configuration data. Covers variable
  CRUD operations, using variables in jobs/scripts, naming conventions,
  and variable management patterns.
triggers:
  - datto variable
  - rmm variable
  - account variable
  - site variable
  - script variable
  - component variable
  - configuration variable

Datto RMM Variables

Overview

Variables in Datto RMM store key-value configuration data at account or site level. They're used to customize component scripts, store configuration values, and maintain environment-specific settings. This skill covers variable management, scoping, and usage patterns.

Key Concepts

Variable Scopes

ScopeDescriptionUse Case
AccountAvailable to all sitesGlobal configuration
SiteAvailable to specific siteClient-specific settings

Variable Inheritance

code
Account Variables (Global)
        │
        ▼ (inherited by)
   Site Variables
        │
        ▼ (used in)
   Jobs/Components

Site variables can override account variables with the same name.

Variable Types

All variables are stored as strings but can represent:

  • Configuration paths
  • Credentials (use secure alternatives when possible)
  • Feature flags
  • Threshold values
  • Client-specific data

Field Reference

Variable Object

typescript
interface Variable {
  id: number;                   // Variable ID
  name: string;                 // Variable name
  value: string;                // Variable value
  description?: string;         // Optional description
  scope: VariableScope;         // "account" or "site"
  siteUid?: string;             // Site UID (for site variables)
  createdAt: number;            // Creation timestamp
  modifiedAt: number;           // Last modification
}

type VariableScope = 'account' | 'site';

Naming Conventions

Recommended Format: SCREAMING_SNAKE_CASE

Examples:

  • BACKUP_PATH
  • ADMIN_EMAIL
  • LOG_RETENTION_DAYS
  • ANTIVIRUS_SCAN_SCHEDULE

Reserved Prefixes:

  • CS_ - Datto internal use
  • DATTO_ - System variables

API Patterns

List Account Variables

http
GET /api/v2/account/variables
Authorization: Bearer {token}

Response:

json
{
  "variables": [
    {
      "id": 1,
      "name": "BACKUP_PATH",
      "value": "D:\\Backups",
      "description": "Default backup destination",
      "scope": "account",
      "createdAt": 1680000000000,
      "modifiedAt": 1707991200000
    },
    {
      "id": 2,
      "name": "ADMIN_EMAIL",
      "value": "alerts@msp.com",
      "scope": "account"
    }
  ]
}

List Site Variables

http
GET /api/v2/site/{siteUid}/variables
Authorization: Bearer {token}

Response:

json
{
  "variables": [
    {
      "id": 101,
      "name": "BACKUP_PATH",
      "value": "E:\\ClientBackups",
      "description": "Client-specific backup path",
      "scope": "site",
      "siteUid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    },
    {
      "id": 102,
      "name": "CLIENT_CODE",
      "value": "ACME",
      "scope": "site",
      "siteUid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }
  ]
}

Create Account Variable

http
POST /api/v2/account/variables
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "NEW_VARIABLE",
  "value": "variable_value",
  "description": "Description of the variable"
}

Create Site Variable

http
POST /api/v2/site/{siteUid}/variables
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "SITE_SPECIFIC_VAR",
  "value": "site_value",
  "description": "Site-specific configuration"
}

Update Variable

http
PUT /api/v2/account/variable/{variableId}
Authorization: Bearer {token}
Content-Type: application/json

{
  "value": "updated_value",
  "description": "Updated description"
}

For site variables:

http
PUT /api/v2/site/{siteUid}/variable/{variableId}

Delete Variable

http
DELETE /api/v2/account/variable/{variableId}
Authorization: Bearer {token}

For site variables:

http
DELETE /api/v2/site/{siteUid}/variable/{variableId}

Workflows

Get Effective Variable Value

Account variables can be overridden by site variables:

javascript
async function getEffectiveVariable(client, siteUid, variableName) {
  // Try site variable first
  const siteVars = await client.request(`/api/v2/site/${siteUid}/variables`);
  const siteVar = siteVars.variables?.find(v => v.name === variableName);

  if (siteVar) {
    return {
      name: variableName,
      value: siteVar.value,
      scope: 'site',
      source: `Site: ${siteUid}`
    };
  }

  // Fall back to account variable
  const accountVars = await client.request('/api/v2/account/variables');
  const accountVar = accountVars.variables?.find(v => v.name === variableName);

  if (accountVar) {
    return {
      name: variableName,
      value: accountVar.value,
      scope: 'account',
      source: 'Account'
    };
  }

  return {
    name: variableName,
    value: null,
    error: 'Variable not found'
  };
}

Bulk Variable Setup

javascript
async function setupSiteVariables(client, siteUid, variables) {
  const results = [];

  for (const [name, value] of Object.entries(variables)) {
    try {
      await client.request(`/api/v2/site/${siteUid}/variables`, {
        method: 'POST',
        body: JSON.stringify({
          name,
          value,
          description: `Auto-created: ${new Date().toISOString()}`
        })
      });
      results.push({ name, success: true });
    } catch (error) {
      results.push({ name, success: false, error: error.message });
    }

    await sleep(100);
  }

  return results;
}

Variable Audit Report

javascript
async function auditVariables(client, siteUids) {
  const report = {
    account: [],
    sites: {}
  };

  // Get account variables
  const accountVars = await client.request('/api/v2/account/variables');
  report.account = accountVars.variables || [];

  // Get site variables
  for (const siteUid of siteUids) {
    try {
      const siteVars = await client.request(`/api/v2/site/${siteUid}/variables`);
      report.sites[siteUid] = siteVars.variables || [];
    } catch (error) {
      report.sites[siteUid] = { error: error.message };
    }

    await sleep(100);
  }

  return report;
}

Find Variable Usage

javascript
async function findVariableOverrides(client, variableName) {
  const accountVars = await client.request('/api/v2/account/variables');
  const accountVar = accountVars.variables?.find(v => v.name === variableName);

  const sitesResponse = await client.request('/api/v2/sites?max=250');
  const sites = sitesResponse.sites || [];

  const overrides = [];

  for (const site of sites) {
    try {
      const siteVars = await client.request(`/api/v2/site/${site.uid}/variables`);
      const siteVar = siteVars.variables?.find(v => v.name === variableName);

      if (siteVar) {
        overrides.push({
          siteName: site.name,
          siteUid: site.uid,
          value: siteVar.value,
          isOverride: accountVar ? true : false
        });
      }
    } catch (error) {
      // Skip sites with errors
    }

    await sleep(100);
  }

  return {
    variableName,
    accountValue: accountVar?.value || null,
    overrides,
    overrideCount: overrides.length
  };
}

Variable Template Application

javascript
async function applyVariableTemplate(client, siteUid, template) {
  /*
   * Template format:
   * {
   *   "BACKUP_PATH": "D:\\Backups\\{SITE_NAME}",
   *   "LOG_RETENTION_DAYS": "30",
   *   "ADMIN_EMAIL": "{inherit}"
   * }
   */

  const site = await client.request(`/api/v2/site/${siteUid}`);
  const results = [];

  for (const [name, valueTemplate] of Object.entries(template)) {
    // Skip inherited variables
    if (valueTemplate === '{inherit}') {
      results.push({ name, action: 'inherited' });
      continue;
    }

    // Replace placeholders
    const value = valueTemplate
      .replace('{SITE_NAME}', site.name)
      .replace('{SITE_UID}', siteUid);

    try {
      await client.request(`/api/v2/site/${siteUid}/variables`, {
        method: 'POST',
        body: JSON.stringify({ name, value })
      });
      results.push({ name, action: 'created', value });
    } catch (error) {
      if (error.message?.includes('already exists')) {
        // Update existing
        const existing = await findVariableByName(client, siteUid, name);
        if (existing) {
          await client.request(`/api/v2/site/${siteUid}/variable/${existing.id}`, {
            method: 'PUT',
            body: JSON.stringify({ value })
          });
          results.push({ name, action: 'updated', value });
        }
      } else {
        results.push({ name, action: 'error', error: error.message });
      }
    }

    await sleep(100);
  }

  return results;
}

async function findVariableByName(client, siteUid, name) {
  const response = await client.request(`/api/v2/site/${siteUid}/variables`);
  return response.variables?.find(v => v.name === name);
}

Error Handling

Common Variable API Errors

ErrorStatusCauseResolution
Variable exists400Duplicate nameUse PUT to update
Variable not found404Invalid IDVerify variable exists
Invalid name400Reserved prefix or invalid charsUse valid naming
Permission denied403API restrictionsCheck permissions

Safe Variable Operations

javascript
async function safeSetVariable(client, scope, siteUid, name, value) {
  // Validate name
  if (name.startsWith('CS_') || name.startsWith('DATTO_')) {
    return { success: false, error: 'Reserved prefix' };
  }

  if (!/^[A-Z][A-Z0-9_]*$/.test(name)) {
    return { success: false, error: 'Invalid name format. Use SCREAMING_SNAKE_CASE' };
  }

  const baseUrl = scope === 'site'
    ? `/api/v2/site/${siteUid}/variables`
    : '/api/v2/account/variables';

  try {
    // Try to create
    await client.request(baseUrl, {
      method: 'POST',
      body: JSON.stringify({ name, value })
    });
    return { success: true, action: 'created' };
  } catch (error) {
    if (error.status === 400) {
      // Try to update
      const existing = scope === 'site'
        ? await findVariableByName(client, siteUid, name)
        : await findAccountVariableByName(client, name);

      if (existing) {
        const updateUrl = scope === 'site'
          ? `/api/v2/site/${siteUid}/variable/${existing.id}`
          : `/api/v2/account/variable/${existing.id}`;

        await client.request(updateUrl, {
          method: 'PUT',
          body: JSON.stringify({ value })
        });
        return { success: true, action: 'updated' };
      }
    }

    return { success: false, error: error.message };
  }
}

async function findAccountVariableByName(client, name) {
  const response = await client.request('/api/v2/account/variables');
  return response.variables?.find(v => v.name === name);
}

Best Practices

  1. Use SCREAMING_SNAKE_CASE - Consistent naming for all variables
  2. Document variables - Use description field
  3. Prefer account-level - For truly global settings
  4. Override at site level - For client-specific needs
  5. Avoid sensitive data - Use secure alternatives for credentials
  6. Audit regularly - Review variable usage
  7. Template common setups - Standardize site onboarding
  8. Prefix by category - e.g., BACKUP_*, ALERT_*, LOG_*
  9. Version control templates - Track variable configurations
  10. Clean up unused - Remove obsolete variables

Common Variable Categories

CategoryExamplesPurpose
BackupBACKUP_PATH, BACKUP_RETENTION_DAYSBackup configuration
LoggingLOG_PATH, LOG_LEVELLog settings
AlertingALERT_EMAIL, ALERT_THRESHOLDAlert configuration
SecurityAV_SCAN_SCHEDULE, FIREWALL_ENABLEDSecurity settings
ClientCLIENT_CODE, CLIENT_CONTACTClient identification

Using Variables in Components

Variables are referenced in component scripts using Datto's syntax:

PowerShell:

powershell
$backupPath = $env:BACKUP_PATH
$retentionDays = $env:LOG_RETENTION_DAYS

# Use the variables
Get-ChildItem -Path $backupPath -Recurse |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$retentionDays) } |
  Remove-Item

Bash:

bash
BACKUP_PATH="${BACKUP_PATH:-/backup}"
RETENTION_DAYS="${LOG_RETENTION_DAYS:-30}"

find "$BACKUP_PATH" -type f -mtime +$RETENTION_DAYS -delete

Related Skills