Skip to content
Back to skills

Variables

ASecurity

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.

  • 48 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aijavascripttypescriptgojavashellbashapisecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add wyre-technology/msp-claude-plugins --skill variables --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Variables?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Variables
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wyre-technology-variables/badge)](https://www.skillsdirectory.com/skills/wyre-technology-variables)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "Datto RMM Variables"
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.
when_to_use: "When working with account-level and site-level variables for storing configuration data in Datto RMM variables"
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

| Scope | Description | Use Case |
|-------|-------------|----------|
| **Account** | Available to all sites | Global configuration |
| **Site** | Available to specific site | Client-specific settings |

### Variable Inheritance

```
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

| Error | Status | Cause | Resolution |
|-------|--------|-------|------------|
| Variable exists | 400 | Duplicate name | Use PUT to update |
| Variable not found | 404 | Invalid ID | Verify variable exists |
| Invalid name | 400 | Reserved prefix or invalid chars | Use valid naming |
| Permission denied | 403 | API restrictions | Check 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

| Category | Examples | Purpose |
|----------|----------|---------|
| Backup | `BACKUP_PATH`, `BACKUP_RETENTION_DAYS` | Backup configuration |
| Logging | `LOG_PATH`, `LOG_LEVEL` | Log settings |
| Alerting | `ALERT_EMAIL`, `ALERT_THRESHOLD` | Alert configuration |
| Security | `AV_SCAN_SCHEDULE`, `FIREWALL_ENABLED` | Security settings |
| Client | `CLIENT_CODE`, `CLIENT_CONTACT` | Client 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

- [Datto RMM Sites](../sites/SKILL.md) - Site-level variable management
- [Datto RMM Jobs](../jobs/SKILL.md) - Using variables in jobs
- [Datto RMM API Patterns](../api-patterns/SKILL.md) - Authentication and pagination

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…