From kaseya-it-glue
Manages IT Glue passwords: secure credential storage, categories, folders, embedded credentials, access patterns, audit logging, retrieval, and security best practices in MSP documentation.
npx claudepluginhub wyre-technology/msp-claude-plugins --plugin it-glueThis skill uses the workspace's default tool permissions.
Passwords in IT Glue provide secure credential storage with organization-level access control. This skill covers password creation, categorization, folder organization, and security best practices for managing sensitive credentials within MSP documentation.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Retrieves current documentation, API references, and code examples for libraries, frameworks, SDKs, CLIs, and services via Context7 CLI. Ideal for API syntax, configs, migrations, and setup queries.
Uses ctx7 CLI to fetch current library docs, manage AI coding skills (install/search/generate), and configure Context7 MCP for AI editors.
Passwords in IT Glue provide secure credential storage with organization-level access control. This skill covers password creation, categorization, folder organization, and security best practices for managing sensitive credentials within MSP documentation.
Passwords are organized by category for classification:
| Category | Description | Examples |
|---|---|---|
| Administrative | Admin/root credentials | Domain Admin, Local Admin |
| Application | Software credentials | Database logins, API keys |
| Network | Network device access | Firewall, switch, router |
| Service Account | Automated process accounts | Backup, monitoring |
| User | End-user credentials | Email, VPN |
| Vendor | Third-party access | Vendor portals, support |
| Cloud | Cloud service credentials | AWS, Azure, Microsoft 365 |
Folders provide hierarchical organization within an organization:
Organization: Acme Corporation
└── Passwords
├── Infrastructure
│ ├── Domain Controllers
│ ├── File Servers
│ └── Network Devices
├── Applications
│ ├── ERP System
│ └── CRM
└── Cloud Services
├── Microsoft 365
└── AWS
Passwords can be embedded directly within documents and flexible assets, providing contextual credential access alongside documentation.
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | System | Auto-generated unique identifier |
organization-id | integer | Yes | Parent organization |
name | string | Yes | Password display name |
username | string | No | Account username |
password | string | No | The actual password (encrypted) |
url | string | No | Related URL/login page |
password-category-id | integer | No | Category classification |
password-folder-id | integer | No | Folder location |
| Field | Type | Description |
|---|---|---|
notes | string | Additional notes (HTML) |
otp-secret | string | TOTP/2FA secret |
| Field | Type | Description |
|---|---|---|
resource-id | integer | Related resource ID |
resource-type | string | Related resource type |
| Field | Type | Description |
|---|---|---|
restricted | boolean | Restricted access flag |
autofill-selectors | string | Browser autofill selectors |
| Field | Type | Description |
|---|---|---|
created-at | datetime | Creation timestamp |
updated-at | datetime | Last update timestamp |
password-updated-at | datetime | Password last changed |
GET /passwords
x-api-key: YOUR_API_KEY
Content-Type: application/vnd.api+json
By Organization:
GET /organizations/123/relationships/passwords
With Filters:
GET /passwords?filter[organization-id]=123&filter[password-category-id]=456
GET /passwords/789
x-api-key: YOUR_API_KEY
With Includes:
GET /passwords/789?include=organization,password-category,password-folder
Note: The password field is returned only when explicitly retrieving a single password.
To retrieve the actual password value, you must request with the show_password parameter:
GET /passwords/789?show_password=true
x-api-key: YOUR_API_KEY
Security Note: This action is logged in the IT Glue audit trail.
POST /passwords
Content-Type: application/vnd.api+json
x-api-key: YOUR_API_KEY
{
"data": {
"type": "passwords",
"attributes": {
"organization-id": 123456,
"name": "Domain Admin - ACME",
"username": "administrator@acme.local",
"password": "SecureP@ssw0rd!",
"url": "https://dc01.acme.local",
"password-category-id": 12,
"notes": "<p>Primary domain administrator account</p>"
}
}
}
PATCH /passwords/789
Content-Type: application/vnd.api+json
x-api-key: YOUR_API_KEY
{
"data": {
"type": "passwords",
"attributes": {
"password": "NewSecureP@ssw0rd!",
"notes": "<p>Password updated on 2024-02-15</p>"
}
}
}
DELETE /passwords/789
x-api-key: YOUR_API_KEY
Warning: Consider archiving instead of deleting for audit purposes.
By Name:
GET /passwords?filter[name]=Domain Admin
By Category:
GET /passwords?filter[password-category-id]=12
By Folder:
GET /passwords?filter[password-folder-id]=34
GET /password-folders
x-api-key: YOUR_API_KEY
By Organization:
GET /organizations/123/relationships/password-folders
POST /password-folders
Content-Type: application/vnd.api+json
{
"data": {
"type": "password-folders",
"attributes": {
"organization-id": 123456,
"name": "Infrastructure",
"parent-id": null
}
}
}
{
"data": {
"type": "password-folders",
"attributes": {
"organization-id": 123456,
"name": "Domain Controllers",
"parent-id": 789
}
}
}
Passwords can be embedded in documents using IT Glue's embedded password syntax:
<p>Login credentials:</p>
<div data-embedded-password-id="12345"></div>
Flexible asset types can include password fields that reference or store passwords directly.
async function createSecurePassword(orgId, data) {
// Create or get appropriate folder
const folder = await ensureFolder(orgId, data.folderPath);
// Create password with category
const password = await createPassword({
'organization-id': orgId,
name: data.name,
username: data.username,
password: data.password,
url: data.url,
'password-category-id': data.categoryId,
'password-folder-id': folder?.id,
notes: `<p>Created: ${new Date().toLocaleDateString()}</p>
<p>Purpose: ${data.purpose}</p>`
});
// Log the creation (your audit system)
await logPasswordAction({
action: 'created',
passwordId: password.id,
passwordName: data.name,
organizationId: orgId,
timestamp: new Date()
});
return password;
}
async function rotatePassword(passwordId, newPassword, reason) {
// Get current password info
const current = await getPassword(passwordId);
// Update with new password
const updated = await updatePassword(passwordId, {
password: newPassword,
notes: `${current.attributes.notes || ''}
<p><strong>Rotated:</strong> ${new Date().toLocaleDateString()}</p>
<p><strong>Reason:</strong> ${reason}</p>`
});
// Log rotation
await logPasswordAction({
action: 'rotated',
passwordId: passwordId,
passwordName: current.attributes.name,
reason: reason,
timestamp: new Date()
});
return updated;
}
async function findPasswordForServer(orgId, serverName) {
// Search for passwords mentioning this server
const passwords = await fetchPasswords({
filter: { 'organization-id': orgId }
});
// Filter by server name in name or notes
return passwords.filter(p =>
p.attributes.name.toLowerCase().includes(serverName.toLowerCase()) ||
p.attributes.notes?.toLowerCase().includes(serverName.toLowerCase())
);
}
async function generatePasswordReport(orgId) {
const passwords = await fetchPasswords({
filter: { 'organization-id': orgId },
include: 'password-category,password-folder'
});
const byCategory = {};
passwords.forEach(p => {
const category = p.relationships['password-category']?.data?.id || 'Uncategorized';
if (!byCategory[category]) byCategory[category] = [];
byCategory[category].push({
name: p.attributes.name,
username: p.attributes.username,
url: p.attributes.url,
lastUpdated: p.attributes['password-updated-at']
});
});
return byCategory;
}
async function findStalePasswords(orgId, daysOld = 90) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
const passwords = await fetchPasswords({
filter: { 'organization-id': orgId }
});
return passwords
.filter(p => {
const updated = p.attributes['password-updated-at'];
return !updated || new Date(updated) < cutoffDate;
})
.map(p => ({
id: p.id,
name: p.attributes.name,
lastUpdated: p.attributes['password-updated-at'] || 'Never',
daysSinceUpdate: p.attributes['password-updated-at']
? Math.floor((new Date() - new Date(p.attributes['password-updated-at'])) / (1000 * 60 * 60 * 24))
: 'Unknown'
}));
}
IT Glue logs all password access. Additionally:
// Your own audit logging
async function logPasswordAccess(passwordId, action, user) {
await auditLog.create({
resource: 'password',
resourceId: passwordId,
action: action, // 'viewed', 'copied', 'updated', 'created', 'deleted'
user: user,
timestamp: new Date(),
ipAddress: getCurrentIp()
});
}
| Code | Message | Resolution |
|---|---|---|
| 400 | Name can't be blank | Provide password name |
| 400 | Organization required | Include organization-id |
| 401 | Invalid API key | Check IT_GLUE_API_KEY |
| 403 | Access denied | User lacks permission |
| 404 | Password not found | Verify password ID |
| 422 | Invalid category | Query valid category IDs |
| Error | Cause | Fix |
|---|---|---|
| Name required | Missing name | Add name to request |
| Organization required | No org ID | Include organization-id |
| Invalid category | Bad category ID | Query /password-categories |
| Invalid folder | Bad folder ID | Query /password-folders |
async function safeGetPassword(passwordId) {
try {
return await getPassword(passwordId, { show_password: true });
} catch (error) {
if (error.status === 403) {
// Don't leak that the password exists
console.log('Password access denied or not found');
return null;
}
if (error.status === 404) {
console.log('Password not found');
return null;
}
// Log security event for unexpected errors
await logSecurityEvent({
event: 'password_access_error',
passwordId: passwordId,
error: error.status,
timestamp: new Date()
});
throw error;
}
}