As I sit here at my gaming computer, half-listening to my guildies planning tonight’s raid in Discord (yes, I’m still a gamer at heart – WoW, Valheim, Final Fantasy, and whatever random game my friends convince me to try), I’m simultaneously orchestrating what would have been impossible just a year ago. My work computer has three VS Code windows open with Claude Code, four PowerShell 7 windows also running Claude Code, and two more PS7 terminals running various automation scripts. Oh, and my gaming rig? It’s pulling double duty with two more VS Code windows because apparently, I’ve turned Claude Code into my overworked digital bestie (one that I literally pay to be my bestie.)
Let me paint you the full picture: In one window, Claude is pulling client profiles to prep for upcoming assessments. Another is extracting ConnectWise ticket data for a different client. There’s one window I particularly love – it’s the one that submits my tickets for me while I’m doing the actual work (and sometimes, let’s be honest, while AI is doing the work too). This isn’t science fiction; this is Tuesday.
The Reality of AI-Powered MSP Operations
What I’m describing isn’t just multitasking on steroids – it’s a fundamental shift in how we approach technical work. As an MSP, I used to spend hours on repetitive tasks that required just enough human judgment to resist traditional automation. Now? Claude Code and I tag-team through complex workflows that adapt on the fly.
The beauty of this setup is that it scales with complexity. Simple ticket categorization? Automated. Complex infrastructure analysis requiring context from multiple systems? Claude Code handles the heavy lifting while I provide the strategic oversight. It’s like having a team of junior engineers who never need coffee breaks and actually enjoy reading documentation.
Setting Up Your AI-Powered Command Center with Microsoft 365
Let’s get into the meat of how to build this setup using the Microsoft ecosystem (because who needs Google when you have the full Microsoft stack?).
Prerequisites: The Foundation You Actually Need
Before we dive into the fun stuff, let’s get real about what you need installed. I learned this the hard way after spending hours troubleshooting why things weren’t working (pssst… copy the entire square from the grey boxes into your powershell 7 window, thank me later):
#Essential Installations (in this order - trust me)
# 1. Git - Non-negotiable, everyone needs this on their radar
winget install --id Git.Git -e --source winget
# 2. Python (latest stable version) - Claude Code needs this
winget install -e --id Python.Python.3.12
# 3. Visual Studio Code - Your command center
winget install -e --id Microsoft.VisualStudioCode
# 4. PowerShell 7 (NOT Windows PowerShell)
winget install --id Microsoft.PowerShell --source winget
# 5. Microsoft .NET SDK (for various integrations)
winget install Microsoft.DotNet.SDK.8
# 6. Node.js (for n8n and various automation tools)
winget install OpenJS.NodeJS
# 7. Azure CLI (for Azure integrations)
winget install -e --id Microsoft.AzureCLI
# 8. Microsoft Graph PowerShell SDK
Install-Module Microsoft.Graph -Scope CurrentUser
# 9. Claude Code (from VS Code marketplace or command line)
code --install-extension claude.claude-code
# 10. Windows Terminal (makes everything prettier)
winget install --id Microsoft.WindowsTerminal -e
💡 Important: Claude Subscription vs API
I use my Claude subscription to log in, NOT the API directly! Claude Code in VS Code uses your Claude.ai subscription – you log in through the extension with your Claude account. The API examples below are for when you need programmatic access outside of Claude Code. Most of the heavy lifting happens through the Claude Code extension where you’re chatting with Claude like a bestie, not making API calls.
Step 1: Establishing Your PowerShell 7 + Claude Code Foundation
First, make sure you have PowerShell 7 (not Windows PowerShell – there’s a difference). Here’s how to set up your environment:
# After installing everything above, configure your environment
# Set up your Git config (yes, you need this even for PowerShell work)
git config --global user.name "Your Name"
git config --global user.email "your.email@company.com"
# Install additional PowerShell modules you'll need
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser
Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser
Install-Module -Name MicrosoftTeams -Scope CurrentUser
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Scope CurrentUser
Install-Module -Name Microsoft.PowerApps.PowerShell -AllowClobber -Scope CurrentUser
# For when you DO need programmatic Claude access (optional)
# This is for automation scripts, not your daily Claude Code usage
$env:CLAUDE_API_KEY = "your-api-key-here" # Only if you need API access
Now, here’s where it gets interesting. With Claude Code logged in through your subscription, you can create PowerShell functions that you manually trigger while Claude helps you write and debug them:
# This function is for when you need programmatic access
# But honestly, 90% of the time I'm just using Claude Code directly
function Invoke-ClaudeAnalysis {
param(
[string]$Context,
[string]$Request
)
# This is only for automated scripts - not your daily workflow
# Your daily workflow is: Open VS Code → Claude Code → "Hey Claude, help me..."
$headers = @{
"Content-Type" = "application/json"
"x-api-key" = $env:CLAUDE_API_KEY
"anthropic-version" = "2023-06-01"
}
$body = @{
model = "claude-3-opus-20240229"
max_tokens = 4000
messages = @(
@{
role = "user"
content = "Context: $Context`n`nRequest: $Request"
}
)
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://api.anthropic.com/v1/messages" `
-Method Post -Headers $headers -Body $body
return $response.content[0].text
}
Step 2: Integrating with ConnectWise Manage API
Here’s how I pull ConnectWise ticket data with AI-assisted analysis:
⚠️ Critical: Pagination and Callbacks for ConnectWise
If you’re integrating ConnectWise with other systems, you MUST handle pagination and callbacks properly! ConnectWise limits responses to 1000 records max, but defaults to 25. If you’re syncing with other platforms like Autotask, ServiceNow, or custom databases, missing pagination means missing tickets. Also, implement callback commands for real-time updates instead of polling – your API rate limits will thank you. Also, get a Connectwise Developer account, alot of their API information is there, and they have a All.Json file you can download and train Claude with. Oh and if you use Connectwise RMM, get that API access as well, but I just got the access today, so I’ll update you later when I get that all figured out.
# ConnectWise API Configuration
$cwCompany = "your-company"
$cwPublicKey = "your-public-key"
$cwPrivateKey = "your-private-key"
$cwClientId = "your-client-id"
# Base64 encode the authentication
$authString = "${cwCompany}+${cwPublicKey}:${cwPrivateKey}"
$encodedAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($authString))
# Function to get ALL tickets with proper pagination
function Get-CWTicketsWithAI {
param(
[string]$Status = "Open",
[int]$PageSize = 100, # Max 1000
[switch]$AllPages
)
$headers = @{
"Authorization" = "Basic $encodedAuth"
"clientId" = $cwClientId
"Content-Type" = "application/json"
}
$allTickets = @()
$page = 1
do {
$uri = "https://api-na.myconnectwise.net/v2025_1/apis/3.0/service/tickets"
$uri += "?conditions=status/name='$Status'&pageSize=$PageSize&page=$page"
try {
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
$allTickets += $response
# Check if there are more pages
$hasMore = $response.Count -eq $PageSize
$page++
# Respect rate limits
Start-Sleep -Milliseconds 200
}
catch {
Write-Host "Error on page $page: $_"
$hasMore = $false
}
} while ($AllPages -and $hasMore)
# Now let Claude analyze patterns
$ticketSummary = $allTickets | ConvertTo-Json -Depth 5
$analysis = Invoke-ClaudeAnalysis -Context $ticketSummary `
-Request "Analyze these tickets for patterns, priority issues, and suggested automation opportunities"
return @{
Tickets = $allTickets
TotalCount = $allTickets.Count
AIAnalysis = $analysis
}
}
# Callback setup for real-time updates
function Register-CWCallback {
param(
[string]$CallbackUrl,
[string]$ObjectType = "Ticket"
)
$headers = @{
"Authorization" = "Basic $encodedAuth"
"clientId" = $cwClientId
"Content-Type" = "application/json"
}
$body = @{
url = $CallbackUrl
objectId = "*"
type = $ObjectType
level = "All"
description = "AI Integration Callback"
} | ConvertTo-Json
$uri = "https://api-na.myconnectwise.net/v2025_1/apis/3.0/system/callbacks"
Invoke-RestMethod -Uri $uri -Headers $headers -Method Post -Body $body
}
Step 3: IT Glue Integration for Documentation Automation
IT Glue integration is where the magic really happens. Here’s my approach:
⚠️ Critical: IT Glue Pagination Is Non-Negotiable
IT Glue returns a maximum of 50 records per page by default! If you’re syncing configurations, passwords, or flexible assets with other systems, you MUST implement pagination. I learned this the hard way when a client audit showed we were only documenting 50 out of 300+ configurations. Also, IT Glue’s webhook support is limited, so implement smart caching with timestamp-based polling.
# IT Glue API Setup
$itGlueApiKey = "your-itglue-api-key"
$itGlueBaseUri = "https://api.itglue.com"
# Function to get ALL flexible assets with pagination
function Get-ITGlueAllAssets {
param(
[string]$AssetTypeId,
[string]$OrganizationId
)
$headers = @{
"x-api-key" = $itGlueApiKey
"Content-Type" = "application/vnd.api+json"
}
$allAssets = @()
$page = 1
$pageSize = 50 # IT Glue max
do {
$uri = "$itGlueBaseUri/flexible_assets?filter[organization_id]=$OrganizationId"
$uri += "&filter[flexible_asset_type_id]=$AssetTypeId"
$uri += "&page[size]=$pageSize&page[number]=$page"
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
if ($response.data) {
$allAssets += $response.data
}
# Check meta for pagination info
$totalPages = [math]::Ceiling($response.meta.'total-count' / $pageSize)
$page++
# Respect rate limits (IT Glue: 10,000 per hour)
Start-Sleep -Milliseconds 500
} while ($page -le $totalPages)
return $allAssets
}
function Update-ITGlueWithAI {
param(
[string]$OrganizationId,
[string]$DocumentationType,
[object]$Data,
[switch]$SyncWithCallback
)
$headers = @{
"x-api-key" = $itGlueApiKey
"Content-Type" = "application/vnd.api+json"
}
# Have Claude format the documentation
$formattedDoc = Invoke-ClaudeAnalysis `
-Context "IT Glue flexible asset for $DocumentationType" `
-Request "Format this data for IT Glue documentation: $($Data | ConvertTo-Json)"
# Create the flexible asset
$body = @{
data = @{
type = "flexible-assets"
attributes = @{
"organization-id" = $OrganizationId
"flexible-asset-type-id" = $DocumentationType
traits = $formattedDoc | ConvertFrom-Json
}
}
} | ConvertTo-Json -Depth 5
$result = Invoke-RestMethod -Uri "$itGlueBaseUri/flexible_assets" `
-Headers $headers -Method Post -Body $body
# If syncing with other systems, trigger callback
if ($SyncWithCallback) {
# Since IT Glue doesn't have native webhooks, implement your own
$callbackData = @{
action = "created"
assetId = $result.data.id
organizationId = $OrganizationId
timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"
}
# Send to your callback endpoint (n8n, Zapier, custom webhook)
Invoke-RestMethod -Uri "https://your-webhook-endpoint.com/itglue-sync" `
-Method Post -Body ($callbackData | ConvertTo-Json) `
-ContentType "application/json"
}
return $result
}
# Smart polling function for IT Glue changes
function Sync-ITGlueChanges {
param(
[datetime]$LastSyncTime = (Get-Date).AddHours(-1)
)
$headers = @{
"x-api-key" = $itGlueApiKey
"Content-Type" = "application/vnd.api+json"
}
# IT Glue doesn't have a native "updated since" filter, so we need to be creative
# Get all assets and filter by updated_at timestamp
$allAssets = Get-ITGlueAllAssets -AssetTypeId "*" -OrganizationId "*"
$changedAssets = $allAssets | Where-Object {
[datetime]$_.attributes.'updated-at' -gt $LastSyncTime
}
Write-Host "Found $($changedAssets.Count) changed assets since $LastSyncTime"
return $changedAssets
}
Automation with n8n: The Microsoft-Friendly Alternative
While Zapier is great, n8n offers more control and can be self-hosted in your Azure environment. Here’s how I set up my ticket automation workflow:
n8n Workflow: Auto-Ticket Creation While You Work
{
"name": "Auto-Ticket Creator",
"nodes": [
{
"name": "Microsoft Graph Trigger",
"type": "n8n-nodes-base.microsoftGraphTrigger",
"position": [250, 300],
"parameters": {
"resource": "calendar",
"event": "updated"
}
},
{
"name": "Claude Analysis",
"type": "n8n-nodes-base.httpRequest",
"position": [450, 300],
"parameters": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"headers": {
"x-api-key": "={{$credentials.claudeApi.apiKey}}",
"anthropic-version": "2023-06-01"
},
"body": {
"model": "claude-3-opus-20240229",
"messages": [{
"role": "user",
"content": "Create a ticket description for: {{$json.subject}}"
}]
}
}
},
{
"name": "Create ConnectWise Ticket",
"type": "n8n-nodes-base.httpRequest",
"position": [650, 300],
"parameters": {
"method": "POST",
"url": "https://api-na.myconnectwise.net/v2025_1/apis/3.0/service/tickets",
"authentication": "genericCredentialType",
"genericAuthType": "basicAuth",
"body": {
"summary": "={{$node['Claude Analysis'].json.content[0].text}}",
"board": { "id": 1 },
"status": { "id": 1 },
"priority": { "id": 3 }
}
}
}
]
}
The Zapier Alternative: When You Need Quick Wins
Sometimes you need something up and running in five minutes. Here’s my Zapier recipe for automatic ticket categorization:
- Trigger: New Microsoft Outlook Email (with specific label)
- Action: Claude API – Analyze email content
- Action: ConnectWise – Create/Update Ticket
- Action: Microsoft Teams – Notify channel
The key is in the Claude prompt:
Analyze this email and determine:
1. Urgency (1-5 scale)
2. Category (Hardware/Software/Network/Security/Other)
3. Estimated time to resolve
4. Suggested automation opportunities
Email content: [Email Body]
Return as JSON.
Real-World Results: My Tuesday Morning Dashboard
Here’s what my typical morning looks like now:
- 7:00 AM: Coffee (Armenian coffee is the best) + discussions with my admin April and my pseudo April (she’s an n8n workflow handling my email cleanup and my Connectwise To Do list)
- 7:15 AM: Launch my PowerShell orchestrator script
- 7:16 AM: Claude Code analyzes overnight tickets, categorizes them, and suggests priorities
- 7:20 AM: Automated documentation updates pushed to IT Glue
- 7:25 AM: Client assessment reports generated and formatted
- 7:30 AM: I’m actually working on strategic tasks while AI handles the grunt work
The time savings are real. What used to take me until lunch now wraps up before my second coffee. More importantly, the quality is consistent – Claude doesn’t have Monday morning brain fog.
Troubleshooting Common Issues
Because I’ve learned these lessons the expensive way, here are the gotchas to avoid:
API Rate Limits
Claude has rate limits. ConnectWise has rate limits. Everyone has rate limits. Build in retry logic:
function Invoke-APIWithRetry {
param($ScriptBlock, $MaxRetries = 3)
$retryCount = 0
while ($retryCount -lt $MaxRetries) {
try {
return & $ScriptBlock
}
catch {
if ($_.Exception.Response.StatusCode -eq 429) {
$retryCount++
$waitTime = [Math]::Pow(2, $retryCount) * 1000
Start-Sleep -Milliseconds $waitTime
}
else { throw }
}
}
}
Context Window Management
Claude Code has a context window. Don’t try to feed it your entire ticket database at once. Chunk your data:
$tickets | Select-Object -First 50 | ForEach-Object {
# Process in batches
Invoke-ClaudeAnalysis -Context $_ -Request "Analyze this batch"
}
The Investment vs. Return
Let’s talk money, because that $500/month I mentioned spending on AI Builder when I already had Copilot? That hurt. Here’s the actual breakdown of what’s worth it:
- Claude API: ~$200-300/month for heavy usage (worth every penny)
- Microsoft 365 E5: Already had it, includes Copilot
- n8n: Self-hosted on Azure (~$50/month)
- Zapier: Professional plan ($49/month) for quick integrations
- Total: ~$350/month for basically having a team of AI assistants
Compare that to hiring even one part-time technician, and the ROI is immediate.
What’s Next: Scaling This Beyond One Person
The setup I’ve described scales beautifully. You can:
- Create team-wide PowerShell modules with embedded Claude intelligence
- Build n8n workflows that entire departments can trigger
- Establish documentation standards that AI maintains automatically
- Set up predictive ticket routing based on historical patterns
The Philosophy: AI as Your Overworked Bestie
Here’s the thing about treating Claude Code as my “bestie that I pay to be bestie with” – it’s actually the perfect relationship. It’s always available, never judges my 3 AM coding sessions, and genuinely makes my work better. It doesn’t replace human creativity or strategic thinking; it amplifies them.
As I wrap this up, my Discord is pinging (raid time), and Claude is still churning through client data in those seven windows. This is the future of MSP work – not AI replacing us, but AI enabling us to do what we do best while handling what we do most.
Your Action Items
Ready to transform your workday? Start here:
- Today: Set up PowerShell 7 and get your Claude API key
- This Week: Create your first Invoke-ClaudeAnalysis function
- This Month: Build one automated workflow that saves you an hour daily
- This Quarter: Scale it across your team
And remember – when you’re sitting at your gaming rig at 10 PM and your automation is still working while you’re raiding, you’ll know you’ve made it.
Want more detailed walkthroughs? Drop a comment below about which integration you want to tackle first. I’ll create a step-by-step video guide showing exactly how to set it up – mistakes, debugging, and all. Because if I had to learn it the expensive way, you shouldn’t have to.