Help us improve
Share bugs, ideas, or general feedback.
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-glueHow this skill is triggered — by the user, by Claude, or both
Slash command
/kaseya-it-glue:passwordsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
Manages Hudu asset passwords via /api/v1/asset_passwords: secure storage, retrieval, folders, access patterns, best practices, and audit logging.
Manages IT Glue documents: creates, organizes in folders, embeds passwords/resources/contacts/images, links related items, tracks versions, and uses API for listing, filtering, and pagination.
Implements Delinea Secret Server for PAM including vault configuration, role-based access policies, automated password rotation, session recording, and Active Directory/cloud integration.
Share bugs, ideas, or general feedback.
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;
}
}