sync: Auto-sync from ACG-M-L5090 at 2026-02-01 16:23:43
This commit is contained in:
158
Check-DataforthMailboxType.ps1
Normal file
158
Check-DataforthMailboxType.ps1
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# Check if notifications@dataforth.com is a shared mailbox and authentication options
|
||||||
|
# This determines how the website should authenticate
|
||||||
|
|
||||||
|
Write-Host "[OK] Checking mailbox configuration..." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Check if connected to Exchange Online
|
||||||
|
$Session = Get-PSSession | Where-Object { $_.ConfigurationName -eq "Microsoft.Exchange" -and $_.State -eq "Opened" }
|
||||||
|
if (-not $Session) {
|
||||||
|
Write-Host "[WARNING] Not connected to Exchange Online, connecting..." -ForegroundColor Yellow
|
||||||
|
Connect-ExchangeOnline -UserPrincipalName sysadmin@dataforth.com -ShowBanner:$false
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "1. MAILBOX TYPE"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
$Mailbox = Get-Mailbox -Identity notifications@dataforth.com
|
||||||
|
|
||||||
|
Write-Host "[OK] Mailbox Details:"
|
||||||
|
Write-Host " Primary SMTP: $($Mailbox.PrimarySmtpAddress)"
|
||||||
|
Write-Host " Display Name: $($Mailbox.DisplayName)"
|
||||||
|
Write-Host " Type: $($Mailbox.RecipientTypeDetails)" -ForegroundColor Cyan
|
||||||
|
Write-Host " Alias: $($Mailbox.Alias)"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if ($Mailbox.RecipientTypeDetails -eq "SharedMailbox") {
|
||||||
|
Write-Host "[CRITICAL] This is a SHARED MAILBOX" -ForegroundColor Red
|
||||||
|
Write-Host " Shared mailboxes CANNOT authenticate directly!" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Options for website authentication:" -ForegroundColor Yellow
|
||||||
|
Write-Host " 1. Use a regular user account with 'Send As' permissions"
|
||||||
|
Write-Host " 2. Convert to regular mailbox (requires license)"
|
||||||
|
Write-Host " 3. Use Microsoft Graph API with OAuth"
|
||||||
|
$IsShared = $true
|
||||||
|
} elseif ($Mailbox.RecipientTypeDetails -eq "UserMailbox") {
|
||||||
|
Write-Host "[OK] This is a USER MAILBOX" -ForegroundColor Green
|
||||||
|
Write-Host " Can authenticate directly with SMTP AUTH" -ForegroundColor Green
|
||||||
|
$IsShared = $false
|
||||||
|
} else {
|
||||||
|
Write-Host "[WARNING] Mailbox type: $($Mailbox.RecipientTypeDetails)" -ForegroundColor Yellow
|
||||||
|
$IsShared = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "2. SMTP AUTH STATUS"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
$CASMailbox = Get-CASMailbox -Identity notifications@dataforth.com
|
||||||
|
|
||||||
|
Write-Host "[OK] Client Access Settings:"
|
||||||
|
Write-Host " SMTP AUTH Disabled: $($CASMailbox.SmtpClientAuthenticationDisabled)"
|
||||||
|
|
||||||
|
if ($CASMailbox.SmtpClientAuthenticationDisabled -eq $true) {
|
||||||
|
Write-Host " [ERROR] SMTP AUTH is DISABLED!" -ForegroundColor Red
|
||||||
|
if (-not $IsShared) {
|
||||||
|
Write-Host " [FIX] To enable: Set-CASMailbox -Identity notifications@dataforth.com -SmtpClientAuthenticationDisabled `$false" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host " [OK] SMTP AUTH is ENABLED" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "3. LICENSE STATUS"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
# Check licenses via Get-MsolUser or Microsoft Graph
|
||||||
|
try {
|
||||||
|
$MsolUser = Get-MsolUser -UserPrincipalName notifications@dataforth.com -ErrorAction SilentlyContinue
|
||||||
|
if ($MsolUser) {
|
||||||
|
Write-Host "[OK] License Status:"
|
||||||
|
Write-Host " Licensed: $($MsolUser.IsLicensed)"
|
||||||
|
if ($MsolUser.IsLicensed) {
|
||||||
|
Write-Host " Licenses: $($MsolUser.Licenses.AccountSkuId -join ', ')"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[WARNING] Could not check licenses via MSOnline module" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "[WARNING] MSOnline module not available" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "4. SEND AS PERMISSIONS (if shared mailbox)"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
if ($IsShared) {
|
||||||
|
$SendAsPermissions = Get-RecipientPermission -Identity notifications@dataforth.com | Where-Object { $_.Trustee -ne "NT AUTHORITY\SELF" }
|
||||||
|
|
||||||
|
if ($SendAsPermissions) {
|
||||||
|
Write-Host "[OK] Users/Groups with 'Send As' permission:"
|
||||||
|
foreach ($Perm in $SendAsPermissions) {
|
||||||
|
Write-Host " - $($Perm.Trustee) ($($Perm.AccessRights))" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[SOLUTION] The website can authenticate using one of these accounts" -ForegroundColor Green
|
||||||
|
Write-Host " with 'Send As' permission, then send as notifications@dataforth.com" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[WARNING] No 'Send As' permissions configured" -ForegroundColor Yellow
|
||||||
|
Write-Host " Grant permission: Add-RecipientPermission -Identity notifications@dataforth.com -Trustee <user> -AccessRights SendAs" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "RECOMMENDATIONS FOR WEBSITE AUTHENTICATION"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
if ($IsShared) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OPTION 1] Use a service account with Send As permission" -ForegroundColor Cyan
|
||||||
|
Write-Host " 1. Create/use existing user account (e.g., sysadmin@dataforth.com)"
|
||||||
|
Write-Host " 2. Grant Send As permission:"
|
||||||
|
Write-Host " Add-RecipientPermission -Identity notifications@dataforth.com -Trustee sysadmin@dataforth.com -AccessRights SendAs"
|
||||||
|
Write-Host " 3. Website config:"
|
||||||
|
Write-Host " - SMTP Server: smtp.office365.com"
|
||||||
|
Write-Host " - Port: 587"
|
||||||
|
Write-Host " - Username: sysadmin@dataforth.com"
|
||||||
|
Write-Host " - Password: <sysadmin password>"
|
||||||
|
Write-Host " - From Address: notifications@dataforth.com"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OPTION 2] Convert to regular mailbox (requires license)" -ForegroundColor Cyan
|
||||||
|
Write-Host " Set-Mailbox -Identity notifications@dataforth.com -Type Regular"
|
||||||
|
Write-Host " Then assign a license and enable SMTP AUTH"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OPTION 3] Use Microsoft Graph API (OAuth - modern auth)" -ForegroundColor Cyan
|
||||||
|
Write-Host " Most secure but requires application changes"
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[SOLUTION] This is a regular mailbox - can authenticate directly" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Website SMTP Configuration:"
|
||||||
|
Write-Host " - SMTP Server: smtp.office365.com"
|
||||||
|
Write-Host " - Port: 587 (STARTTLS)"
|
||||||
|
Write-Host " - Username: notifications@dataforth.com"
|
||||||
|
Write-Host " - Password: <account password>"
|
||||||
|
Write-Host " - Authentication: Required"
|
||||||
|
Write-Host " - SSL/TLS: Yes"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if ($CASMailbox.SmtpClientAuthenticationDisabled -eq $false) {
|
||||||
|
Write-Host "[OK] SMTP AUTH is enabled - credentials should work" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "If still failing, check:" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Correct password in website config"
|
||||||
|
Write-Host " - Firewall allowing outbound port 587"
|
||||||
|
Write-Host " - Run Test-DataforthSMTP.ps1 to verify credentials"
|
||||||
|
} else {
|
||||||
|
Write-Host "[ERROR] SMTP AUTH is DISABLED - must enable first!" -ForegroundColor Red
|
||||||
|
Write-Host "Run: Set-CASMailbox -Identity notifications@dataforth.com -SmtpClientAuthenticationDisabled `$false" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
124
Get-DataforthEmailLogs.ps1
Normal file
124
Get-DataforthEmailLogs.ps1
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Get Exchange Online logs for notifications@dataforth.com
|
||||||
|
# This script retrieves message traces and mailbox audit logs
|
||||||
|
|
||||||
|
Write-Host "[OK] Checking Exchange Online connection..." -ForegroundColor Green
|
||||||
|
|
||||||
|
# Check if connected to Exchange Online
|
||||||
|
$Session = Get-PSSession | Where-Object { $_.ConfigurationName -eq "Microsoft.Exchange" -and $_.State -eq "Opened" }
|
||||||
|
|
||||||
|
if (-not $Session) {
|
||||||
|
Write-Host "[WARNING] Not connected to Exchange Online" -ForegroundColor Yellow
|
||||||
|
Write-Host " Connecting now..." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
Connect-ExchangeOnline -UserPrincipalName sysadmin@dataforth.com -ShowBanner:$false
|
||||||
|
Write-Host "[OK] Connected to Exchange Online" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to connect to Exchange Online" -ForegroundColor Red
|
||||||
|
Write-Host " Error: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "1. Checking SMTP AUTH status"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
$CASMailbox = Get-CASMailbox -Identity notifications@dataforth.com
|
||||||
|
Write-Host "[OK] SMTP AUTH Status:"
|
||||||
|
Write-Host " SmtpClientAuthenticationDisabled: $($CASMailbox.SmtpClientAuthenticationDisabled)"
|
||||||
|
|
||||||
|
if ($CASMailbox.SmtpClientAuthenticationDisabled -eq $true) {
|
||||||
|
Write-Host "[ERROR] SMTP AUTH is DISABLED for this mailbox!" -ForegroundColor Red
|
||||||
|
Write-Host " To enable: Set-CASMailbox -Identity notifications@dataforth.com -SmtpClientAuthenticationDisabled `$false" -ForegroundColor Yellow
|
||||||
|
} else {
|
||||||
|
Write-Host "[OK] SMTP AUTH is enabled" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "2. Checking message trace (last 7 days)"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
$StartDate = (Get-Date).AddDays(-7)
|
||||||
|
$EndDate = Get-Date
|
||||||
|
|
||||||
|
Write-Host "[OK] Searching for messages from notifications@dataforth.com..."
|
||||||
|
|
||||||
|
$Messages = Get-MessageTrace -SenderAddress notifications@dataforth.com -StartDate $StartDate -EndDate $EndDate
|
||||||
|
|
||||||
|
if ($Messages) {
|
||||||
|
Write-Host "[OK] Found $($Messages.Count) messages sent in the last 7 days" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$Messages | Select-Object -First 10 | Format-Table Received, RecipientAddress, Subject, Status, Size -AutoSize
|
||||||
|
|
||||||
|
$FailedMessages = $Messages | Where-Object { $_.Status -ne "Delivered" }
|
||||||
|
if ($FailedMessages) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[WARNING] Found $($FailedMessages.Count) failed/pending messages:" -ForegroundColor Yellow
|
||||||
|
$FailedMessages | Format-Table Received, RecipientAddress, Subject, Status -AutoSize
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[WARNING] No messages found in the last 7 days" -ForegroundColor Yellow
|
||||||
|
Write-Host " This suggests emails are not reaching Exchange Online" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "3. Checking mailbox audit logs"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
Write-Host "[OK] Checking for authentication events..."
|
||||||
|
|
||||||
|
$AuditLogs = Search-MailboxAuditLog -Identity notifications@dataforth.com -StartDate $StartDate -EndDate $EndDate -ShowDetails
|
||||||
|
|
||||||
|
if ($AuditLogs) {
|
||||||
|
Write-Host "[OK] Found $($AuditLogs.Count) audit events" -ForegroundColor Green
|
||||||
|
$AuditLogs | Select-Object -First 10 | Format-Table LastAccessed, Operation, LogonType, ClientIPAddress -AutoSize
|
||||||
|
} else {
|
||||||
|
Write-Host "[OK] No mailbox audit events found" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "4. Checking for failed authentication attempts (Unified Audit Log)"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
Write-Host "[OK] Searching for failed logins..."
|
||||||
|
|
||||||
|
$AuditRecords = Search-UnifiedAuditLog -UserIds notifications@dataforth.com -StartDate $StartDate -EndDate $EndDate -Operations UserLoginFailed,MailboxLogin -ResultSize 100
|
||||||
|
|
||||||
|
if ($AuditRecords) {
|
||||||
|
Write-Host "[WARNING] Found $($AuditRecords.Count) authentication events" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
foreach ($Record in $AuditRecords | Select-Object -First 5) {
|
||||||
|
$AuditData = $Record.AuditData | ConvertFrom-Json
|
||||||
|
Write-Host " [EVENT] $($Record.CreationDate)"
|
||||||
|
Write-Host " Operation: $($Record.Operations)"
|
||||||
|
Write-Host " Client IP: $($AuditData.ClientIP)"
|
||||||
|
Write-Host " Result: $($AuditData.ResultStatus)"
|
||||||
|
if ($AuditData.LogonError) {
|
||||||
|
Write-Host " Error: $($AuditData.LogonError)" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[OK] No failed authentication attempts found" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "SUMMARY"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "Review the logs above to identify the issue."
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Common issues:"
|
||||||
|
Write-Host " - SMTP AUTH disabled (check section 1)"
|
||||||
|
Write-Host " - Wrong credentials (check section 4 for failed logins)"
|
||||||
|
Write-Host " - No messages reaching Exchange (check section 2)"
|
||||||
|
Write-Host " - Firewall blocking connection"
|
||||||
|
Write-Host " - App needs app-specific password (if MFA enabled)"
|
||||||
140
Reset-DataforthAD-Password.ps1
Normal file
140
Reset-DataforthAD-Password.ps1
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Reset password for notifications@dataforth.com in on-premises AD
|
||||||
|
# For hybrid environments with Azure AD Connect password sync
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$DomainController = "192.168.0.27", # AD1 (primary DC)
|
||||||
|
[string]$NewPassword = "%5cfI:G71)}=g4ZS"
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host "[OK] Resetting password in on-premises Active Directory..." -ForegroundColor Green
|
||||||
|
Write-Host " Domain Controller: $DomainController (AD1)" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Credentials for remote connection
|
||||||
|
$AdminUser = "INTRANET\sysadmin"
|
||||||
|
$AdminPassword = ConvertTo-SecureString "Paper123!@#" -AsPlainText -Force
|
||||||
|
$Credential = New-Object System.Management.Automation.PSCredential($AdminUser, $AdminPassword)
|
||||||
|
|
||||||
|
Write-Host "[OK] Connecting to $DomainController via PowerShell remoting..." -ForegroundColor Green
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Execute on remote DC
|
||||||
|
Invoke-Command -ComputerName $DomainController -Credential $Credential -ScriptBlock {
|
||||||
|
param($NewPass, $UserName)
|
||||||
|
|
||||||
|
Import-Module ActiveDirectory
|
||||||
|
|
||||||
|
# Find the user account
|
||||||
|
Write-Host "[OK] Searching for user in Active Directory..."
|
||||||
|
$User = Get-ADUser -Filter "UserPrincipalName -eq '$UserName'" -Properties PasswordNeverExpires, PasswordLastSet
|
||||||
|
|
||||||
|
if (-not $User) {
|
||||||
|
Write-Host "[ERROR] User not found in Active Directory!" -ForegroundColor Red
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[OK] Found user: $($User.Name) ($($User.UserPrincipalName))"
|
||||||
|
Write-Host " Current PasswordNeverExpires: $($User.PasswordNeverExpires)"
|
||||||
|
Write-Host " Last Password Set: $($User.PasswordLastSet)"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Reset password
|
||||||
|
Write-Host "[OK] Resetting password..." -ForegroundColor Green
|
||||||
|
$SecurePassword = ConvertTo-SecureString $NewPass -AsPlainText -Force
|
||||||
|
Set-ADAccountPassword -Identity $User.SamAccountName -NewPassword $SecurePassword -Reset
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] Password reset successfully!" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Set password to never expire
|
||||||
|
Write-Host "[OK] Setting password to never expire..." -ForegroundColor Green
|
||||||
|
Set-ADUser -Identity $User.SamAccountName -PasswordNeverExpires $true -ChangePasswordAtLogon $false
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] Password set to never expire!" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
$UpdatedUser = Get-ADUser -Identity $User.SamAccountName -Properties PasswordNeverExpires, PasswordLastSet
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OK] Verification:"
|
||||||
|
Write-Host " PasswordNeverExpires: $($UpdatedUser.PasswordNeverExpires)"
|
||||||
|
Write-Host " PasswordLastSet: $($UpdatedUser.PasswordLastSet)"
|
||||||
|
|
||||||
|
# Force Azure AD Connect sync (if available)
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OK] Checking for Azure AD Connect..." -ForegroundColor Green
|
||||||
|
if (Get-Command Start-ADSyncSyncCycle -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "[OK] Triggering Azure AD Connect sync..." -ForegroundColor Green
|
||||||
|
Start-ADSyncSyncCycle -PolicyType Delta
|
||||||
|
Write-Host "[OK] Sync triggered - password will sync to Azure AD in ~3 minutes" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[WARNING] Azure AD Connect not found on this server" -ForegroundColor Yellow
|
||||||
|
Write-Host " Password will sync automatically within 30 minutes" -ForegroundColor Yellow
|
||||||
|
Write-Host " Or manually trigger sync on AAD Connect server" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
} -ArgumentList $NewPassword, "notifications@dataforth.com"
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "PASSWORD RESET COMPLETE"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "New Password: $NewPassword" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OK] Password policy: NEVER EXPIRES (set in AD)" -ForegroundColor Green
|
||||||
|
Write-Host "[OK] Azure AD Connect will sync this change automatically" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "NEXT STEPS"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "1. Wait 3-5 minutes for Azure AD Connect to sync" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "2. Update website SMTP configuration:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Username: notifications@dataforth.com"
|
||||||
|
Write-Host " - Password: $NewPassword" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "3. Test SMTP authentication:" -ForegroundColor Cyan
|
||||||
|
Write-Host " D:\ClaudeTools\Test-DataforthSMTP.ps1"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "4. Verify authentication succeeds:" -ForegroundColor Cyan
|
||||||
|
Write-Host " D:\ClaudeTools\Get-DataforthEmailLogs.ps1"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Save credentials
|
||||||
|
$CredPath = "D:\ClaudeTools\dataforth-notifications-FINAL-PASSWORD.txt"
|
||||||
|
@"
|
||||||
|
Dataforth Notifications Account - PASSWORD RESET (HYBRID AD)
|
||||||
|
Reset Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
Username: notifications@dataforth.com
|
||||||
|
Password: $NewPassword
|
||||||
|
|
||||||
|
Password Policy:
|
||||||
|
- Set in: On-Premises Active Directory (INTRANET domain)
|
||||||
|
- Never Expires: YES
|
||||||
|
- Synced to Azure AD: Via Azure AD Connect
|
||||||
|
|
||||||
|
SMTP Configuration for Website:
|
||||||
|
- Server: smtp.office365.com
|
||||||
|
- Port: 587
|
||||||
|
- TLS: Yes
|
||||||
|
- Username: notifications@dataforth.com
|
||||||
|
- Password: $NewPassword
|
||||||
|
|
||||||
|
Note: Allow 3-5 minutes for password to sync to Azure AD before testing.
|
||||||
|
|
||||||
|
DO NOT COMMIT TO GIT OR SHARE PUBLICLY
|
||||||
|
"@ | Out-File -FilePath $CredPath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host "[OK] Credentials saved to: $CredPath" -ForegroundColor Green
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to reset password: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Troubleshooting:" -ForegroundColor Yellow
|
||||||
|
Write-Host "- Ensure you're on the Dataforth VPN or network" -ForegroundColor Yellow
|
||||||
|
Write-Host "- Verify AD1 (192.168.0.27) is accessible" -ForegroundColor Yellow
|
||||||
|
Write-Host "- Check WinRM is enabled on AD1" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Alternative: RDP to AD1 and run locally:" -ForegroundColor Cyan
|
||||||
|
Write-Host " Set-ADAccountPassword -Identity notifications -Reset -NewPassword (ConvertTo-SecureString '$NewPassword' -AsPlainText -Force)" -ForegroundColor Gray
|
||||||
|
Write-Host " Set-ADUser -Identity notifications -PasswordNeverExpires `$true -ChangePasswordAtLogon `$false" -ForegroundColor Gray
|
||||||
|
}
|
||||||
105
Reset-DataforthNotificationsPassword.ps1
Normal file
105
Reset-DataforthNotificationsPassword.ps1
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Reset password for notifications@dataforth.com and set to never expire
|
||||||
|
# Using Microsoft Graph PowerShell (modern approach)
|
||||||
|
|
||||||
|
Write-Host "[OK] Resetting password for notifications@dataforth.com..." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Check if Microsoft.Graph module is installed
|
||||||
|
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Users)) {
|
||||||
|
Write-Host "[WARNING] Microsoft.Graph.Users module not installed" -ForegroundColor Yellow
|
||||||
|
Write-Host " Installing now..." -ForegroundColor Yellow
|
||||||
|
Install-Module Microsoft.Graph.Users -Scope CurrentUser -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect to Microsoft Graph
|
||||||
|
Write-Host "[OK] Connecting to Microsoft Graph..." -ForegroundColor Green
|
||||||
|
Connect-MgGraph -Scopes "User.ReadWrite.All", "Directory.ReadWrite.All" -TenantId "7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584"
|
||||||
|
|
||||||
|
# Generate a strong random password
|
||||||
|
Add-Type -AssemblyName System.Web
|
||||||
|
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(16, 4)
|
||||||
|
|
||||||
|
Write-Host "[OK] Generated new password: $NewPassword" -ForegroundColor Cyan
|
||||||
|
Write-Host " SAVE THIS PASSWORD - you'll need it for the website config" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Reset the password
|
||||||
|
$PasswordProfile = @{
|
||||||
|
Password = $NewPassword
|
||||||
|
ForceChangePasswordNextSignIn = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Update-MgUser -UserId "notifications@dataforth.com" -PasswordProfile $PasswordProfile
|
||||||
|
Write-Host "[SUCCESS] Password reset successfully!" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to reset password: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set password to never expire
|
||||||
|
Write-Host "[OK] Setting password to never expire..." -ForegroundColor Green
|
||||||
|
|
||||||
|
try {
|
||||||
|
Update-MgUser -UserId "notifications@dataforth.com" -PasswordPolicies "DisablePasswordExpiration"
|
||||||
|
Write-Host "[SUCCESS] Password set to never expire!" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to set password policy: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify the settings
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "Verifying Configuration"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
|
||||||
|
$User = Get-MgUser -UserId "notifications@dataforth.com" -Property UserPrincipalName,PasswordPolicies,LastPasswordChangeDateTime
|
||||||
|
|
||||||
|
Write-Host "[OK] User: $($User.UserPrincipalName)"
|
||||||
|
Write-Host " Password Policies: $($User.PasswordPolicies)"
|
||||||
|
Write-Host " Last Password Change: $($User.LastPasswordChangeDateTime)"
|
||||||
|
|
||||||
|
if ($User.PasswordPolicies -contains "DisablePasswordExpiration") {
|
||||||
|
Write-Host " [OK] Password will never expire" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host " [WARNING] Password expiration policy not confirmed" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "NEXT STEPS"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "1. Update the website SMTP configuration with:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Username: notifications@dataforth.com"
|
||||||
|
Write-Host " - Password: $NewPassword" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "2. Test SMTP authentication:"
|
||||||
|
Write-Host " D:\ClaudeTools\Test-DataforthSMTP.ps1"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "3. Monitor for successful sends:"
|
||||||
|
Write-Host " Get-MessageTrace -SenderAddress notifications@dataforth.com -StartDate (Get-Date).AddHours(-1)"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Save credentials to a secure file for reference
|
||||||
|
$CredPath = "D:\ClaudeTools\dataforth-notifications-creds.txt"
|
||||||
|
@"
|
||||||
|
Dataforth Notifications Account Credentials
|
||||||
|
Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
Username: notifications@dataforth.com
|
||||||
|
Password: $NewPassword
|
||||||
|
|
||||||
|
SMTP Configuration for Website:
|
||||||
|
- Server: smtp.office365.com
|
||||||
|
- Port: 587
|
||||||
|
- TLS: Yes
|
||||||
|
- Username: notifications@dataforth.com
|
||||||
|
- Password: $NewPassword
|
||||||
|
|
||||||
|
DO NOT COMMIT TO GIT OR SHARE PUBLICLY
|
||||||
|
"@ | Out-File -FilePath $CredPath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host "[OK] Credentials saved to: $CredPath" -ForegroundColor Green
|
||||||
|
Write-Host " (Keep this file secure!)" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
Disconnect-MgGraph
|
||||||
81
Reset-Password-ExchangeOnline.ps1
Normal file
81
Reset-Password-ExchangeOnline.ps1
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Reset password for notifications@dataforth.com using Exchange Online
|
||||||
|
# This works when Microsoft Graph permissions are insufficient
|
||||||
|
|
||||||
|
Write-Host "[OK] Resetting password via Azure AD (using web portal method)..." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$UserPrincipalName = "notifications@dataforth.com"
|
||||||
|
|
||||||
|
# Generate a strong password
|
||||||
|
Add-Type -AssemblyName System.Web
|
||||||
|
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(16, 4)
|
||||||
|
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "PASSWORD RESET OPTIONS"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OPTION 1] Use Azure AD Portal (Recommended - Always Works)" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "1. Open browser to: https://portal.azure.com"
|
||||||
|
Write-Host "2. Navigate to: Azure Active Directory > Users"
|
||||||
|
Write-Host "3. Search for: notifications@dataforth.com"
|
||||||
|
Write-Host "4. Click 'Reset password'"
|
||||||
|
Write-Host "5. Use this generated password: $NewPassword" -ForegroundColor Yellow
|
||||||
|
Write-Host "6. UNCHECK 'Make this user change password on first sign in'"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[OPTION 2] Use PowerShell with Elevated Admin Account" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "If you have a Global Admin account, connect to Azure AD:"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Install-Module AzureAD -Scope CurrentUser" -ForegroundColor Gray
|
||||||
|
Write-Host "Connect-AzureAD -TenantId 7dfa3ce8-c496-4b51-ab8d-bd3dcd78b584" -ForegroundColor Gray
|
||||||
|
Write-Host "`$Password = ConvertTo-SecureString '$NewPassword' -AsPlainText -Force" -ForegroundColor Gray
|
||||||
|
Write-Host "Set-AzureADUserPassword -ObjectId notifications@dataforth.com -Password `$Password -ForceChangePasswordNextSignIn `$false" -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "RECOMMENDED PASSWORD"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " $NewPassword" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "SAVE THIS PASSWORD for the website configuration!"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Save to file
|
||||||
|
$CredPath = "D:\ClaudeTools\dataforth-notifications-NEW-PASSWORD.txt"
|
||||||
|
@"
|
||||||
|
Dataforth Notifications Account - PASSWORD RESET
|
||||||
|
Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
Username: notifications@dataforth.com
|
||||||
|
NEW Password: $NewPassword
|
||||||
|
|
||||||
|
IMPORTANT: Password policy is already set to never expire!
|
||||||
|
You just need to reset the actual password.
|
||||||
|
|
||||||
|
SMTP Configuration for Website:
|
||||||
|
- Server: smtp.office365.com
|
||||||
|
- Port: 587
|
||||||
|
- TLS: Yes
|
||||||
|
- Username: notifications@dataforth.com
|
||||||
|
- Password: $NewPassword
|
||||||
|
|
||||||
|
STATUS:
|
||||||
|
- Password Never Expires: YES (already configured)
|
||||||
|
- Password Reset: PENDING (use Azure portal or PowerShell above)
|
||||||
|
|
||||||
|
DO NOT COMMIT TO GIT OR SHARE PUBLICLY
|
||||||
|
"@ | Out-File -FilePath $CredPath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host "[OK] Instructions and password saved to:" -ForegroundColor Green
|
||||||
|
Write-Host " $CredPath" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "AFTER RESETTING PASSWORD"
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "1. Update website SMTP config with new password"
|
||||||
|
Write-Host "2. Test: D:\ClaudeTools\Test-DataforthSMTP.ps1"
|
||||||
|
Write-Host "3. Verify: Get-MessageTrace -SenderAddress notifications@dataforth.com"
|
||||||
|
Write-Host ""
|
||||||
69
Test-DataforthSMTP.ps1
Normal file
69
Test-DataforthSMTP.ps1
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Test SMTP Authentication for notifications@dataforth.com
|
||||||
|
# This script tests SMTP authentication to verify credentials work
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Password = $(Read-Host -Prompt "Enter password for notifications@dataforth.com" -AsSecureString | ConvertFrom-SecureString)
|
||||||
|
)
|
||||||
|
|
||||||
|
$SMTPServer = "smtp.office365.com"
|
||||||
|
$SMTPPort = 587
|
||||||
|
$Username = "notifications@dataforth.com"
|
||||||
|
|
||||||
|
Write-Host "[OK] Testing SMTP authentication..." -ForegroundColor Green
|
||||||
|
Write-Host " Server: $SMTPServer"
|
||||||
|
Write-Host " Port: $SMTPPort"
|
||||||
|
Write-Host " Username: $Username"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Create secure password
|
||||||
|
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
|
||||||
|
$Credential = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)
|
||||||
|
|
||||||
|
# Create SMTP client
|
||||||
|
$SMTPClient = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort)
|
||||||
|
$SMTPClient.EnableSsl = $true
|
||||||
|
$SMTPClient.Credentials = $Credential
|
||||||
|
|
||||||
|
# Create test message
|
||||||
|
$MailMessage = New-Object System.Net.Mail.MailMessage
|
||||||
|
$MailMessage.From = $Username
|
||||||
|
$MailMessage.To.Add($Username)
|
||||||
|
$MailMessage.Subject = "SMTP Test - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
||||||
|
$MailMessage.Body = "This is a test message to verify SMTP authentication."
|
||||||
|
|
||||||
|
Write-Host "[OK] Sending test email..." -ForegroundColor Green
|
||||||
|
$SMTPClient.Send($MailMessage)
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] SMTP authentication successful!" -ForegroundColor Green
|
||||||
|
Write-Host " Test email sent successfully." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OK] The credentials work correctly." -ForegroundColor Green
|
||||||
|
Write-Host " If the website is still failing, check:" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Website SMTP configuration" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Firewall rules blocking port 587" -ForegroundColor Yellow
|
||||||
|
Write-Host " - IP address restrictions in M365" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] SMTP authentication failed!" -ForegroundColor Red
|
||||||
|
Write-Host " Error: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if ($_.Exception.Message -like "*authentication*") {
|
||||||
|
Write-Host "[ISSUE] Authentication credentials are incorrect" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Verify the password is correct" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Check if MFA requires an app password" -ForegroundColor Yellow
|
||||||
|
} elseif ($_.Exception.Message -like "*5.7.57*") {
|
||||||
|
Write-Host "[ISSUE] SMTP AUTH is disabled for this tenant or user" -ForegroundColor Yellow
|
||||||
|
Write-Host " Run: Set-CASMailbox -Identity notifications@dataforth.com -SmtpClientAuthenticationDisabled `$false" -ForegroundColor Yellow
|
||||||
|
} elseif ($_.Exception.Message -like "*connection*") {
|
||||||
|
Write-Host "[ISSUE] Connection problem" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Check firewall rules" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Verify port 587 is accessible" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================================================================"
|
||||||
|
Write-Host "Next: Check Exchange Online logs for more details"
|
||||||
|
Write-Host "================================================================"
|
||||||
273
azcomputerguru-changelog.md
Normal file
273
azcomputerguru-changelog.md
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
# Arizona Computer Guru Redesign - Change Log
|
||||||
|
|
||||||
|
## Version 2.0.0 - "Desert Brutalism" (2026-02-01)
|
||||||
|
|
||||||
|
### MAJOR CHANGES FROM PREVIOUS VERSION
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typography Transformation
|
||||||
|
|
||||||
|
### BEFORE
|
||||||
|
- Inter (generic, overused)
|
||||||
|
- Standard weights
|
||||||
|
- Minimal letter-spacing
|
||||||
|
- Conservative sizing
|
||||||
|
|
||||||
|
### AFTER
|
||||||
|
- **Space Grotesk** - Geometric brutalist headings
|
||||||
|
- **IBM Plex Sans** - Warm technical body text
|
||||||
|
- **JetBrains Mono** - Monospace tech accents
|
||||||
|
- Negative letter-spacing (-0.03em to -0.01em)
|
||||||
|
- Bolder sizing (H1: 3.5-5rem vs 2rem)
|
||||||
|
- Uppercase dominance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Color Palette Evolution
|
||||||
|
|
||||||
|
### BEFORE
|
||||||
|
```css
|
||||||
|
--color2: #f57c00 /* Generic orange */
|
||||||
|
--color1: #1b263b /* Navy blue */
|
||||||
|
--color3: #0d1b2a /* Dark blue */
|
||||||
|
```
|
||||||
|
|
||||||
|
### AFTER
|
||||||
|
```css
|
||||||
|
--sunset-copper: #D4771C /* Warmer, deeper orange */
|
||||||
|
--midnight-desert: #0A0F14 /* Near-black with blue undertones */
|
||||||
|
--canyon-shadow: #2D1B14 /* Deep brown */
|
||||||
|
--sandstone: #E8D5C4 /* Warm neutral */
|
||||||
|
--neon-accent: #00FFA3 /* Cyberpunk green - NEW */
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact:** Shifted from blue-heavy to warm desert palette with unexpected neon accent
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visual Effects Added
|
||||||
|
|
||||||
|
### Geometric Transforms
|
||||||
|
- **NEW:** `skewY(-2deg)` on cards and boxes
|
||||||
|
- **NEW:** `skewX(-5deg)` on navigation hovers
|
||||||
|
- **NEW:** Angular elements mimicking geological strata
|
||||||
|
|
||||||
|
### Border Treatments
|
||||||
|
- **BEFORE:** 2-5px borders
|
||||||
|
- **AFTER:** 8-12px thick brutalist borders
|
||||||
|
- **NEW:** Neon accent borders (left/bottom)
|
||||||
|
- **NEW:** Border width changes on hover (8px → 12px)
|
||||||
|
|
||||||
|
### Shadow System
|
||||||
|
- **BEFORE:** Simple box-shadows
|
||||||
|
- **AFTER:** Dramatic offset shadows (4px, 8px, 12px)
|
||||||
|
- **NEW:** Neon glow shadows: `0 0 20px rgba(0, 255, 163, 0.3)`
|
||||||
|
- **NEW:** Multi-layer shadows on hover
|
||||||
|
|
||||||
|
### Background Textures
|
||||||
|
- **NEW:** Radial gradient overlays
|
||||||
|
- **NEW:** Repeating line patterns
|
||||||
|
- **NEW:** Desert texture simulation
|
||||||
|
- **NEW:** Gradient overlays on dark sections
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interactive Animations
|
||||||
|
|
||||||
|
### Link Hover Effects
|
||||||
|
- **BEFORE:** Simple color change
|
||||||
|
- **AFTER:** Underline slide animation (::after pseudo-element)
|
||||||
|
- Width: 0 → 100%
|
||||||
|
- Positioned with absolute bottom
|
||||||
|
|
||||||
|
### Button Animations
|
||||||
|
- **BEFORE:** Background + color transition
|
||||||
|
- **AFTER:** Background slide-in effect (::before pseudo-element)
|
||||||
|
- Left: -100% → 0
|
||||||
|
- Neon glow on hover
|
||||||
|
|
||||||
|
### Card Hover Effects
|
||||||
|
- **BEFORE:** `translateY(-4px)` + shadow
|
||||||
|
- **AFTER:** Combined transform: `skewY(-2deg) translateY(-8px) scale(1.02)`
|
||||||
|
- Border thickness change
|
||||||
|
- Neon glow shadow
|
||||||
|
- Multiple property transitions
|
||||||
|
|
||||||
|
### Icon Animations
|
||||||
|
- **NEW:** `scale(1.2) rotate(-5deg)` on button box icons
|
||||||
|
- **NEW:** Neon glow filter effect
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Component-Specific Changes
|
||||||
|
|
||||||
|
### Navigation
|
||||||
|
- **Font:** Inter → Space Grotesk
|
||||||
|
- **Weight:** 500 → 600
|
||||||
|
- **Border:** 2px → 4px (active states)
|
||||||
|
- **Hover:** Simple background → Skewed background + border animation
|
||||||
|
- **CTA Button:** Orange → Neon green with glow
|
||||||
|
|
||||||
|
### Above Header
|
||||||
|
- **Background:** Gradient → Solid midnight desert
|
||||||
|
- **Border:** Gradient border → 4px solid copper
|
||||||
|
- **Font:** Inter → JetBrains Mono
|
||||||
|
- **Link hover:** Color change → Underline slide + color
|
||||||
|
|
||||||
|
### Feature/Hero Section
|
||||||
|
- **Background:** Simple gradient → Desert gradient + textured overlay
|
||||||
|
- **Typography:** 2rem → 4.5rem headings
|
||||||
|
- **Shadow:** Simple → 4px offset with transparency
|
||||||
|
- **Overlay:** None → Multi-layer pattern overlays
|
||||||
|
|
||||||
|
### Columns Upper (Cards)
|
||||||
|
- **Transform:** None → `skewY(-2deg)`
|
||||||
|
- **Border:** None → 8px neon left border
|
||||||
|
- **Hover:** `translateY(-4px)` → Complex transform + scale
|
||||||
|
- **Background:** Solid → Gradient overlay effect
|
||||||
|
|
||||||
|
### Button Boxes
|
||||||
|
- **Border:** 15px orange → 12px copper (mobile: 8px)
|
||||||
|
- **Transform:** None → `skewY(-2deg)`
|
||||||
|
- **Hover:** Simple → Background slide + border color change
|
||||||
|
- **Icon:** Static → Scale + rotate animation
|
||||||
|
- **Size:** 25rem → 28rem height
|
||||||
|
|
||||||
|
### Footer
|
||||||
|
- **Background:** Solid dark → Gradient + repeating line texture
|
||||||
|
- **Border:** Simple → 6px copper top border
|
||||||
|
- **Links:** Color transition → Underline slide animation
|
||||||
|
- **Headings:** Orange → Neon green with left border
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Layout Changes
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Increased padding on major sections (2rem → 4rem, 8rem)
|
||||||
|
- More generous margins on cards (0.5rem → 1rem)
|
||||||
|
- Better breathing room in content areas
|
||||||
|
|
||||||
|
### Typography Scale
|
||||||
|
- **H1:** 2rem → 3.5-5rem
|
||||||
|
- **H2:** 1.6rem → 2.4-3.5rem
|
||||||
|
- **H3:** 1.2rem → 1.6-2.2rem
|
||||||
|
- **Body:** 1.2rem (maintained, improved line-height)
|
||||||
|
|
||||||
|
### Border Weights
|
||||||
|
- Thin (2-5px) → Thick (6-12px)
|
||||||
|
- Consistent brutalist aesthetic
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mobile/Responsive Changes
|
||||||
|
|
||||||
|
### Maintained
|
||||||
|
- Core responsive structure
|
||||||
|
- Flexbox collapse patterns
|
||||||
|
- Mobile menu functionality
|
||||||
|
|
||||||
|
### Enhanced
|
||||||
|
- Removed skew transforms on mobile (performance + clarity)
|
||||||
|
- Simplified border weights on small screens
|
||||||
|
- Better contrast with dark background priority
|
||||||
|
- Improved touch target sizes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Font Loading
|
||||||
|
- Google Fonts with `display=swap`
|
||||||
|
- Three typefaces vs one (acceptable for impact)
|
||||||
|
|
||||||
|
### Animation Performance
|
||||||
|
- CSS-only (no JavaScript)
|
||||||
|
- GPU-accelerated transforms (translateY, scale, skew)
|
||||||
|
- Cubic-bezier timing: `cubic-bezier(0.4, 0, 0.2, 1)`
|
||||||
|
|
||||||
|
### Code Size
|
||||||
|
- **Previous:** 28KB
|
||||||
|
- **New:** 31KB (+10% for significant visual enhancement)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Accessibility Maintained
|
||||||
|
|
||||||
|
### Contrast Ratios
|
||||||
|
- High contrast preserved
|
||||||
|
- Neon accent (#00FFA3) used carefully for CTAs only
|
||||||
|
- Dark backgrounds with light text meet WCAG AA
|
||||||
|
|
||||||
|
### Interactive States
|
||||||
|
- Clear focus states
|
||||||
|
- Hover states distinct from default
|
||||||
|
- Active states visually obvious
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Stayed the Same
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
- HTML structure unchanged
|
||||||
|
- WordPress theme compatibility maintained
|
||||||
|
- Navigation hierarchy preserved
|
||||||
|
- Content organization intact
|
||||||
|
|
||||||
|
### Functionality
|
||||||
|
- All links work identically
|
||||||
|
- Forms function the same
|
||||||
|
- Mobile menu behavior consistent
|
||||||
|
- Responsive breakpoints similar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
### Primary
|
||||||
|
- `style.css` - Complete redesign
|
||||||
|
|
||||||
|
### Backups
|
||||||
|
- `style.css.backup-20260201-154357` - Previous version saved
|
||||||
|
|
||||||
|
### New Documentation
|
||||||
|
- `azcomputerguru-design-vision.md` - Design philosophy
|
||||||
|
- `azcomputerguru-changelog.md` - This file
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Details
|
||||||
|
|
||||||
|
**Date:** 2026-02-01
|
||||||
|
**Time:** ~16:00
|
||||||
|
**Server:** 172.16.3.10
|
||||||
|
**Path:** `/home/azcomputerguru/public_html/testsite/wp-content/themes/arizonacomputerguru/`
|
||||||
|
**Live URL:** https://azcomputerguru.com/testsite
|
||||||
|
**Status:** Active
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Instructions
|
||||||
|
|
||||||
|
If needed, restore previous version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@172.16.3.10
|
||||||
|
cd /home/azcomputerguru/public_html/testsite/wp-content/themes/arizonacomputerguru/
|
||||||
|
cp style.css.backup-20260201-154357 style.css
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This redesign transforms the site from a **conservative corporate aesthetic** to a **bold, distinctive Desert Brutalism identity**. The changes prioritize:
|
||||||
|
|
||||||
|
1. **Memorability** - Geometric brutalism + unexpected neon accents
|
||||||
|
2. **Regional Identity** - Arizona desert color palette
|
||||||
|
3. **Tech Credibility** - Monospace accents + clean typography
|
||||||
|
4. **Visual Impact** - Dramatic scale, shadows, transforms
|
||||||
|
5. **Professional Edge** - Maintained structure, improved hierarchy
|
||||||
|
|
||||||
|
The result is a website that commands attention while maintaining complete functionality and accessibility.
|
||||||
229
azcomputerguru-design-vision.md
Normal file
229
azcomputerguru-design-vision.md
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# Arizona Computer Guru - Bold Redesign Vision
|
||||||
|
|
||||||
|
## DESIGN PHILOSOPHY: DESERT BRUTALISM MEETS SOUTHWEST FUTURISM
|
||||||
|
|
||||||
|
The redesign breaks away from generic corporate aesthetics by fusing brutalist design principles with Arizona's dramatic desert landscape. This creates a distinctive, memorable identity that commands attention while maintaining professional credibility.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CORE DESIGN ELEMENTS
|
||||||
|
|
||||||
|
### Typography System
|
||||||
|
|
||||||
|
**PRIMARY: Space Grotesk**
|
||||||
|
- Geometric, brutalist character
|
||||||
|
- Architectural precision
|
||||||
|
- Strong uppercase presence
|
||||||
|
- Negative letter-spacing for impact
|
||||||
|
- Used for: All headings, navigation, CTAs
|
||||||
|
|
||||||
|
**SECONDARY: IBM Plex Sans**
|
||||||
|
- Technical warmth (warmer than Inter/Roboto)
|
||||||
|
- Excellent readability
|
||||||
|
- Professional yet distinctive
|
||||||
|
- Used for: Body text, descriptions
|
||||||
|
|
||||||
|
**ACCENT: JetBrains Mono**
|
||||||
|
- Monospace personality
|
||||||
|
- Tech credibility signal
|
||||||
|
- Distinctive rhythm
|
||||||
|
- Used for: Tech elements, small text, code snippets
|
||||||
|
|
||||||
|
### Color Palette
|
||||||
|
|
||||||
|
**Sunset Copper (#D4771C)**
|
||||||
|
- Primary brand color
|
||||||
|
- Warmer, deeper than generic orange
|
||||||
|
- Evokes Arizona desert sunsets
|
||||||
|
- Usage: Primary accents, highlights, hover states
|
||||||
|
|
||||||
|
**Midnight Desert (#0A0F14)**
|
||||||
|
- Near-black with blue undertones
|
||||||
|
- Deep, mysterious night sky
|
||||||
|
- Usage: Dark backgrounds, text, headers
|
||||||
|
|
||||||
|
**Canyon Shadow (#2D1B14)**
|
||||||
|
- Deep brown with earth tones
|
||||||
|
- Geological depth
|
||||||
|
- Usage: Secondary dark elements
|
||||||
|
|
||||||
|
**Sandstone (#E8D5C4)**
|
||||||
|
- Warm neutral light tone
|
||||||
|
- Desert sediment texture
|
||||||
|
- Usage: Light text on dark backgrounds
|
||||||
|
|
||||||
|
**Neon Accent (#00FFA3)**
|
||||||
|
- Unexpected cyberpunk touch
|
||||||
|
- High-tech contrast signal
|
||||||
|
- Usage: CTAs, active states, special highlights
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VISUAL LANGUAGE
|
||||||
|
|
||||||
|
### Geometric Brutalism
|
||||||
|
- **Thick borders** (8-12px) on major elements
|
||||||
|
- **Skewed transforms** (skewY/skewX) mimicking geological strata
|
||||||
|
- **Chunky typography** with bold weights
|
||||||
|
- **Asymmetric layouts** for visual interest
|
||||||
|
- **High contrast** shadow and light
|
||||||
|
|
||||||
|
### Desert Aesthetics
|
||||||
|
- **Textured backgrounds** - Subtle radial gradients and line patterns
|
||||||
|
- **Sunset gradients** - Warm copper to deep brown
|
||||||
|
- **Geological angles** - 2-5 degree skews
|
||||||
|
- **Shadow depth** - Dramatic drop shadows (4-8px offsets)
|
||||||
|
- **Layered atmosphere** - Overlapping semi-transparent effects
|
||||||
|
|
||||||
|
### Tech Elements
|
||||||
|
- **Neon glow effects** - Cyan/green accents with glow shadows
|
||||||
|
- **Grid patterns** - Repeating line textures
|
||||||
|
- **Monospace touches** - Code-style elements
|
||||||
|
- **Geometric shapes** - Angular borders and dividers
|
||||||
|
- **Hover animations** - Transform + shadow combos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## KEY DESIGN FEATURES
|
||||||
|
|
||||||
|
### Navigation
|
||||||
|
- Bold uppercase Space Grotesk
|
||||||
|
- Skewed hover states with full background fill
|
||||||
|
- Neon CTA button (last menu item)
|
||||||
|
- Geometric dropdown with thick copper/neon borders
|
||||||
|
- Mobile: Full-screen dark overlay with neon accents
|
||||||
|
|
||||||
|
### Hero/Feature Area
|
||||||
|
- Desert gradient backgrounds
|
||||||
|
- Massive 4.5rem headings with shadow
|
||||||
|
- Textured overlays (subtle line patterns)
|
||||||
|
- Dramatic positioning and scale
|
||||||
|
|
||||||
|
### Content Cards (Columns Upper)
|
||||||
|
- Skewed -2deg transform
|
||||||
|
- Thick neon left border (8-12px)
|
||||||
|
- Gradient overlay effects
|
||||||
|
- Transform + scale on hover
|
||||||
|
- Neon glow shadow
|
||||||
|
|
||||||
|
### Button Boxes
|
||||||
|
- 12px thick borders
|
||||||
|
- Skewed containers
|
||||||
|
- Gradient background slide-in on hover
|
||||||
|
- Icon scale + rotate animation
|
||||||
|
- Border color change (copper to neon)
|
||||||
|
|
||||||
|
### Typography Hierarchy
|
||||||
|
- **H1:** 3.5-5rem, uppercase, geometric, heavy shadow
|
||||||
|
- **H2:** 2.4-3.5rem, uppercase, neon underlines
|
||||||
|
- **H3:** 1.6-2.2rem, left border accents
|
||||||
|
- **Body:** 1.2rem, light weight, excellent line height
|
||||||
|
|
||||||
|
### Interactive Elements
|
||||||
|
- **Links:** Underline slide animation (width 0 to 100%)
|
||||||
|
- **Buttons:** Background slide + neon glow
|
||||||
|
- **Cards:** Transform + shadow + border width change
|
||||||
|
- **Hover timing:** 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TECHNICAL IMPLEMENTATION
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Google Fonts with display=swap
|
||||||
|
- CSS-only animations (no JS dependencies)
|
||||||
|
- Efficient transforms (GPU-accelerated)
|
||||||
|
- Minimal animation complexity
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
- High contrast ratios maintained
|
||||||
|
- Readable font sizes (min 16px)
|
||||||
|
- Clear focus states
|
||||||
|
- Semantic HTML structure preserved
|
||||||
|
|
||||||
|
### Responsive Strategy
|
||||||
|
- Mobile: Remove skews, simplify transforms
|
||||||
|
- Mobile: Full-width cards, simplified borders
|
||||||
|
- Mobile: Dark background prioritized
|
||||||
|
- Tablet: Reduced border thickness, smaller cards
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WHAT MAKES THIS DISTINCTIVE
|
||||||
|
|
||||||
|
### AVOIDS:
|
||||||
|
- Inter/Roboto fonts
|
||||||
|
- Purple/blue gradients
|
||||||
|
- Generic rounded corners
|
||||||
|
- Subtle gray palettes
|
||||||
|
- Minimal flat design
|
||||||
|
- Cookie-cutter layouts
|
||||||
|
|
||||||
|
### EMBRACES:
|
||||||
|
- Geometric brutalism
|
||||||
|
- Southwest color palette
|
||||||
|
- Unexpected neon accents
|
||||||
|
- Angular/skewed elements
|
||||||
|
- Dramatic shadows
|
||||||
|
- Textured layers
|
||||||
|
- Monospace personality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DESIGN RATIONALE
|
||||||
|
|
||||||
|
**Why Space Grotesk?**
|
||||||
|
Geometric, architectural, brutalist character creates instant visual distinction. The negative letter-spacing adds density and impact.
|
||||||
|
|
||||||
|
**Why Neon Accent?**
|
||||||
|
The unexpected cyberpunk green (#00FFA3) creates memorable contrast against warm desert tones. It signals tech expertise without being generic.
|
||||||
|
|
||||||
|
**Why Skewed Elements?**
|
||||||
|
2-5 degree skews reference geological formations (strata, canyon walls) while adding dynamic brutalist energy. Creates movement without rotation.
|
||||||
|
|
||||||
|
**Why Thick Borders?**
|
||||||
|
8-12px borders are brutalist signatures. They create bold separation, architectural weight, and memorable chunky aesthetics.
|
||||||
|
|
||||||
|
**Why Desert Palette?**
|
||||||
|
Grounds the brand in Arizona geography while differentiating from generic blue/purple tech palettes. Warm, distinctive, regionally authentic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## USER EXPERIENCE IMPROVEMENTS
|
||||||
|
|
||||||
|
### Visual Hierarchy
|
||||||
|
- Clearer section separation with borders
|
||||||
|
- Stronger color contrast for CTAs
|
||||||
|
- More dramatic scale differences
|
||||||
|
- Better defined interactive states
|
||||||
|
|
||||||
|
### Engagement
|
||||||
|
- Satisfying hover animations
|
||||||
|
- Memorable visual language
|
||||||
|
- Distinctive personality
|
||||||
|
- Professional yet bold
|
||||||
|
|
||||||
|
### Brand Identity
|
||||||
|
- Regionally grounded (Arizona desert)
|
||||||
|
- Tech-forward (neon accents, geometric)
|
||||||
|
- Confident (brutalist boldness)
|
||||||
|
- Unforgettable (breaks conventions)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LIVE SITE
|
||||||
|
|
||||||
|
**URL:** https://azcomputerguru.com/testsite
|
||||||
|
**Deployed:** 2026-02-01
|
||||||
|
**Backup:** style.css.backup-20260201-154357
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DESIGN CREDITS
|
||||||
|
|
||||||
|
**Design System:** Desert Brutalism
|
||||||
|
**Typography:** Space Grotesk + IBM Plex Sans + JetBrains Mono
|
||||||
|
**Color Philosophy:** Arizona Sunset meets Cyberpunk
|
||||||
|
**Visual Language:** Geometric Brutalism with Southwest Soul
|
||||||
|
|
||||||
|
This design intentionally breaks from safe, generic patterns to create a memorable, distinctive identity that positions Arizona Computer Guru as bold, confident, and unforgettable.
|
||||||
1520
azcomputerguru-refined.css
Normal file
1520
azcomputerguru-refined.css
Normal file
File diff suppressed because it is too large
Load Diff
212
clients/glaztech/DEPLOYMENT-READY.md
Normal file
212
clients/glaztech/DEPLOYMENT-READY.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Glaztech PDF Fix - READY TO DEPLOY
|
||||||
|
|
||||||
|
**Status:** ✅ All scripts configured with Glaztech file server information
|
||||||
|
**File Server:** \\192.168.8.62\
|
||||||
|
**Created:** 2026-01-27
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Deployment
|
||||||
|
|
||||||
|
### Option 1: Deploy via GuruRMM (Recommended for Multiple Computers)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\ClaudeTools\clients\glaztech
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates: `GuruRMM-Glaztech-PDF-Fix.ps1`
|
||||||
|
|
||||||
|
**Upload to GuruRMM:**
|
||||||
|
- Client: Glaztech Industries
|
||||||
|
- Client ID: d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||||
|
- Site: SLC - Salt Lake City
|
||||||
|
- Task Type: PowerShell Script
|
||||||
|
- Run As: SYSTEM
|
||||||
|
- Timeout: 5 minutes
|
||||||
|
|
||||||
|
### Option 2: Test on Single Computer First
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Copy to target computer and run as Administrator:
|
||||||
|
.\Fix-PDFPreview-Glaztech-UPDATED.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Deploy to Multiple Computers via PowerShell Remoting
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$Computers = @("GLAZ-PC001", "GLAZ-PC002", "GLAZ-PC003")
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What's Configured
|
||||||
|
|
||||||
|
### File Server
|
||||||
|
- **IP:** 192.168.8.62
|
||||||
|
- **Automatically scanned paths:**
|
||||||
|
- \\192.168.8.62\alb_patterns
|
||||||
|
- \\192.168.8.62\boi_patterns
|
||||||
|
- \\192.168.8.62\brl_patterns
|
||||||
|
- \\192.168.8.62\den_patterns
|
||||||
|
- \\192.168.8.62\elp_patterns
|
||||||
|
- \\192.168.8.62\emails
|
||||||
|
- \\192.168.8.62\ftp_brl
|
||||||
|
- \\192.168.8.62\ftp_shp
|
||||||
|
- \\192.168.8.62\ftp_slc
|
||||||
|
- \\192.168.8.62\GeneReport
|
||||||
|
- \\192.168.8.62\Graphics
|
||||||
|
- \\192.168.8.62\gt_invoice
|
||||||
|
- \\192.168.8.62\Logistics
|
||||||
|
- \\192.168.8.62\phx_patterns
|
||||||
|
- \\192.168.8.62\reports
|
||||||
|
- \\192.168.8.62\shp_patterns
|
||||||
|
- \\192.168.8.62\slc_patterns
|
||||||
|
- \\192.168.8.62\sql_backup
|
||||||
|
- \\192.168.8.62\sql_jobs
|
||||||
|
- \\192.168.8.62\tuc_patterns
|
||||||
|
- \\192.168.8.62\vs_code
|
||||||
|
|
||||||
|
### Network Ranges
|
||||||
|
- glaztech.com domain
|
||||||
|
- 192.168.0.* through 192.168.9.* (all 10 sites)
|
||||||
|
- 192.168.8.62 (file server - explicitly added)
|
||||||
|
|
||||||
|
### Local Paths
|
||||||
|
- User Desktop
|
||||||
|
- User Downloads
|
||||||
|
- User Documents
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What the Script Does
|
||||||
|
|
||||||
|
1. ✅ **Unblocks PDFs** - Scans all configured paths and removes Zone.Identifier
|
||||||
|
2. ✅ **Trusts file server** - Adds 192.168.8.62 to Intranet security zone
|
||||||
|
3. ✅ **Trusts networks** - Adds all Glaztech IP ranges to Intranet zone
|
||||||
|
4. ✅ **Disables SmartScreen** - For Glaztech internal resources only
|
||||||
|
5. ✅ **Enables PDF preview** - Ensures preview handlers are active
|
||||||
|
6. ✅ **Creates log** - C:\Temp\Glaztech-PDF-Fix.log on each computer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Pilot Test
|
||||||
|
|
||||||
|
Before mass deployment, test on 2-3 computers:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Test computers (adjust names as needed)
|
||||||
|
$TestComputers = @("GLAZ-PC001", "GLAZ-PC002")
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $TestComputers
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verify on test computers:**
|
||||||
|
1. Open File Explorer
|
||||||
|
2. Navigate to: \\192.168.8.62\reports (or any folder with PDFs)
|
||||||
|
3. Select a PDF file
|
||||||
|
4. Enable Preview Pane: View → Preview Pane
|
||||||
|
5. **Expected:** PDF displays in preview pane
|
||||||
|
6. Check log: `C:\Temp\Glaztech-PDF-Fix.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## After Successful Pilot
|
||||||
|
|
||||||
|
### Deploy to All Computers
|
||||||
|
|
||||||
|
**Method A: GuruRMM (Best for large deployment)**
|
||||||
|
```powershell
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
# Upload generated script to GuruRMM
|
||||||
|
# Schedule/execute on all Glaztech computers
|
||||||
|
```
|
||||||
|
|
||||||
|
**Method B: PowerShell (Good for AD environments)**
|
||||||
|
```powershell
|
||||||
|
# Get all Glaztech computers from Active Directory
|
||||||
|
$AllComputers = Get-ADComputer -Filter {OperatingSystem -like "*Windows 10*" -or OperatingSystem -like "*Windows 11*"} -SearchBase "DC=glaztech,DC=com" | Select -ExpandProperty Name
|
||||||
|
|
||||||
|
# Deploy to all
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $AllComputers
|
||||||
|
```
|
||||||
|
|
||||||
|
**Method C: Site-by-Site (Controlled rollout)**
|
||||||
|
```powershell
|
||||||
|
# Site 1
|
||||||
|
$Site1 = Get-ADComputer -Filter * -SearchBase "OU=Site1,DC=glaztech,DC=com" | Select -ExpandProperty Name
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Site1
|
||||||
|
|
||||||
|
# Verify, then continue to Site 2, 3, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Commands
|
||||||
|
|
||||||
|
### Check if script ran successfully
|
||||||
|
```powershell
|
||||||
|
# View log on remote computer
|
||||||
|
Invoke-Command -ComputerName "GLAZ-PC001" -ScriptBlock {
|
||||||
|
Get-Content C:\Temp\Glaztech-PDF-Fix.log -Tail 20
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check if file server is trusted
|
||||||
|
```powershell
|
||||||
|
# On local or remote computer
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\192.168.8.62" -ErrorAction SilentlyContinue
|
||||||
|
# Should return: file = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test PDF preview manually
|
||||||
|
```powershell
|
||||||
|
# Open file server in Explorer
|
||||||
|
explorer "\\192.168.8.62\reports"
|
||||||
|
# Enable Preview Pane, select PDF, verify preview works
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Available
|
||||||
|
|
||||||
|
| File | Purpose | Status |
|
||||||
|
|------|---------|--------|
|
||||||
|
| `Fix-PDFPreview-Glaztech-UPDATED.ps1` | Main fix script (use this one) | ✅ Ready |
|
||||||
|
| `Deploy-PDFFix-BulkRemote.ps1` | Bulk deployment script | ✅ Ready |
|
||||||
|
| `GPO-Configuration-Guide.md` | Group Policy setup guide | ✅ Ready |
|
||||||
|
| `README.md` | Complete documentation | ✅ Ready |
|
||||||
|
| `QUICK-REFERENCE.md` | Command cheat sheet | ✅ Ready |
|
||||||
|
| `DEPLOYMENT-READY.md` | This file | ✅ Ready |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
**GuruRMM Access:**
|
||||||
|
- Client ID: d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||||
|
- Site: SLC - Salt Lake City
|
||||||
|
- Site ID: 290bd2ea-4af5-49c6-8863-c6d58c5a55de
|
||||||
|
- API Key: grmm_Qw64eawPBjnMdwN5UmDGWoPlqwvjM7lI
|
||||||
|
|
||||||
|
**Network Details:**
|
||||||
|
- Domain: glaztech.com
|
||||||
|
- File Server: \\192.168.8.62\
|
||||||
|
- Site Networks: 192.168.0-9.0/24
|
||||||
|
|
||||||
|
**Script Location:** D:\ClaudeTools\clients\glaztech\
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- [ ] Pilot test on 2-3 computers
|
||||||
|
- [ ] Verify PDF preview works on test computers
|
||||||
|
- [ ] Review logs for any errors
|
||||||
|
- [ ] Deploy to all affected computers
|
||||||
|
- [ ] (Optional) Configure GPO for permanent solution
|
||||||
|
- [ ] Document which computers were fixed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready to deploy! Start with the pilot test, then proceed to full deployment via GuruRMM or PowerShell remoting.**
|
||||||
207
clients/glaztech/Deploy-PDFFix-BulkRemote.ps1
Normal file
207
clients/glaztech/Deploy-PDFFix-BulkRemote.ps1
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#requires -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Deploy PDF preview fix to multiple Glaztech computers remotely
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Runs Fix-PDFPreview-Glaztech.ps1 on multiple remote computers via PowerShell remoting
|
||||||
|
or prepares for deployment via GuruRMM
|
||||||
|
|
||||||
|
.PARAMETER ComputerNames
|
||||||
|
Array of computer names to target
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
PSCredential for remote access (optional, uses current user if not provided)
|
||||||
|
|
||||||
|
.PARAMETER UseGuruRMM
|
||||||
|
Export script as GuruRMM task instead of running directly
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames "PC001","PC002","PC003"
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames (Get-Content computers.txt)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
Generates GuruRMM deployment package
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string[]]$ComputerNames = @(),
|
||||||
|
|
||||||
|
[PSCredential]$Credential,
|
||||||
|
|
||||||
|
[switch]$UseGuruRMM,
|
||||||
|
|
||||||
|
[string[]]$ServerNames = @("192.168.8.62"),
|
||||||
|
|
||||||
|
[string[]]$AdditionalPaths = @()
|
||||||
|
)
|
||||||
|
|
||||||
|
$ScriptPath = Join-Path $PSScriptRoot "Fix-PDFPreview-Glaztech.ps1"
|
||||||
|
|
||||||
|
if (-not (Test-Path $ScriptPath)) {
|
||||||
|
Write-Host "[ERROR] Fix-PDFPreview-Glaztech.ps1 not found in script directory" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($UseGuruRMM) {
|
||||||
|
Write-Host "[OK] Generating GuruRMM deployment package..." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$GuruRMMScript = @"
|
||||||
|
# Glaztech PDF Preview Fix - GuruRMM Deployment
|
||||||
|
# Auto-generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
`$ScriptContent = @'
|
||||||
|
$(Get-Content $ScriptPath -Raw)
|
||||||
|
'@
|
||||||
|
|
||||||
|
# Save script to temp location
|
||||||
|
`$TempScript = "`$env:TEMP\Fix-PDFPreview-Glaztech.ps1"
|
||||||
|
`$ScriptContent | Out-File -FilePath `$TempScript -Encoding UTF8 -Force
|
||||||
|
|
||||||
|
# Build parameters
|
||||||
|
`$Params = @{}
|
||||||
|
"@
|
||||||
|
|
||||||
|
if ($ServerNames.Count -gt 0) {
|
||||||
|
$ServerList = ($ServerNames | ForEach-Object { "`"$_`"" }) -join ","
|
||||||
|
$GuruRMMScript += @"
|
||||||
|
|
||||||
|
`$Params['ServerNames'] = @($ServerList)
|
||||||
|
"@
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($AdditionalPaths.Count -gt 0) {
|
||||||
|
$PathList = ($AdditionalPaths | ForEach-Object { "`"$_`"" }) -join ","
|
||||||
|
$GuruRMMScript += @"
|
||||||
|
|
||||||
|
`$Params['UnblockPaths'] = @($PathList)
|
||||||
|
"@
|
||||||
|
}
|
||||||
|
|
||||||
|
$GuruRMMScript += @"
|
||||||
|
|
||||||
|
|
||||||
|
# Execute script (includes automatic Explorer restart)
|
||||||
|
& `$TempScript @Params
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
Remove-Item `$TempScript -Force -ErrorAction SilentlyContinue
|
||||||
|
"@
|
||||||
|
|
||||||
|
$GuruRMMPath = Join-Path $PSScriptRoot "GuruRMM-Glaztech-PDF-Fix.ps1"
|
||||||
|
$GuruRMMScript | Out-File -FilePath $GuruRMMPath -Encoding UTF8 -Force
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] GuruRMM script generated: $GuruRMMPath" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "To deploy via GuruRMM:" -ForegroundColor Cyan
|
||||||
|
Write-Host "1. Log into GuruRMM dashboard"
|
||||||
|
Write-Host "2. Create new PowerShell task"
|
||||||
|
Write-Host "3. Copy contents of: $GuruRMMPath"
|
||||||
|
Write-Host "4. Target: Glaztech Industries (Client ID: d857708c-5713-4ee5-a314-679f86d2f9f9)"
|
||||||
|
Write-Host "5. Execute on affected computers"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "GuruRMM API Key: grmm_Qw64eawPBjnMdwN5UmDGWoPlqwvjM7lI" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ComputerNames.Count -eq 0) {
|
||||||
|
Write-Host "[ERROR] No computer names provided" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Usage examples:" -ForegroundColor Yellow
|
||||||
|
Write-Host " .\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames 'PC001','PC002','PC003'"
|
||||||
|
Write-Host " .\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames (Get-Content computers.txt)"
|
||||||
|
Write-Host " .\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[OK] Deploying PDF fix to $($ComputerNames.Count) computers..." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$Results = @()
|
||||||
|
$ScriptContent = Get-Content $ScriptPath -Raw
|
||||||
|
|
||||||
|
foreach ($Computer in $ComputerNames) {
|
||||||
|
Write-Host "[$Computer] Connecting..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Test connectivity
|
||||||
|
if (-not (Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
|
||||||
|
Write-Host "[$Computer] [ERROR] Cannot reach computer" -ForegroundColor Red
|
||||||
|
$Results += [PSCustomObject]@{
|
||||||
|
ComputerName = $Computer
|
||||||
|
Status = "Unreachable"
|
||||||
|
PDFsUnblocked = 0
|
||||||
|
ConfigChanges = 0
|
||||||
|
Error = "Cannot ping"
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build parameters
|
||||||
|
$RemoteParams = @{}
|
||||||
|
if ($ServerNames.Count -gt 0) { $RemoteParams['ServerNames'] = $ServerNames }
|
||||||
|
if ($AdditionalPaths.Count -gt 0) { $RemoteParams['UnblockPaths'] = $AdditionalPaths }
|
||||||
|
|
||||||
|
# Execute remotely
|
||||||
|
$InvokeParams = @{
|
||||||
|
ComputerName = $Computer
|
||||||
|
ScriptBlock = [ScriptBlock]::Create($ScriptContent)
|
||||||
|
ArgumentList = $RemoteParams
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Credential) {
|
||||||
|
$InvokeParams['Credential'] = $Credential
|
||||||
|
}
|
||||||
|
|
||||||
|
$Result = Invoke-Command @InvokeParams -ErrorAction Stop
|
||||||
|
|
||||||
|
Write-Host "[$Computer] [SUCCESS] PDFs: $($Result.PDFsUnblocked), Changes: $($Result.ConfigChanges)" -ForegroundColor Green
|
||||||
|
|
||||||
|
$Results += [PSCustomObject]@{
|
||||||
|
ComputerName = $Computer
|
||||||
|
Status = "Success"
|
||||||
|
PDFsUnblocked = $Result.PDFsUnblocked
|
||||||
|
ConfigChanges = $Result.ConfigChanges
|
||||||
|
Error = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: Explorer restart is now handled by the main script automatically
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Host "[$Computer] [ERROR] $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
$Results += [PSCustomObject]@{
|
||||||
|
ComputerName = $Computer
|
||||||
|
Status = "Failed"
|
||||||
|
PDFsUnblocked = 0
|
||||||
|
ConfigChanges = 0
|
||||||
|
Error = $_.Exception.Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
Write-Host "========================================"
|
||||||
|
Write-Host "DEPLOYMENT SUMMARY"
|
||||||
|
Write-Host "========================================"
|
||||||
|
$Results | Format-Table -AutoSize
|
||||||
|
|
||||||
|
$SuccessCount = ($Results | Where-Object { $_.Status -eq "Success" }).Count
|
||||||
|
$FailureCount = ($Results | Where-Object { $_.Status -ne "Success" }).Count
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Total Computers: $($Results.Count)"
|
||||||
|
Write-Host "Successful: $SuccessCount" -ForegroundColor Green
|
||||||
|
Write-Host "Failed: $FailureCount" -ForegroundColor $(if ($FailureCount -gt 0) { "Red" } else { "Green" })
|
||||||
|
|
||||||
|
# Export results
|
||||||
|
$ResultsPath = Join-Path $PSScriptRoot "deployment-results-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
|
||||||
|
$Results | Export-Csv -Path $ResultsPath -NoTypeInformation
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Results exported to: $ResultsPath"
|
||||||
347
clients/glaztech/Fix-PDFPreview-Glaztech-UPDATED.ps1
Normal file
347
clients/glaztech/Fix-PDFPreview-Glaztech-UPDATED.ps1
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
#requires -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Fix PDF preview issues in Windows Explorer for Glaztech Industries
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Resolves PDF preview failures caused by Windows security updates (KB5066791/KB5066835)
|
||||||
|
by unblocking PDF files and configuring trusted zones for Glaztech network resources.
|
||||||
|
|
||||||
|
.PARAMETER UnblockPaths
|
||||||
|
Array of paths where PDFs should be unblocked. Supports UNC paths and local paths.
|
||||||
|
Default: User Desktop, Downloads, Documents, and Glaztech file server paths
|
||||||
|
|
||||||
|
.PARAMETER ServerNames
|
||||||
|
Array of server hostnames/IPs to add to trusted Intranet zone
|
||||||
|
Default: 192.168.8.2 (Glaztech main file server)
|
||||||
|
|
||||||
|
.PARAMETER WhatIf
|
||||||
|
Shows what changes would be made without actually making them
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Fix-PDFPreview-Glaztech-UPDATED.ps1
|
||||||
|
Run with defaults, unblock PDFs and configure zones
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Company: Glaztech Industries
|
||||||
|
Domain: glaztech.com
|
||||||
|
Network: 192.168.0.0/24 through 192.168.9.0/24 (10 sites)
|
||||||
|
File Server: \\192.168.8.62\
|
||||||
|
Issue: Windows 10/11 security updates block PDF preview from network shares
|
||||||
|
|
||||||
|
Version: 1.1
|
||||||
|
Date: 2026-01-27
|
||||||
|
Updated: Added Glaztech file server paths
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param(
|
||||||
|
[string[]]$UnblockPaths = @(),
|
||||||
|
|
||||||
|
[string[]]$ServerNames = @(
|
||||||
|
"192.168.8.62" # Glaztech main file server
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
$Script:ChangesMade = 0
|
||||||
|
|
||||||
|
# Logging function
|
||||||
|
function Write-Log {
|
||||||
|
param([string]$Message, [string]$Level = "INFO")
|
||||||
|
|
||||||
|
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
|
$Color = switch ($Level) {
|
||||||
|
"ERROR" { "Red" }
|
||||||
|
"WARNING" { "Yellow" }
|
||||||
|
"SUCCESS" { "Green" }
|
||||||
|
default { "White" }
|
||||||
|
}
|
||||||
|
|
||||||
|
$LogMessage = "[$Timestamp] [$Level] $Message"
|
||||||
|
Write-Host $LogMessage -ForegroundColor $Color
|
||||||
|
|
||||||
|
# Log to file
|
||||||
|
$LogPath = "C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
if (-not (Test-Path "C:\Temp")) { New-Item -ItemType Directory -Path "C:\Temp" -Force | Out-Null }
|
||||||
|
Add-Content -Path $LogPath -Value $LogMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "Glaztech PDF Preview Fix Script v1.1"
|
||||||
|
Write-Log "Computer: $env:COMPUTERNAME"
|
||||||
|
Write-Log "User: $env:USERNAME"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Function to unblock files
|
||||||
|
function Remove-ZoneIdentifier {
|
||||||
|
param([string]$Path, [string]$Filter = "*.pdf")
|
||||||
|
|
||||||
|
if (-not (Test-Path $Path)) {
|
||||||
|
Write-Log "Path not accessible: $Path" "WARNING"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Scanning for PDFs in: $Path"
|
||||||
|
|
||||||
|
try {
|
||||||
|
$Files = Get-ChildItem -Path $Path -Filter $Filter -Recurse -File -ErrorAction SilentlyContinue
|
||||||
|
$UnblockedCount = 0
|
||||||
|
|
||||||
|
foreach ($File in $Files) {
|
||||||
|
try {
|
||||||
|
# Check if file has Zone.Identifier
|
||||||
|
$ZoneId = Get-Item -Path $File.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if ($ZoneId) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($File.FullName, "Unblock file")) {
|
||||||
|
Unblock-File -Path $File.FullName -ErrorAction Stop
|
||||||
|
$UnblockedCount++
|
||||||
|
Write-Log " Unblocked: $($File.FullName)" "SUCCESS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log " Failed to unblock: $($File.FullName) - $($_.Exception.Message)" "WARNING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($UnblockedCount -gt 0) {
|
||||||
|
Write-Log "Unblocked $UnblockedCount PDF files in $Path" "SUCCESS"
|
||||||
|
} else {
|
||||||
|
Write-Log "No blocked PDFs found in $Path"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $UnblockedCount
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Log "Error scanning path: $Path - $($_.Exception.Message)" "ERROR"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add sites to Intranet Zone
|
||||||
|
function Add-ToIntranetZone {
|
||||||
|
param([string]$Site)
|
||||||
|
|
||||||
|
$ZonePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Parse site for registry path creation
|
||||||
|
if ($Site -match "^(\d+\.){3}\d+$") {
|
||||||
|
# IP address - add to ESC Domains
|
||||||
|
$EscPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\$Site"
|
||||||
|
|
||||||
|
if (-not (Test-Path $EscPath)) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($Site, "Add IP to Intranet Zone")) {
|
||||||
|
New-Item -Path $EscPath -Force | Out-Null
|
||||||
|
Set-ItemProperty -Path $EscPath -Name "file" -Value 1 -Type DWord
|
||||||
|
Write-Log " Added IP to Intranet Zone: $Site" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log " IP already in Intranet Zone: $Site"
|
||||||
|
}
|
||||||
|
} elseif ($Site -match "^\\\\(.+)$") {
|
||||||
|
# UNC path - extract hostname
|
||||||
|
$Hostname = $Matches[1] -replace "\\.*", ""
|
||||||
|
Add-ToIntranetZone -Site $Hostname
|
||||||
|
} else {
|
||||||
|
# Hostname/domain
|
||||||
|
$Parts = $Site -split "\."
|
||||||
|
$BasePath = $ZonePath
|
||||||
|
|
||||||
|
# Build registry path (reverse domain order)
|
||||||
|
for ($i = $Parts.Count - 1; $i -ge 0; $i--) {
|
||||||
|
$BasePath = Join-Path $BasePath $Parts[$i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $BasePath)) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($Site, "Add domain to Intranet Zone")) {
|
||||||
|
New-Item -Path $BasePath -Force | Out-Null
|
||||||
|
Set-ItemProperty -Path $BasePath -Name "file" -Value 1 -Type DWord
|
||||||
|
Write-Log " Added domain to Intranet Zone: $Site" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log " Domain already in Intranet Zone: $Site"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log " Failed to add $Site to Intranet Zone: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to configure PDF preview handler
|
||||||
|
function Enable-PDFPreview {
|
||||||
|
$PreviewHandlerPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PreviewHandlers"
|
||||||
|
$PDFPreviewCLSID = "{DC6EFB56-9CFA-464D-8880-44885D7DC193}"
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($PSCmdlet.ShouldProcess("PDF Preview Handler", "Enable")) {
|
||||||
|
# Ensure preview handler is registered
|
||||||
|
$HandlerExists = Get-ItemProperty -Path $PreviewHandlerPath -Name $PDFPreviewCLSID -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $HandlerExists) {
|
||||||
|
Write-Log "PDF Preview Handler not found in registry" "WARNING"
|
||||||
|
} else {
|
||||||
|
Write-Log "PDF Preview Handler is registered"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable previews in Explorer
|
||||||
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "ShowPreviewHandlers" -Value 1 -Type DWord -ErrorAction Stop
|
||||||
|
Write-Log "Enabled preview handlers in Windows Explorer" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Failed to enable PDF preview: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# MAIN EXECUTION
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 1: Unblocking PDF Files"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Glaztech file server paths
|
||||||
|
$GlaztechPaths = @(
|
||||||
|
"\\192.168.8.62\alb_patterns",
|
||||||
|
"\\192.168.8.62\boi_patterns",
|
||||||
|
"\\192.168.8.62\brl_patterns",
|
||||||
|
"\\192.168.8.62\den_patterns",
|
||||||
|
"\\192.168.8.62\elp_patterns",
|
||||||
|
"\\192.168.8.62\emails",
|
||||||
|
"\\192.168.8.62\ftp_brl",
|
||||||
|
"\\192.168.8.62\ftp_shp",
|
||||||
|
"\\192.168.8.62\ftp_slc",
|
||||||
|
"\\192.168.8.62\GeneReport",
|
||||||
|
"\\192.168.8.62\Graphics",
|
||||||
|
"\\192.168.8.62\gt_invoice",
|
||||||
|
"\\192.168.8.62\Logistics",
|
||||||
|
"\\192.168.8.62\phx_patterns",
|
||||||
|
"\\192.168.8.62\reports",
|
||||||
|
"\\192.168.8.62\shp_patterns",
|
||||||
|
"\\192.168.8.62\slc_patterns",
|
||||||
|
"\\192.168.8.62\sql_backup",
|
||||||
|
"\\192.168.8.62\sql_jobs",
|
||||||
|
"\\192.168.8.62\tuc_patterns",
|
||||||
|
"\\192.168.8.62\vs_code"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Default local paths
|
||||||
|
$LocalPaths = @(
|
||||||
|
"$env:USERPROFILE\Desktop",
|
||||||
|
"$env:USERPROFILE\Downloads",
|
||||||
|
"$env:USERPROFILE\Documents"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine all paths
|
||||||
|
$AllPaths = $LocalPaths + $GlaztechPaths + $UnblockPaths | Select-Object -Unique
|
||||||
|
|
||||||
|
$TotalUnblocked = 0
|
||||||
|
foreach ($Path in $AllPaths) {
|
||||||
|
$TotalUnblocked += Remove-ZoneIdentifier -Path $Path
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Total PDFs unblocked: $TotalUnblocked" "SUCCESS"
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 2: Configuring Trusted Zones"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Add Glaztech domain
|
||||||
|
Write-Log "Adding Glaztech domain to Intranet Zone..."
|
||||||
|
Add-ToIntranetZone -Site "glaztech.com"
|
||||||
|
Add-ToIntranetZone -Site "*.glaztech.com"
|
||||||
|
|
||||||
|
# Add all 10 Glaztech site IP ranges (192.168.0.0/24 through 192.168.9.0/24)
|
||||||
|
Write-Log "Adding Glaztech site IP ranges to Intranet Zone..."
|
||||||
|
for ($i = 0; $i -le 9; $i++) {
|
||||||
|
$Network = "192.168.$i.*"
|
||||||
|
Add-ToIntranetZone -Site $Network
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add Glaztech file server specifically
|
||||||
|
Write-Log "Adding Glaztech file server to Intranet Zone..."
|
||||||
|
foreach ($Server in $ServerNames) {
|
||||||
|
Add-ToIntranetZone -Site $Server
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 3: Enabling PDF Preview"
|
||||||
|
Write-Log "========================================"
|
||||||
|
Enable-PDFPreview
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 4: Configuring Security Policies"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Disable SmartScreen for Intranet Zone
|
||||||
|
try {
|
||||||
|
if ($PSCmdlet.ShouldProcess("Intranet Zone", "Disable SmartScreen")) {
|
||||||
|
$IntranetZonePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1"
|
||||||
|
if (-not (Test-Path $IntranetZonePath)) {
|
||||||
|
New-Item -Path $IntranetZonePath -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Zone 1 = Local Intranet
|
||||||
|
# 2702 = Use SmartScreen Filter (0 = Disable, 1 = Enable)
|
||||||
|
Set-ItemProperty -Path $IntranetZonePath -Name "2702" -Value 0 -Type DWord -ErrorAction Stop
|
||||||
|
Write-Log "Disabled SmartScreen for Intranet Zone" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Failed to configure SmartScreen: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "SUMMARY"
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "PDFs Unblocked: $TotalUnblocked"
|
||||||
|
Write-Log "Configuration Changes: $Script:ChangesMade"
|
||||||
|
Write-Log "File Server: \\192.168.8.62\ (added to trusted zone)"
|
||||||
|
Write-Log ""
|
||||||
|
|
||||||
|
if ($Script:ChangesMade -gt 0 -or $TotalUnblocked -gt 0) {
|
||||||
|
Write-Log "Changes applied - restarting Windows Explorer..." "WARNING"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Stop Explorer
|
||||||
|
Stop-Process -Name explorer -Force -ErrorAction Stop
|
||||||
|
Write-Log "Windows Explorer stopped" "SUCCESS"
|
||||||
|
|
||||||
|
# Wait a moment for processes to clean up
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
# Explorer will auto-restart, but we can force it if needed
|
||||||
|
$ExplorerRunning = Get-Process -Name explorer -ErrorAction SilentlyContinue
|
||||||
|
if (-not $ExplorerRunning) {
|
||||||
|
Start-Process explorer.exe
|
||||||
|
Write-Log "Windows Explorer restarted" "SUCCESS"
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Could not restart Explorer automatically: $($_.Exception.Message)" "WARNING"
|
||||||
|
Write-Log "Please restart Explorer manually: Stop-Process -Name explorer -Force" "WARNING"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "COMPLETED SUCCESSFULLY" "SUCCESS"
|
||||||
|
} else {
|
||||||
|
Write-Log "No changes needed - system already configured" "SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Log file: C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Return summary object
|
||||||
|
[PSCustomObject]@{
|
||||||
|
ComputerName = $env:COMPUTERNAME
|
||||||
|
PDFsUnblocked = $TotalUnblocked
|
||||||
|
ConfigChanges = $Script:ChangesMade
|
||||||
|
FileServer = "\\192.168.8.62\"
|
||||||
|
Success = ($TotalUnblocked -gt 0 -or $Script:ChangesMade -gt 0)
|
||||||
|
LogPath = "C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
}
|
||||||
323
clients/glaztech/Fix-PDFPreview-Glaztech.ps1
Normal file
323
clients/glaztech/Fix-PDFPreview-Glaztech.ps1
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
#requires -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Fix PDF preview issues in Windows Explorer for Glaztech Industries
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Resolves PDF preview failures caused by Windows security updates (KB5066791/KB5066835)
|
||||||
|
by unblocking PDF files and configuring trusted zones for Glaztech network resources.
|
||||||
|
|
||||||
|
.PARAMETER UnblockPaths
|
||||||
|
Array of paths where PDFs should be unblocked. Supports UNC paths and local paths.
|
||||||
|
Default: User Desktop, Downloads, Documents, and common network paths
|
||||||
|
|
||||||
|
.PARAMETER ServerNames
|
||||||
|
Array of server hostnames/IPs to add to trusted Intranet zone
|
||||||
|
Add Glaztech file servers here when identified
|
||||||
|
|
||||||
|
.PARAMETER WhatIf
|
||||||
|
Shows what changes would be made without actually making them
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1
|
||||||
|
Run with defaults, unblock PDFs and configure zones
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -UnblockPaths "\\fileserver\shared","C:\Data" -ServerNames "fileserver01","192.168.1.10"
|
||||||
|
Specify custom paths and servers
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Company: Glaztech Industries
|
||||||
|
Domain: glaztech.com
|
||||||
|
Network: 192.168.0.0/24 through 192.168.9.0/24 (10 sites)
|
||||||
|
Issue: Windows 10/11 security updates block PDF preview from network shares
|
||||||
|
Deployment: GPO or remote PowerShell
|
||||||
|
|
||||||
|
Version: 1.0
|
||||||
|
Date: 2026-01-27
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param(
|
||||||
|
[string[]]$UnblockPaths = @(),
|
||||||
|
|
||||||
|
[string[]]$ServerNames = @(
|
||||||
|
# TODO: Add Glaztech file server names/IPs here when identified
|
||||||
|
# Example: "fileserver01", "192.168.1.50", "\\glaztech-fs01"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
$Script:ChangesMade = 0
|
||||||
|
|
||||||
|
# Logging function
|
||||||
|
function Write-Log {
|
||||||
|
param([string]$Message, [string]$Level = "INFO")
|
||||||
|
|
||||||
|
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
|
$Color = switch ($Level) {
|
||||||
|
"ERROR" { "Red" }
|
||||||
|
"WARNING" { "Yellow" }
|
||||||
|
"SUCCESS" { "Green" }
|
||||||
|
default { "White" }
|
||||||
|
}
|
||||||
|
|
||||||
|
$LogMessage = "[$Timestamp] [$Level] $Message"
|
||||||
|
Write-Host $LogMessage -ForegroundColor $Color
|
||||||
|
|
||||||
|
# Log to file
|
||||||
|
$LogPath = "C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
if (-not (Test-Path "C:\Temp")) { New-Item -ItemType Directory -Path "C:\Temp" -Force | Out-Null }
|
||||||
|
Add-Content -Path $LogPath -Value $LogMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "Glaztech PDF Preview Fix Script"
|
||||||
|
Write-Log "Computer: $env:COMPUTERNAME"
|
||||||
|
Write-Log "User: $env:USERNAME"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Function to unblock files
|
||||||
|
function Remove-ZoneIdentifier {
|
||||||
|
param([string]$Path, [string]$Filter = "*.pdf")
|
||||||
|
|
||||||
|
if (-not (Test-Path $Path)) {
|
||||||
|
Write-Log "Path not found: $Path" "WARNING"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Scanning for PDFs in: $Path"
|
||||||
|
|
||||||
|
try {
|
||||||
|
$Files = Get-ChildItem -Path $Path -Filter $Filter -Recurse -File -ErrorAction SilentlyContinue
|
||||||
|
$UnblockedCount = 0
|
||||||
|
|
||||||
|
foreach ($File in $Files) {
|
||||||
|
try {
|
||||||
|
# Check if file has Zone.Identifier
|
||||||
|
$ZoneId = Get-Item -Path $File.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if ($ZoneId) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($File.FullName, "Unblock file")) {
|
||||||
|
Unblock-File -Path $File.FullName -ErrorAction Stop
|
||||||
|
$UnblockedCount++
|
||||||
|
Write-Log " Unblocked: $($File.FullName)" "SUCCESS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log " Failed to unblock: $($File.FullName) - $($_.Exception.Message)" "WARNING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Unblocked $UnblockedCount PDF files in $Path"
|
||||||
|
return $UnblockedCount
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Log "Error scanning path: $Path - $($_.Exception.Message)" "ERROR"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add sites to Intranet Zone
|
||||||
|
function Add-ToIntranetZone {
|
||||||
|
param([string]$Site)
|
||||||
|
|
||||||
|
$ZonePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Parse site for registry path creation
|
||||||
|
if ($Site -match "^(\d+\.){3}\d+$") {
|
||||||
|
# IP address - add to ESC Domains
|
||||||
|
$EscPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\$Site"
|
||||||
|
|
||||||
|
if (-not (Test-Path $EscPath)) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($Site, "Add IP to Intranet Zone")) {
|
||||||
|
New-Item -Path $EscPath -Force | Out-Null
|
||||||
|
Set-ItemProperty -Path $EscPath -Name "*" -Value 1 -Type DWord
|
||||||
|
Write-Log " Added IP to Intranet Zone: $Site" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log " IP already in Intranet Zone: $Site"
|
||||||
|
}
|
||||||
|
} elseif ($Site -match "^\\\\(.+)$") {
|
||||||
|
# UNC path - extract hostname
|
||||||
|
$Hostname = $Matches[1] -replace "\\.*", ""
|
||||||
|
Add-ToIntranetZone -Site $Hostname
|
||||||
|
} else {
|
||||||
|
# Hostname/domain
|
||||||
|
$Parts = $Site -split "\."
|
||||||
|
$BasePath = $ZonePath
|
||||||
|
|
||||||
|
# Build registry path (reverse domain order)
|
||||||
|
for ($i = $Parts.Count - 1; $i -ge 0; $i--) {
|
||||||
|
$BasePath = Join-Path $BasePath $Parts[$i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $BasePath)) {
|
||||||
|
if ($PSCmdlet.ShouldProcess($Site, "Add domain to Intranet Zone")) {
|
||||||
|
New-Item -Path $BasePath -Force | Out-Null
|
||||||
|
Set-ItemProperty -Path $BasePath -Name "*" -Value 1 -Type DWord
|
||||||
|
Write-Log " Added domain to Intranet Zone: $Site" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log " Domain already in Intranet Zone: $Site"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log " Failed to add $Site to Intranet Zone: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to configure PDF preview handler
|
||||||
|
function Enable-PDFPreview {
|
||||||
|
$PreviewHandlerPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PreviewHandlers"
|
||||||
|
$PDFPreviewCLSID = "{DC6EFB56-9CFA-464D-8880-44885D7DC193}"
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($PSCmdlet.ShouldProcess("PDF Preview Handler", "Enable")) {
|
||||||
|
# Ensure preview handler is registered
|
||||||
|
$HandlerExists = Get-ItemProperty -Path $PreviewHandlerPath -Name $PDFPreviewCLSID -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $HandlerExists) {
|
||||||
|
Write-Log "PDF Preview Handler not found in registry" "WARNING"
|
||||||
|
} else {
|
||||||
|
Write-Log "PDF Preview Handler is registered"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable previews in Explorer
|
||||||
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "ShowPreviewHandlers" -Value 1 -Type DWord -ErrorAction Stop
|
||||||
|
Write-Log "Enabled preview handlers in Windows Explorer" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Failed to enable PDF preview: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# MAIN EXECUTION
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 1: Unblocking PDF Files"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Default paths to check
|
||||||
|
$DefaultPaths = @(
|
||||||
|
"$env:USERPROFILE\Desktop",
|
||||||
|
"$env:USERPROFILE\Downloads",
|
||||||
|
"$env:USERPROFILE\Documents"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine default and custom paths
|
||||||
|
$AllPaths = $DefaultPaths + $UnblockPaths | Select-Object -Unique
|
||||||
|
|
||||||
|
$TotalUnblocked = 0
|
||||||
|
foreach ($Path in $AllPaths) {
|
||||||
|
$TotalUnblocked += Remove-ZoneIdentifier -Path $Path
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Total PDFs unblocked: $TotalUnblocked" "SUCCESS"
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 2: Configuring Trusted Zones"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Add Glaztech domain
|
||||||
|
Write-Log "Adding Glaztech domain to Intranet Zone..."
|
||||||
|
Add-ToIntranetZone -Site "glaztech.com"
|
||||||
|
Add-ToIntranetZone -Site "*.glaztech.com"
|
||||||
|
|
||||||
|
# Add all 10 Glaztech site IP ranges (192.168.0.0/24 through 192.168.9.0/24)
|
||||||
|
Write-Log "Adding Glaztech site IP ranges to Intranet Zone..."
|
||||||
|
for ($i = 0; $i -le 9; $i++) {
|
||||||
|
$Network = "192.168.$i.*"
|
||||||
|
Add-ToIntranetZone -Site $Network
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add specific servers if provided
|
||||||
|
if ($ServerNames.Count -gt 0) {
|
||||||
|
Write-Log "Adding specified servers to Intranet Zone..."
|
||||||
|
foreach ($Server in $ServerNames) {
|
||||||
|
Add-ToIntranetZone -Site $Server
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log "No specific servers provided - add them with -ServerNames parameter" "WARNING"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 3: Enabling PDF Preview"
|
||||||
|
Write-Log "========================================"
|
||||||
|
Enable-PDFPreview
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "STEP 4: Configuring Security Policies"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Disable SmartScreen for Intranet Zone
|
||||||
|
try {
|
||||||
|
if ($PSCmdlet.ShouldProcess("Intranet Zone", "Disable SmartScreen")) {
|
||||||
|
$IntranetZonePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1"
|
||||||
|
if (-not (Test-Path $IntranetZonePath)) {
|
||||||
|
New-Item -Path $IntranetZonePath -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Zone 1 = Local Intranet
|
||||||
|
# 2702 = Use SmartScreen Filter (0 = Disable, 1 = Enable)
|
||||||
|
Set-ItemProperty -Path $IntranetZonePath -Name "2702" -Value 0 -Type DWord -ErrorAction Stop
|
||||||
|
Write-Log "Disabled SmartScreen for Intranet Zone" "SUCCESS"
|
||||||
|
$Script:ChangesMade++
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Failed to configure SmartScreen: $($_.Exception.Message)" "ERROR"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "SUMMARY"
|
||||||
|
Write-Log "========================================"
|
||||||
|
Write-Log "PDFs Unblocked: $TotalUnblocked"
|
||||||
|
Write-Log "Configuration Changes: $Script:ChangesMade"
|
||||||
|
Write-Log ""
|
||||||
|
|
||||||
|
if ($Script:ChangesMade -gt 0 -or $TotalUnblocked -gt 0) {
|
||||||
|
Write-Log "Changes applied - restarting Windows Explorer..." "WARNING"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Stop Explorer
|
||||||
|
Stop-Process -Name explorer -Force -ErrorAction Stop
|
||||||
|
Write-Log "Windows Explorer stopped" "SUCCESS"
|
||||||
|
|
||||||
|
# Wait a moment for processes to clean up
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
# Explorer will auto-restart, but we can force it if needed
|
||||||
|
$ExplorerRunning = Get-Process -Name explorer -ErrorAction SilentlyContinue
|
||||||
|
if (-not $ExplorerRunning) {
|
||||||
|
Start-Process explorer.exe
|
||||||
|
Write-Log "Windows Explorer restarted" "SUCCESS"
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Log "Could not restart Explorer automatically: $($_.Exception.Message)" "WARNING"
|
||||||
|
Write-Log "Please restart Explorer manually: Stop-Process -Name explorer -Force" "WARNING"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ""
|
||||||
|
Write-Log "COMPLETED SUCCESSFULLY" "SUCCESS"
|
||||||
|
} else {
|
||||||
|
Write-Log "No changes needed - system already configured" "SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Log file: C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
Write-Log "========================================"
|
||||||
|
|
||||||
|
# Return summary object
|
||||||
|
[PSCustomObject]@{
|
||||||
|
ComputerName = $env:COMPUTERNAME
|
||||||
|
PDFsUnblocked = $TotalUnblocked
|
||||||
|
ConfigChanges = $Script:ChangesMade
|
||||||
|
Success = ($TotalUnblocked -gt 0 -or $Script:ChangesMade -gt 0)
|
||||||
|
LogPath = "C:\Temp\Glaztech-PDF-Fix.log"
|
||||||
|
}
|
||||||
309
clients/glaztech/GPO-Configuration-Guide.md
Normal file
309
clients/glaztech/GPO-Configuration-Guide.md
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
# Glaztech PDF Preview Fix - Group Policy Configuration
|
||||||
|
|
||||||
|
**Issue:** Windows 10/11 security updates (KB5066791, KB5066835) block PDF previews from network shares
|
||||||
|
**Solution:** Configure Group Policy to trust Glaztech network resources
|
||||||
|
**Client:** Glaztech Industries
|
||||||
|
**Domain:** glaztech.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
**Option 1:** Run PowerShell script once on each computer (fastest for immediate fix)
|
||||||
|
**Option 2:** Configure GPO for permanent solution (recommended for long-term)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GPO Configuration (Permanent Solution)
|
||||||
|
|
||||||
|
### Policy 1: Add Sites to Local Intranet Zone
|
||||||
|
|
||||||
|
**Purpose:** Trust Glaztech internal network resources
|
||||||
|
|
||||||
|
1. **Open Group Policy Management Console**
|
||||||
|
- Run: `gpmc.msc`
|
||||||
|
- Navigate to: `Forest > Domains > glaztech.com > Group Policy Objects`
|
||||||
|
|
||||||
|
2. **Create New GPO**
|
||||||
|
- Right-click "Group Policy Objects" → New
|
||||||
|
- Name: `Glaztech - PDF Preview Fix`
|
||||||
|
- Description: `Fix PDF preview issues from network shares (KB5066791/KB5066835)`
|
||||||
|
|
||||||
|
3. **Edit GPO**
|
||||||
|
- Right-click GPO → Edit
|
||||||
|
|
||||||
|
4. **Configure Intranet Zone Sites**
|
||||||
|
- Navigate to: `User Configuration > Policies > Windows Settings > Internet Explorer Maintenance > Security`
|
||||||
|
- Double-click: **Security Zones and Content Ratings**
|
||||||
|
- Click: **Import the current security zones and privacy settings**
|
||||||
|
- Click: **Modify Settings**
|
||||||
|
|
||||||
|
5. **Add Sites to Local Intranet Zone**
|
||||||
|
- Click: **Local intranet** → **Sites** → **Advanced**
|
||||||
|
- Add these sites (one per line):
|
||||||
|
```
|
||||||
|
*.glaztech.com
|
||||||
|
https://*.glaztech.com
|
||||||
|
http://*.glaztech.com
|
||||||
|
file://*.glaztech.com
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Add IP Ranges** (if servers use IPs)
|
||||||
|
- For each Glaztech site (192.168.0.* through 192.168.9.*):
|
||||||
|
```
|
||||||
|
https://192.168.0.*
|
||||||
|
https://192.168.1.*
|
||||||
|
https://192.168.2.*
|
||||||
|
https://192.168.3.*
|
||||||
|
https://192.168.4.*
|
||||||
|
https://192.168.5.*
|
||||||
|
https://192.168.6.*
|
||||||
|
https://192.168.7.*
|
||||||
|
https://192.168.8.*
|
||||||
|
https://192.168.9.*
|
||||||
|
file://192.168.0.*
|
||||||
|
file://192.168.1.*
|
||||||
|
(etc. for all 10 sites)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Policy 2: Disable SmartScreen for Intranet Zone
|
||||||
|
|
||||||
|
**Purpose:** Prevent SmartScreen from blocking trusted internal resources
|
||||||
|
|
||||||
|
1. **Navigate to:** `User Configuration > Administrative Templates > Windows Components > File Explorer`
|
||||||
|
|
||||||
|
2. **Configure:**
|
||||||
|
- **Configure Windows Defender SmartScreen** → **Disabled** (for Intranet zone only)
|
||||||
|
|
||||||
|
3. **Alternative Registry-Based Setting:**
|
||||||
|
- Navigate to: `User Configuration > Preferences > Windows Settings > Registry`
|
||||||
|
- Create new Registry Item:
|
||||||
|
- Action: **Update**
|
||||||
|
- Hive: **HKEY_CURRENT_USER**
|
||||||
|
- Key Path: `Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1`
|
||||||
|
- Value Name: `2702`
|
||||||
|
- Value Type: **REG_DWORD**
|
||||||
|
- Value Data: `0` (Disable SmartScreen for Intranet)
|
||||||
|
|
||||||
|
### Policy 3: Enable PDF Preview Handlers
|
||||||
|
|
||||||
|
**Purpose:** Ensure PDF preview is enabled in Windows Explorer
|
||||||
|
|
||||||
|
1. **Navigate to:** `User Configuration > Preferences > Windows Settings > Registry`
|
||||||
|
|
||||||
|
2. **Create Registry Item:**
|
||||||
|
- Action: **Update**
|
||||||
|
- Hive: **HKEY_CURRENT_USER**
|
||||||
|
- Key Path: `Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced`
|
||||||
|
- Value Name: `ShowPreviewHandlers`
|
||||||
|
- Value Type: **REG_DWORD**
|
||||||
|
- Value Data: `1`
|
||||||
|
|
||||||
|
### Policy 4: Unblock Network Shares (Advanced)
|
||||||
|
|
||||||
|
**Purpose:** Automatically remove Zone.Identifier from files on network shares
|
||||||
|
|
||||||
|
**Option A: Startup Script (runs at computer startup)**
|
||||||
|
|
||||||
|
1. **Navigate to:** `Computer Configuration > Policies > Windows Settings > Scripts > Startup`
|
||||||
|
2. **Add Script:**
|
||||||
|
- Click: **Add** → **Browse**
|
||||||
|
- Copy `Fix-PDFPreview-Glaztech.ps1` to: `\\glaztech.com\SYSVOL\glaztech.com\scripts\`
|
||||||
|
- Script Name: `Fix-PDFPreview-Glaztech.ps1`
|
||||||
|
- Script Parameters: Leave blank (uses defaults)
|
||||||
|
|
||||||
|
**Option B: Logon Script (runs at user logon)**
|
||||||
|
|
||||||
|
1. **Navigate to:** `User Configuration > Policies > Windows Settings > Scripts > Logon`
|
||||||
|
2. **Add Script:** (same as above)
|
||||||
|
|
||||||
|
**Option C: Scheduled Task via GPO**
|
||||||
|
|
||||||
|
1. **Navigate to:** `Computer Configuration > Preferences > Control Panel Settings > Scheduled Tasks`
|
||||||
|
2. **Create new Scheduled Task:**
|
||||||
|
- Action: **Create**
|
||||||
|
- Name: `Glaztech PDF Preview Maintenance`
|
||||||
|
- Run as: **NT AUTHORITY\SYSTEM** or **%LogonDomain%\%LogonUser%**
|
||||||
|
- Trigger: **At log on** (or daily)
|
||||||
|
- Action: Start a program
|
||||||
|
- Program: `powershell.exe`
|
||||||
|
- Arguments: `-ExecutionPolicy Bypass -File "\\glaztech.com\SYSVOL\glaztech.com\scripts\Fix-PDFPreview-Glaztech.ps1"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Link GPO to OUs
|
||||||
|
|
||||||
|
1. **In Group Policy Management:**
|
||||||
|
- Right-click appropriate OU (e.g., "Computers" or "Workstations")
|
||||||
|
- Select: **Link an Existing GPO**
|
||||||
|
- Choose: `Glaztech - PDF Preview Fix`
|
||||||
|
|
||||||
|
2. **Verify Link:**
|
||||||
|
- Ensure GPO is enabled (checkmark in "Link Enabled" column)
|
||||||
|
- Set appropriate link order (higher = applied later)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing GPO
|
||||||
|
|
||||||
|
1. **Force GPO Update on Test Computer:**
|
||||||
|
```powershell
|
||||||
|
gpupdate /force
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Verify Applied Policies:**
|
||||||
|
```powershell
|
||||||
|
gpresult /H C:\Temp\gpresult.html
|
||||||
|
# Open C:\Temp\gpresult.html in browser to review applied policies
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check Registry Values:**
|
||||||
|
```powershell
|
||||||
|
# Check Intranet Zone configuration
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1"
|
||||||
|
|
||||||
|
# Check if preview handlers are enabled
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name ShowPreviewHandlers
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test PDF Preview:**
|
||||||
|
- Navigate to network share with PDFs
|
||||||
|
- Select a PDF file
|
||||||
|
- Check if preview appears in Preview Pane (View → Preview Pane)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### PDF Preview Still Not Working
|
||||||
|
|
||||||
|
1. **Check if GPO applied:**
|
||||||
|
```powershell
|
||||||
|
gpresult /r /scope:user
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Restart Windows Explorer:**
|
||||||
|
```powershell
|
||||||
|
Stop-Process -Name explorer -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check for blocked files manually:**
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem "\\server\share" -Filter "*.pdf" -Recurse |
|
||||||
|
ForEach-Object {
|
||||||
|
if (Get-Item $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue) {
|
||||||
|
Unblock-File $_.FullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GPO Not Applying
|
||||||
|
|
||||||
|
1. **Check GPO replication:**
|
||||||
|
```powershell
|
||||||
|
dcdiag /test:replications
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Verify SYSVOL replication:**
|
||||||
|
```powershell
|
||||||
|
Get-SmbShare SYSVOL
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check event logs:**
|
||||||
|
- Event Viewer → Windows Logs → Application
|
||||||
|
- Look for Group Policy errors
|
||||||
|
|
||||||
|
### SmartScreen Still Blocking
|
||||||
|
|
||||||
|
1. **Manually disable SmartScreen for Intranet (temporary):**
|
||||||
|
```powershell
|
||||||
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1" -Name "2702" -Value 0 -Type DWord
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check Windows Defender settings:**
|
||||||
|
- Settings → Update & Security → Windows Security → App & browser control
|
||||||
|
- Ensure SmartScreen isn't overriding zone settings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If issues occur after GPO deployment:
|
||||||
|
|
||||||
|
1. **Disable GPO:**
|
||||||
|
- GPMC → Right-click GPO → **Link Enabled** (uncheck)
|
||||||
|
|
||||||
|
2. **Delete GPO (if needed):**
|
||||||
|
- GPMC → Right-click GPO → **Delete**
|
||||||
|
|
||||||
|
3. **Force refresh on clients:**
|
||||||
|
```powershell
|
||||||
|
gpupdate /force
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alternative: PowerShell Deployment (No GPO)
|
||||||
|
|
||||||
|
If GPO deployment is not feasible:
|
||||||
|
|
||||||
|
1. **Deploy via GuruRMM:**
|
||||||
|
```powershell
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
# Upload generated script to GuruRMM dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Deploy via PowerShell Remoting:**
|
||||||
|
```powershell
|
||||||
|
$Computers = Get-ADComputer -Filter * -SearchBase "OU=Workstations,DC=glaztech,DC=com" | Select-Object -ExpandProperty Name
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Manual deployment:**
|
||||||
|
- Copy script to network share
|
||||||
|
- Email link to users
|
||||||
|
- Instruct users to right-click → "Run with PowerShell"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use Each Method
|
||||||
|
|
||||||
|
| Method | Use When | Pros | Cons |
|
||||||
|
|--------|----------|------|------|
|
||||||
|
| **GPO** | Large environment, permanent fix needed | Automatic, consistent, centrally managed | Requires AD infrastructure, slower rollout |
|
||||||
|
| **GuruRMM** | Quick deployment needed, mixed environment | Fast, flexible, good reporting | Requires GuruRMM access, manual execution |
|
||||||
|
| **PowerShell Remoting** | AD environment, immediate fix needed | Very fast, scriptable | Requires WinRM enabled, manual execution |
|
||||||
|
| **Manual** | Small number of computers, no remote access | Simple, no infrastructure needed | Time-consuming, inconsistent |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Server Names/IPs
|
||||||
|
|
||||||
|
**TODO:** Update this list when user provides Glaztech file server details
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Add servers to script parameters:
|
||||||
|
$ServerNames = @(
|
||||||
|
# "fileserver01",
|
||||||
|
# "192.168.1.50",
|
||||||
|
# "glaztech-nas01",
|
||||||
|
# Add more as identified...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Update script on SYSVOL or re-run deployment after adding servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Microsoft KB5066791](https://support.microsoft.com/kb/5066791) - Security update that changed file handling
|
||||||
|
- [Microsoft KB5066835](https://support.microsoft.com/kb/5066835) - Related security update
|
||||||
|
- [Mark of the Web (MOTW)](https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/mark-of-the-web) - Zone.Identifier explanation
|
||||||
|
- [Internet Explorer Security Zones](https://docs.microsoft.com/en-us/troubleshoot/browsers/how-to-add-sites-to-the-local-intranet-zone)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2026-01-27
|
||||||
|
**Contact:** AZ Computer Guru MSP
|
||||||
|
**Client:** Glaztech Industries (GuruRMM Client ID: d857708c-5713-4ee5-a314-679f86d2f9f9)
|
||||||
BIN
clients/glaztech/PDF-FIX.zip
Normal file
BIN
clients/glaztech/PDF-FIX.zip
Normal file
Binary file not shown.
185
clients/glaztech/QUICK-REFERENCE.md
Normal file
185
clients/glaztech/QUICK-REFERENCE.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# Glaztech PDF Fix - Quick Reference Card
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
### Run on Single Computer (Local)
|
||||||
|
```powershell
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy to Multiple Computers (Remote)
|
||||||
|
```powershell
|
||||||
|
# From list
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames "PC001","PC002","PC003"
|
||||||
|
|
||||||
|
# From file
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames (Get-Content computers.txt)
|
||||||
|
|
||||||
|
# All AD computers
|
||||||
|
$Computers = Get-ADComputer -Filter * | Select -ExpandProperty Name
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate GuruRMM Script
|
||||||
|
```powershell
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
# Output: GuruRMM-Glaztech-PDF-Fix.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add File Servers
|
||||||
|
```powershell
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -ServerNames "fileserver01","192.168.1.50"
|
||||||
|
|
||||||
|
# Bulk deployment with servers
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers -ServerNames "fileserver01","192.168.1.50"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Custom Paths
|
||||||
|
```powershell
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -UnblockPaths "\\fileserver\shared","C:\Data"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Commands
|
||||||
|
|
||||||
|
### Check Log
|
||||||
|
```powershell
|
||||||
|
Get-Content C:\Temp\Glaztech-PDF-Fix.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Zone Configuration
|
||||||
|
```powershell
|
||||||
|
# Check Intranet zone
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1"
|
||||||
|
|
||||||
|
# Check SmartScreen (should be 0 = disabled for Intranet)
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1" -Name "2702"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check if File is Blocked
|
||||||
|
```powershell
|
||||||
|
$File = "\\server\share\document.pdf"
|
||||||
|
Get-Item $File -Stream Zone.Identifier -ErrorAction SilentlyContinue
|
||||||
|
# No output = file is unblocked
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test PDF Preview
|
||||||
|
```powershell
|
||||||
|
# Open Explorer to network share
|
||||||
|
explorer "\\fileserver\documents"
|
||||||
|
# Enable Preview Pane: View → Preview Pane
|
||||||
|
# Select a PDF - should preview
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting Commands
|
||||||
|
|
||||||
|
### Restart Explorer
|
||||||
|
```powershell
|
||||||
|
Stop-Process -Name explorer -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manually Unblock Single File
|
||||||
|
```powershell
|
||||||
|
Unblock-File "\\server\share\file.pdf"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manually Unblock All PDFs in Folder
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem "\\server\share" -Filter "*.pdf" -Recurse | Unblock-File
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable PowerShell Remoting
|
||||||
|
```powershell
|
||||||
|
Enable-PSRemoting -Force
|
||||||
|
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Force GPO Update
|
||||||
|
```powershell
|
||||||
|
gpupdate /force
|
||||||
|
gpresult /H C:\Temp\gpresult.html
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GuruRMM Deployment
|
||||||
|
|
||||||
|
1. Generate script:
|
||||||
|
```powershell
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Upload to GuruRMM:
|
||||||
|
- Task Type: PowerShell
|
||||||
|
- Target: Glaztech Industries (d857708c-5713-4ee5-a314-679f86d2f9f9)
|
||||||
|
- Run As: SYSTEM
|
||||||
|
- Timeout: 5 minutes
|
||||||
|
|
||||||
|
3. Execute and monitor results
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GPO Deployment
|
||||||
|
|
||||||
|
See: `GPO-Configuration-Guide.md`
|
||||||
|
|
||||||
|
**Quick Steps:**
|
||||||
|
1. Create GPO: "Glaztech - PDF Preview Fix"
|
||||||
|
2. Add sites to Intranet Zone:
|
||||||
|
- `*.glaztech.com`
|
||||||
|
- `192.168.0.*` through `192.168.9.*`
|
||||||
|
3. Disable SmartScreen for Intranet (Zone 1, value 2702 = 0)
|
||||||
|
4. Link GPO to computer OUs
|
||||||
|
5. Force update: `gpupdate /force`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `Fix-PDFPreview-Glaztech.ps1` | Main script (run on individual computer) |
|
||||||
|
| `Deploy-PDFFix-BulkRemote.ps1` | Bulk deployment (run from admin workstation) |
|
||||||
|
| `GPO-Configuration-Guide.md` | Group Policy setup instructions |
|
||||||
|
| `README.md` | Complete documentation |
|
||||||
|
| `QUICK-REFERENCE.md` | This file (cheat sheet) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Default Behavior
|
||||||
|
|
||||||
|
Without parameters, the script:
|
||||||
|
- ✅ Scans Desktop, Downloads, Documents
|
||||||
|
- ✅ Unblocks all PDF files found
|
||||||
|
- ✅ Adds `glaztech.com` to Intranet zone
|
||||||
|
- ✅ Adds `192.168.0.*` - `192.168.9.*` to Intranet zone
|
||||||
|
- ✅ Disables SmartScreen for Intranet zone
|
||||||
|
- ✅ Enables PDF preview handlers
|
||||||
|
- ✅ Creates log: `C:\Temp\Glaztech-PDF-Fix.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
**GuruRMM Client ID:** d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||||
|
**Domain:** glaztech.com
|
||||||
|
**Networks:** 192.168.0-9.0/24
|
||||||
|
**Script Location:** `D:\ClaudeTools\clients\glaztech\`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status Checklist
|
||||||
|
|
||||||
|
- [x] Scripts created
|
||||||
|
- [x] GPO guide created
|
||||||
|
- [x] GuruRMM deployment option available
|
||||||
|
- [ ] File server names/IPs pending (waiting on user)
|
||||||
|
- [ ] Pilot testing (1-5 computers)
|
||||||
|
- [ ] Bulk deployment
|
||||||
|
- [ ] GPO configuration
|
||||||
|
- [ ] Verification complete
|
||||||
|
|
||||||
|
**Next:** Get file server details from Glaztech IT, then update script parameters.
|
||||||
451
clients/glaztech/README.md
Normal file
451
clients/glaztech/README.md
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
# Glaztech PDF Preview Fix
|
||||||
|
|
||||||
|
**Client:** Glaztech Industries
|
||||||
|
**Issue:** Windows 10/11 PDF preview failures after security updates
|
||||||
|
**Root Cause:** KB5066791 and KB5066835 security updates add Mark of the Web (MOTW) to files from network shares
|
||||||
|
**Impact:** Users cannot preview PDFs in Windows Explorer from network locations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Summary
|
||||||
|
|
||||||
|
Recent Windows security updates (KB5066791, KB5066835) changed how Windows handles files downloaded from network shares. These files now receive a "Zone.Identifier" alternate data stream (Mark of the Web) that blocks preview functionality as a security measure.
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- PDF files cannot be previewed in Windows Explorer Preview Pane
|
||||||
|
- Files may show "This file came from another computer and might be blocked"
|
||||||
|
- Right-click → Properties shows "Unblock" button
|
||||||
|
- Preview works after manually unblocking individual files
|
||||||
|
|
||||||
|
**Affected Systems:**
|
||||||
|
- Windows 10 (with KB5066791 or KB5066835)
|
||||||
|
- Windows 11 (with KB5066791 or KB5066835)
|
||||||
|
- Files accessed from network shares (UNC paths)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Solution Overview
|
||||||
|
|
||||||
|
This solution provides **three deployment methods**:
|
||||||
|
|
||||||
|
1. **PowerShell Script** - Immediate fix, run on individual or bulk computers
|
||||||
|
2. **Group Policy (GPO)** - Permanent solution, automatic deployment
|
||||||
|
3. **GuruRMM** - MSP deployment via RMM platform
|
||||||
|
|
||||||
|
All methods configure:
|
||||||
|
- ✅ Unblock existing PDF files (remove Zone.Identifier)
|
||||||
|
- ✅ Add Glaztech networks to trusted Intranet zone
|
||||||
|
- ✅ Disable SmartScreen for internal resources
|
||||||
|
- ✅ Enable PDF preview handlers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### For IT Administrators (Recommended)
|
||||||
|
|
||||||
|
**Option 1: Deploy via GuruRMM** (Fastest for multiple computers)
|
||||||
|
```powershell
|
||||||
|
cd D:\ClaudeTools\clients\glaztech
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
# Upload generated script to GuruRMM dashboard
|
||||||
|
# Target: Glaztech Industries (Client ID: d857708c-5713-4ee5-a314-679f86d2f9f9)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2: Configure Group Policy** (Best for permanent fix)
|
||||||
|
- See: `GPO-Configuration-Guide.md`
|
||||||
|
- Creates automatic fix for all current and future computers
|
||||||
|
|
||||||
|
**Option 3: PowerShell Remoting** (Good for AD environments)
|
||||||
|
```powershell
|
||||||
|
$Computers = @("PC001", "PC002", "PC003")
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
```
|
||||||
|
|
||||||
|
### For End Users (Individual Computer)
|
||||||
|
|
||||||
|
1. Download: `Fix-PDFPreview-Glaztech.ps1`
|
||||||
|
2. Right-click → **Run with PowerShell**
|
||||||
|
3. Restart Windows Explorer when prompted
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Included
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `Fix-PDFPreview-Glaztech.ps1` | Main fix script - runs on individual computer |
|
||||||
|
| `Deploy-PDFFix-BulkRemote.ps1` | Bulk deployment script - runs on multiple computers remotely |
|
||||||
|
| `GPO-Configuration-Guide.md` | Group Policy configuration instructions |
|
||||||
|
| `README.md` | This file - overview and usage instructions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed Usage
|
||||||
|
|
||||||
|
### Script 1: Fix-PDFPreview-Glaztech.ps1
|
||||||
|
|
||||||
|
**Purpose:** Fixes PDF preview on a single computer
|
||||||
|
|
||||||
|
**Basic Usage:**
|
||||||
|
```powershell
|
||||||
|
# Run with defaults (scans user folders, configures Glaztech network)
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advanced Usage:**
|
||||||
|
```powershell
|
||||||
|
# Specify additional file server paths
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -UnblockPaths "\\fileserver01\shared", "\\192.168.1.50\documents"
|
||||||
|
|
||||||
|
# Add specific file servers to trusted zone
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -ServerNames "fileserver01", "192.168.1.50", "glaztech-nas"
|
||||||
|
|
||||||
|
# Test mode (see what would change without making changes)
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -WhatIf
|
||||||
|
```
|
||||||
|
|
||||||
|
**What It Does:**
|
||||||
|
1. Scans Desktop, Downloads, Documents for PDFs
|
||||||
|
2. Removes Zone.Identifier stream from all PDFs found
|
||||||
|
3. Adds `glaztech.com` and `*.glaztech.com` to Intranet zone
|
||||||
|
4. Adds IP ranges `192.168.0.*` through `192.168.9.*` to Intranet zone
|
||||||
|
5. Adds specified servers (if provided) to Intranet zone
|
||||||
|
6. Enables PDF preview handlers in Windows Explorer
|
||||||
|
7. Disables SmartScreen for Intranet zone
|
||||||
|
8. Creates log file at `C:\Temp\Glaztech-PDF-Fix.log`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Windows 10 or Windows 11
|
||||||
|
- PowerShell 5.1 or higher
|
||||||
|
- Administrator privileges
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Script 2: Deploy-PDFFix-BulkRemote.ps1
|
||||||
|
|
||||||
|
**Purpose:** Deploy fix to multiple computers remotely
|
||||||
|
|
||||||
|
**Method A: PowerShell Remoting**
|
||||||
|
```powershell
|
||||||
|
# Deploy to specific computers
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames "PC001","PC002","PC003"
|
||||||
|
|
||||||
|
# Deploy to computers from file
|
||||||
|
$Computers = Get-Content "computers.txt"
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
|
||||||
|
# Deploy to all computers in AD OU
|
||||||
|
$Computers = Get-ADComputer -Filter * -SearchBase "OU=Workstations,DC=glaztech,DC=com" | Select -ExpandProperty Name
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers
|
||||||
|
|
||||||
|
# With specific servers and paths
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Computers -ServerNames "fileserver01","192.168.1.50" -AdditionalPaths "\\fileserver01\shared"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Method B: GuruRMM Deployment**
|
||||||
|
```powershell
|
||||||
|
# Generate GuruRMM script
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
|
||||||
|
# Output: GuruRMM-Glaztech-PDF-Fix.ps1
|
||||||
|
# Upload to GuruRMM dashboard as PowerShell task
|
||||||
|
# Target: Glaztech Industries (Site: SLC - Salt Lake City)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- PowerShell remoting enabled on target computers
|
||||||
|
- Administrator credentials (or current user must be admin on targets)
|
||||||
|
- Network connectivity to target computers
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Console output showing progress
|
||||||
|
- CSV file: `deployment-results-YYYYMMDD-HHMMSS.csv`
|
||||||
|
- Individual log files on each computer: `C:\Temp\Glaztech-PDF-Fix.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Details
|
||||||
|
|
||||||
|
### Networks Automatically Trusted
|
||||||
|
|
||||||
|
The script automatically adds these to the Intranet security zone:
|
||||||
|
|
||||||
|
**Domains:**
|
||||||
|
- `glaztech.com`
|
||||||
|
- `*.glaztech.com`
|
||||||
|
|
||||||
|
**IP Ranges (All 10 Glaztech Sites):**
|
||||||
|
- `192.168.0.*` (Site 1)
|
||||||
|
- `192.168.1.*` (Site 2)
|
||||||
|
- `192.168.2.*` (Site 3)
|
||||||
|
- `192.168.3.*` (Site 4)
|
||||||
|
- `192.168.4.*` (Site 5)
|
||||||
|
- `192.168.5.*` (Site 6)
|
||||||
|
- `192.168.6.*` (Site 7)
|
||||||
|
- `192.168.7.*` (Site 8)
|
||||||
|
- `192.168.8.*` (Site 9)
|
||||||
|
- `192.168.9.*` (Site 10)
|
||||||
|
|
||||||
|
### Additional Servers (To Be Added)
|
||||||
|
|
||||||
|
**TODO:** Update script parameters when file server details are available:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Example - add these parameters when deploying:
|
||||||
|
$ServerNames = @(
|
||||||
|
"fileserver01",
|
||||||
|
"192.168.1.50",
|
||||||
|
"glaztech-nas01",
|
||||||
|
"glaztech-sharepoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -ServerNames $ServerNames
|
||||||
|
```
|
||||||
|
|
||||||
|
**Waiting on user to provide:**
|
||||||
|
- File server hostnames
|
||||||
|
- File server IP addresses
|
||||||
|
- SharePoint URLs (if applicable)
|
||||||
|
- NAS device names (if applicable)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Strategy
|
||||||
|
|
||||||
|
### Phase 1: Pilot Testing (1-5 Computers)
|
||||||
|
|
||||||
|
1. **Select test computers** representing different sites/configurations
|
||||||
|
2. **Run script manually** on test computers:
|
||||||
|
```powershell
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 -WhatIf # Preview changes
|
||||||
|
.\Fix-PDFPreview-Glaztech.ps1 # Apply changes
|
||||||
|
```
|
||||||
|
3. **Verify PDF preview works** on network shares
|
||||||
|
4. **Check for side effects** (ensure other functionality not affected)
|
||||||
|
5. **Review logs:** `C:\Temp\Glaztech-PDF-Fix.log`
|
||||||
|
|
||||||
|
### Phase 2: Bulk Deployment (All Computers)
|
||||||
|
|
||||||
|
**Option A: GuruRMM (Recommended)**
|
||||||
|
```powershell
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -UseGuruRMM
|
||||||
|
# Upload to GuruRMM
|
||||||
|
# Schedule during maintenance window
|
||||||
|
# Execute on all Glaztech computers
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: PowerShell Remoting**
|
||||||
|
```powershell
|
||||||
|
# Get all computers from Active Directory
|
||||||
|
$AllComputers = Get-ADComputer -Filter {OperatingSystem -like "*Windows 10*" -or OperatingSystem -like "*Windows 11*"} -SearchBase "DC=glaztech,DC=com" | Select -ExpandProperty Name
|
||||||
|
|
||||||
|
# Deploy to all
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $AllComputers
|
||||||
|
|
||||||
|
# Or deploy by site
|
||||||
|
$Site1Computers = Get-ADComputer -Filter * -SearchBase "OU=Site1,OU=Computers,DC=glaztech,DC=com" | Select -ExpandProperty Name
|
||||||
|
.\Deploy-PDFFix-BulkRemote.ps1 -ComputerNames $Site1Computers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Group Policy (Long-Term Solution)
|
||||||
|
|
||||||
|
1. **Follow:** `GPO-Configuration-Guide.md`
|
||||||
|
2. **Create GPO:** "Glaztech - PDF Preview Fix"
|
||||||
|
3. **Link to OUs:** All computer OUs
|
||||||
|
4. **Test on pilot group first**
|
||||||
|
5. **Roll out to all OUs**
|
||||||
|
|
||||||
|
**Benefits of GPO:**
|
||||||
|
- Automatic deployment to new computers
|
||||||
|
- Consistent configuration across all systems
|
||||||
|
- Centrally managed and auditable
|
||||||
|
- Persists across Windows updates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After deployment, verify the fix on affected computers:
|
||||||
|
|
||||||
|
1. **Check log file:**
|
||||||
|
```powershell
|
||||||
|
Get-Content C:\Temp\Glaztech-PDF-Fix.log
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Test PDF preview:**
|
||||||
|
- Open File Explorer
|
||||||
|
- Navigate to network share with PDFs (e.g., `\\fileserver\documents`)
|
||||||
|
- Select a PDF file
|
||||||
|
- Enable Preview Pane (View → Preview Pane)
|
||||||
|
- PDF should display in preview
|
||||||
|
|
||||||
|
3. **Verify zone configuration:**
|
||||||
|
```powershell
|
||||||
|
# Check if glaztech.com is in Intranet zone
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\com\glaztech"
|
||||||
|
|
||||||
|
# Check SmartScreen disabled for Intranet
|
||||||
|
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1" -Name "2702"
|
||||||
|
# Should return 0 (disabled)
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Check for Zone.Identifier on PDFs:**
|
||||||
|
```powershell
|
||||||
|
# Pick a PDF file
|
||||||
|
$PDFFile = "C:\Users\username\Desktop\test.pdf"
|
||||||
|
|
||||||
|
# Check for Zone.Identifier
|
||||||
|
Get-Item $PDFFile -Stream Zone.Identifier -ErrorAction SilentlyContinue
|
||||||
|
# Should return nothing (file is unblocked)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Problem: Script execution blocked
|
||||||
|
|
||||||
|
**Error:** "Running scripts is disabled on this system"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```powershell
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: PDF preview still not working
|
||||||
|
|
||||||
|
**Possible Causes:**
|
||||||
|
1. Windows Explorer needs restart
|
||||||
|
```powershell
|
||||||
|
Stop-Process -Name explorer -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
2. File server not in trusted zone
|
||||||
|
- Add server explicitly: `.\Fix-PDFPreview-Glaztech.ps1 -ServerNames "servername"`
|
||||||
|
|
||||||
|
3. PDF files still blocked
|
||||||
|
- Run script again to unblock new files
|
||||||
|
- Or manually unblock: `Unblock-File "\\server\share\file.pdf"`
|
||||||
|
|
||||||
|
4. PDF preview handler disabled
|
||||||
|
- Settings → Apps → Default apps → Choose default apps by file type
|
||||||
|
- Set `.pdf` to Adobe Acrobat or Microsoft Edge
|
||||||
|
|
||||||
|
### Problem: PowerShell remoting fails
|
||||||
|
|
||||||
|
**Error:** "WinRM cannot process the request"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```powershell
|
||||||
|
# On target computer (or via GPO):
|
||||||
|
Enable-PSRemoting -Force
|
||||||
|
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: GuruRMM deployment fails
|
||||||
|
|
||||||
|
**Possible Causes:**
|
||||||
|
1. Script blocked by execution policy
|
||||||
|
- Ensure GuruRMM task uses: `-ExecutionPolicy Bypass`
|
||||||
|
|
||||||
|
2. Insufficient permissions
|
||||||
|
- GuruRMM should run as SYSTEM or local administrator
|
||||||
|
|
||||||
|
3. Network timeout
|
||||||
|
- Increase GuruRMM task timeout setting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
If issues occur after applying the fix:
|
||||||
|
|
||||||
|
1. **Remove Intranet zone sites manually:**
|
||||||
|
```powershell
|
||||||
|
Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\com\glaztech" -Recurse -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Re-enable SmartScreen for Intranet:**
|
||||||
|
```powershell
|
||||||
|
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1" -Name "2702" -Value 1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Remove GPO (if deployed):**
|
||||||
|
- GPMC → Unlink or delete "Glaztech - PDF Preview Fix" GPO
|
||||||
|
- Force update: `gpupdate /force`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
**What This Script Does:**
|
||||||
|
- ✅ Adds Glaztech internal networks to trusted zone (safe for internal resources)
|
||||||
|
- ✅ Disables SmartScreen for internal sites only (not Internet sites)
|
||||||
|
- ✅ Removes Zone.Identifier from files on trusted shares
|
||||||
|
- ✅ Does NOT disable Windows Defender or other security features
|
||||||
|
- ✅ Does NOT affect Internet security settings
|
||||||
|
|
||||||
|
**What Remains Protected:**
|
||||||
|
- Internet downloads still blocked by SmartScreen
|
||||||
|
- External sites not affected
|
||||||
|
- Windows Defender continues scanning files
|
||||||
|
- UAC prompts remain active
|
||||||
|
- Firewall rules unchanged
|
||||||
|
|
||||||
|
**Best Practices:**
|
||||||
|
- Only add trusted internal servers to Intranet zone
|
||||||
|
- Do NOT add external/Internet sites
|
||||||
|
- Review server list before deployment
|
||||||
|
- Monitor for unusual network activity
|
||||||
|
- Keep Windows Defender and antivirus enabled
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support Information
|
||||||
|
|
||||||
|
**Client:** Glaztech Industries
|
||||||
|
**MSP:** AZ Computer Guru
|
||||||
|
**GuruRMM Client ID:** d857708c-5713-4ee5-a314-679f86d2f9f9
|
||||||
|
**GuruRMM Site:** SLC - Salt Lake City (Site ID: 290bd2ea-4af5-49c6-8863-c6d58c5a55de)
|
||||||
|
**GuruRMM API Key:** grmm_Qw64eawPBjnMdwN5UmDGWoPlqwvjM7lI
|
||||||
|
|
||||||
|
**Domain:** glaztech.com
|
||||||
|
**Network Ranges:** 192.168.0.0/24 through 192.168.9.0/24 (10 sites)
|
||||||
|
|
||||||
|
**Script Location:** `D:\ClaudeTools\clients\glaztech\`
|
||||||
|
**Created:** 2026-01-27
|
||||||
|
|
||||||
|
**Contact:**
|
||||||
|
- For urgent issues: Check GuruRMM ticket system
|
||||||
|
- For questions: AZ Computer Guru support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. ✅ **Pilot test** - Deploy to 1-5 test computers
|
||||||
|
2. ⏳ **Get server details** - Request file server names/IPs from local IT
|
||||||
|
3. ⏳ **Update script** - Add servers to script parameters
|
||||||
|
4. ⏳ **Bulk deploy** - Use GuruRMM or PowerShell remoting
|
||||||
|
5. ⏳ **Configure GPO** - Set up permanent solution
|
||||||
|
6. ⏳ **Document** - Record which computers are fixed
|
||||||
|
|
||||||
|
**Waiting on:**
|
||||||
|
- File server hostnames/IPs from Glaztech IT
|
||||||
|
- SharePoint URLs (if applicable)
|
||||||
|
- NAS device names (if applicable)
|
||||||
|
- Specific folder paths where PDFs are commonly accessed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [KB5066791 - Windows Security Update](https://support.microsoft.com/kb/5066791)
|
||||||
|
- [KB5066835 - Windows Security Update](https://support.microsoft.com/kb/5066835)
|
||||||
|
- [Mark of the Web (MOTW) - Microsoft Docs](https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/mark-of-the-web)
|
||||||
|
- [Security Zones - Microsoft Docs](https://docs.microsoft.com/en-us/troubleshoot/browsers/how-to-add-sites-to-the-local-intranet-zone)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2026-01-27
|
||||||
14
clients/glaztech/computers-example.txt
Normal file
14
clients/glaztech/computers-example.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Glaztech Computers - Example List
|
||||||
|
# Add one computer name per line
|
||||||
|
# Lines starting with # are ignored
|
||||||
|
|
||||||
|
# Site 1 - Example computers
|
||||||
|
GLAZ-PC001
|
||||||
|
GLAZ-PC002
|
||||||
|
GLAZ-PC003
|
||||||
|
|
||||||
|
# Site 2 - Example computers
|
||||||
|
GLAZ-PC101
|
||||||
|
GLAZ-PC102
|
||||||
|
|
||||||
|
# Add more computers below...
|
||||||
14
dataforth-notifications-creds.txt
Normal file
14
dataforth-notifications-creds.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Dataforth Notifications Account Credentials
|
||||||
|
Generated: 2026-01-27 10:57:03
|
||||||
|
|
||||||
|
Username: notifications@dataforth.com
|
||||||
|
Password: %5cfI:G71)}=g4ZS
|
||||||
|
|
||||||
|
SMTP Configuration for Website:
|
||||||
|
- Server: smtp.office365.com
|
||||||
|
- Port: 587
|
||||||
|
- TLS: Yes
|
||||||
|
- Username: notifications@dataforth.com
|
||||||
|
- Password: %5cfI:G71)}=g4ZS
|
||||||
|
|
||||||
|
DO NOT COMMIT TO GIT OR SHARE PUBLICLY
|
||||||
399
projects/msp-pricing/GPS_Price_Sheet_12.html
Normal file
399
projects/msp-pricing/GPS_Price_Sheet_12.html
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>GPS Pricing - Arizona Computer Guru</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { font-family: 'Segoe UI', Tahoma, sans-serif; line-height: 1.5; color: #333; }
|
||||||
|
|
||||||
|
.page {
|
||||||
|
width: 8.5in;
|
||||||
|
height: 11in;
|
||||||
|
padding: 0.5in;
|
||||||
|
padding-bottom: 0.8in;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen {
|
||||||
|
body { background: #f5f5f5; }
|
||||||
|
.page { margin: 20px auto; box-shadow: 0 0 20px rgba(0,0,0,0.1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
@page { size: letter; margin: 0; }
|
||||||
|
body { margin: 0; padding: 0; }
|
||||||
|
.page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5in;
|
||||||
|
padding-bottom: 0.8in;
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
.page:last-child { page-break-after: auto; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 3px solid #1e3c72; margin-bottom: 12px; }
|
||||||
|
.logo { font-size: 20px; font-weight: bold; color: #1e3c72; }
|
||||||
|
.contact { text-align: right; font-size: 10px; color: #666; }
|
||||||
|
.contact .phone { font-size: 14px; font-weight: bold; color: #f39c12; }
|
||||||
|
|
||||||
|
h1 { color: #1e3c72; font-size: 24px; margin-bottom: 6px; }
|
||||||
|
h2 { color: #1e3c72; font-size: 16px; margin: 12px 0 8px 0; padding-bottom: 5px; border-bottom: 2px solid #f39c12; }
|
||||||
|
.subtitle { font-size: 13px; color: #666; font-style: italic; margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.hero-box { background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: white; padding: 15px; border-radius: 8px; margin: 12px 0; }
|
||||||
|
.hero-box h2 { color: white; border-bottom: 2px solid #f39c12; margin-top: 0; font-size: 15px; }
|
||||||
|
.value-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 10px; }
|
||||||
|
.value-card { background: rgba(255,255,255,0.15); padding: 10px; border-radius: 6px; border-left: 3px solid #f39c12; }
|
||||||
|
.value-card h3 { margin: 0 0 6px 0; font-size: 12px; }
|
||||||
|
.value-card ul { list-style: none; padding: 0; }
|
||||||
|
.value-card li { padding: 2px 0; padding-left: 14px; position: relative; font-size: 10px; }
|
||||||
|
.value-card li:before { content: "✓"; position: absolute; left: 0; color: #27ae60; font-weight: bold; font-size: 10px; }
|
||||||
|
|
||||||
|
.tier-box { background: white; border: 2px solid #e0e0e0; border-radius: 8px; padding: 10px; margin: 8px 0; position: relative; }
|
||||||
|
.tier-box.popular { border-color: #f39c12; border-width: 2px; }
|
||||||
|
.tier-box .badge { position: absolute; top: -9px; right: 12px; background: #f39c12; color: white; padding: 2px 8px; border-radius: 10px; font-weight: bold; font-size: 9px; }
|
||||||
|
.tier-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
|
||||||
|
.tier-name { font-size: 13px; font-weight: bold; color: #1e3c72; }
|
||||||
|
.tier-price { font-size: 18px; font-weight: bold; color: #27ae60; }
|
||||||
|
.tier-price .period { font-size: 10px; color: #666; }
|
||||||
|
.features-list { list-style: none; padding: 0; margin: 4px 0; }
|
||||||
|
.features-list li { padding: 2px 0; padding-left: 16px; position: relative; font-size: 10px; }
|
||||||
|
.features-list li:before { content: "✓"; position: absolute; left: 0; color: #27ae60; font-weight: bold; font-size: 11px; }
|
||||||
|
.best-for { background: #e8f5e9; padding: 5px 8px; border-radius: 4px; margin-top: 5px; font-size: 9px; }
|
||||||
|
|
||||||
|
.callout-box { background: #fff3cd; border-left: 3px solid #f39c12; padding: 8px; margin: 8px 0; border-radius: 3px; font-size: 10px; }
|
||||||
|
.callout-box.success { background: #d4edda; border-left-color: #27ae60; }
|
||||||
|
.callout-box.info { background: #d1ecf1; border-left-color: #17a2b8; }
|
||||||
|
|
||||||
|
.support-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 10px 0; }
|
||||||
|
.support-card { background: white; border: 2px solid #e0e0e0; border-radius: 8px; padding: 8px; position: relative; }
|
||||||
|
.support-card.popular { border-color: #f39c12; }
|
||||||
|
.support-card.popular:before { content: "⭐ MOST POPULAR"; position: absolute; top: -8px; left: 50%; transform: translateX(-50%); background: #f39c12; color: white; padding: 2px 6px; border-radius: 8px; font-size: 8px; font-weight: bold; }
|
||||||
|
.support-header { text-align: center; margin-bottom: 6px; padding-bottom: 6px; border-bottom: 2px solid #f39c12; }
|
||||||
|
.support-name { font-size: 11px; font-weight: bold; color: #1e3c72; margin-bottom: 2px; }
|
||||||
|
.support-price { font-size: 15px; font-weight: bold; color: #27ae60; }
|
||||||
|
.support-rate { font-size: 8px; color: #666; margin-top: 2px; }
|
||||||
|
|
||||||
|
.table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 10px; }
|
||||||
|
.table th { background: #1e3c72; color: white; padding: 5px; text-align: left; }
|
||||||
|
.table td { padding: 5px; border-bottom: 1px solid #e0e0e0; }
|
||||||
|
|
||||||
|
.example-box { background: white; border: 2px solid #1e3c72; border-radius: 8px; padding: 10px; margin: 8px 0; }
|
||||||
|
.example-header { font-size: 12px; font-weight: bold; color: #1e3c72; margin-bottom: 4px; }
|
||||||
|
.cost-breakdown { background: #f8f9fa; padding: 6px; border-radius: 4px; margin: 4px 0; }
|
||||||
|
.cost-breakdown .line-item { display: flex; justify-content: space-between; padding: 2px 0; font-size: 10px; }
|
||||||
|
.cost-breakdown .total { font-weight: bold; font-size: 12px; color: #1e3c72; border-top: 2px solid #1e3c72; margin-top: 4px; padding-top: 4px; }
|
||||||
|
|
||||||
|
.cta-box { background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%); color: white; padding: 12px; border-radius: 8px; text-align: center; margin: 10px 0; }
|
||||||
|
.cta-box h2 { color: white; border: none; margin: 0 0 4px 0; font-size: 13px; }
|
||||||
|
.cta-box .phone-large { font-size: 20px; font-weight: bold; margin: 4px 0; }
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 2px solid #1e3c72;
|
||||||
|
color: #666;
|
||||||
|
font-size: 9px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.35in;
|
||||||
|
left: 0.5in;
|
||||||
|
right: 0.5in;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- PAGE 1: INTRO -->
|
||||||
|
<div class="page">
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">Arizona Computer Guru</div>
|
||||||
|
<div class="contact">
|
||||||
|
<div class="phone">520.304.8300</div>
|
||||||
|
<div>7437 E. 22nd St, Tucson, AZ 85710</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>Complete IT Protection & Support</h1>
|
||||||
|
<div class="subtitle">Enterprise-Grade Security + Predictable Monthly Support</div>
|
||||||
|
|
||||||
|
<p style="font-size: 12px; margin: 10px 0;">We provide comprehensive IT management through our GPS (Guru Protection Services) platform—combining advanced security monitoring with predictable support plans at transparent, competitive rates.</p>
|
||||||
|
|
||||||
|
<div class="hero-box">
|
||||||
|
<h2>What You Get with GPS</h2>
|
||||||
|
<div class="value-grid">
|
||||||
|
<div class="value-card">
|
||||||
|
<h3>🛡️ Enterprise Security</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Advanced threat detection</li>
|
||||||
|
<li>Email security & anti-phishing</li>
|
||||||
|
<li>Dark web monitoring</li>
|
||||||
|
<li>Security awareness training</li>
|
||||||
|
<li>Compliance tools</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="value-card">
|
||||||
|
<h3>🔧 Proactive Management</h3>
|
||||||
|
<ul>
|
||||||
|
<li>24/7 monitoring & alerting</li>
|
||||||
|
<li>Automated patch management</li>
|
||||||
|
<li>Remote support</li>
|
||||||
|
<li>Performance optimization</li>
|
||||||
|
<li>Regular health reports</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="value-card">
|
||||||
|
<h3>👥 Predictable Support</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed monthly rates</li>
|
||||||
|
<li>Guaranteed response times</li>
|
||||||
|
<li>Included support hours</li>
|
||||||
|
<li>Local experienced team</li>
|
||||||
|
<li>After-hours emergency</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout-box success">
|
||||||
|
<strong>Trusted by Tucson businesses for over 20 years.</strong> Let us show you how GPS provides enterprise-grade protection at small business prices.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">Protecting Tucson Businesses Since 2001 | Page 1 of 4</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PAGE 2: ALL THREE GPS TIERS -->
|
||||||
|
<div class="page">
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">Arizona Computer Guru</div>
|
||||||
|
<div class="contact"><div class="phone">520.304.8300</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>GPS Endpoint Monitoring</h1>
|
||||||
|
<div class="subtitle">Choose the protection level that matches your business needs</div>
|
||||||
|
|
||||||
|
<div class="tier-box">
|
||||||
|
<div class="tier-header">
|
||||||
|
<div class="tier-name">GPS-BASIC: Essential Protection</div>
|
||||||
|
<div class="tier-price">$19<span class="period">/endpoint/month</span></div>
|
||||||
|
</div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>24/7 System Monitoring & Alerting</li>
|
||||||
|
<li>Automated Patch Management</li>
|
||||||
|
<li>Remote Management & Support</li>
|
||||||
|
<li>Endpoint Security (Antivirus)</li>
|
||||||
|
<li>Monthly Health Reports</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best For:</strong> Small businesses with straightforward IT environments</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tier-box popular">
|
||||||
|
<span class="badge">⭐ MOST POPULAR</span>
|
||||||
|
<div class="tier-header">
|
||||||
|
<div class="tier-name">GPS-PRO: Business Protection</div>
|
||||||
|
<div class="tier-price">$26<span class="period">/endpoint/month</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-weight: 600; margin-bottom: 3px; font-size: 10px;">Everything in GPS-Basic, PLUS:</p>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li><strong>Advanced EDR</strong> - Stops threats antivirus misses</li>
|
||||||
|
<li><strong>Email Security</strong> - Anti-phishing & spam filtering</li>
|
||||||
|
<li><strong>Dark Web Monitoring</strong> - Alerts if credentials compromised</li>
|
||||||
|
<li><strong>Security Training</strong> - Monthly phishing simulations</li>
|
||||||
|
<li><strong>Cloud Monitoring</strong> - Microsoft 365 & Google protection</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best For:</strong> Businesses handling customer data or requiring cyber insurance</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tier-box">
|
||||||
|
<div class="tier-header">
|
||||||
|
<div class="tier-name">GPS-ADVANCED: Maximum Protection</div>
|
||||||
|
<div class="tier-price">$39<span class="period">/endpoint/month</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-weight: 600; margin-bottom: 3px; font-size: 10px;">Everything in GPS-Pro, PLUS:</p>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li><strong>Advanced Threat Intelligence</strong> - Real-time global threat data</li>
|
||||||
|
<li><strong>Ransomware Rollback</strong> - Automatic recovery from attacks</li>
|
||||||
|
<li><strong>Compliance Tools</strong> - HIPAA, PCI-DSS, SOC 2 reporting</li>
|
||||||
|
<li><strong>Priority Response</strong> - Fast-tracked incident response</li>
|
||||||
|
<li><strong>Enhanced SaaS Backup</strong> - Complete M365/Google backup</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best For:</strong> Healthcare, legal, financial services, or businesses with sensitive data</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 style="margin-top: 8px;">GPS-Equipment Monitoring Pack</h2>
|
||||||
|
<p style="font-size: 10px; margin-bottom: 6px;">Extend support plan coverage to network equipment, printers, and other devices</p>
|
||||||
|
|
||||||
|
<div class="tier-box" style="margin: 6px 0;">
|
||||||
|
<div class="tier-header">
|
||||||
|
<div class="tier-name">Equipment Monitoring Pack</div>
|
||||||
|
<div class="tier-price">$25<span class="period">/month</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 10px; margin-bottom: 3px;"><strong>Covers up to 10 non-computer devices:</strong> Routers, switches, firewalls, printers, scanners, NAS, cameras, and other network equipment. $3 per additional device beyond 10.</p>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>Basic uptime monitoring & alerting</li>
|
||||||
|
<li>Devices eligible for Support Plan labor coverage</li>
|
||||||
|
<li>Quick fixes under 10 minutes included</li>
|
||||||
|
<li>Monthly equipment health reports</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Note:</strong> Equipment Pack makes devices eligible for Support Plan hours. Block time covers any device regardless of enrollment.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout-box info">
|
||||||
|
<strong>💰 Volume Discounts Available:</strong> Contact us for custom pricing on larger deployments.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">Protecting Tucson Businesses Since 2001 | Page 2 of 4</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PAGE 3: SUPPORT PLANS -->
|
||||||
|
<div class="page">
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">Arizona Computer Guru</div>
|
||||||
|
<div class="contact"><div class="phone">520.304.8300</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>Support Plans</h1>
|
||||||
|
<div class="subtitle">Predictable monthly labor costs with guaranteed response times</div>
|
||||||
|
|
||||||
|
<div class="support-grid">
|
||||||
|
<div class="support-card">
|
||||||
|
<div class="support-header">
|
||||||
|
<div class="support-name">Essential Support</div>
|
||||||
|
<div class="support-price">$200/month</div>
|
||||||
|
<div class="support-rate">2 hours included • $100/hr effective</div>
|
||||||
|
</div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>Next business day response</li>
|
||||||
|
<li>Email & phone support</li>
|
||||||
|
<li>Business hours coverage</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best for:</strong> Minimal IT issues</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="support-card popular">
|
||||||
|
<div class="support-header">
|
||||||
|
<div class="support-name">Standard Support</div>
|
||||||
|
<div class="support-price">$380/month</div>
|
||||||
|
<div class="support-rate">4 hours included • $95/hr effective</div>
|
||||||
|
</div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>8-hour response guarantee</li>
|
||||||
|
<li>Priority phone support</li>
|
||||||
|
<li>Business hours coverage</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best for:</strong> Regular IT needs</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="support-card">
|
||||||
|
<div class="support-header">
|
||||||
|
<div class="support-name">Premium Support</div>
|
||||||
|
<div class="support-price">$540/month</div>
|
||||||
|
<div class="support-rate">6 hours included • $90/hr effective</div>
|
||||||
|
</div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>4-hour response guarantee</li>
|
||||||
|
<li>After-hours emergency support</li>
|
||||||
|
<li>Extended coverage</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best for:</strong> Technology-dependent businesses</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="support-card">
|
||||||
|
<div class="support-header">
|
||||||
|
<div class="support-name">Priority Support</div>
|
||||||
|
<div class="support-price">$850/month</div>
|
||||||
|
<div class="support-rate">10 hours included • $85/hr effective</div>
|
||||||
|
</div>
|
||||||
|
<ul class="features-list">
|
||||||
|
<li>2-hour response guarantee</li>
|
||||||
|
<li>24/7 emergency support</li>
|
||||||
|
<li>Dedicated account manager</li>
|
||||||
|
</ul>
|
||||||
|
<div class="best-for"><strong>Best for:</strong> Mission-critical operations</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout-box">
|
||||||
|
<strong>How Labor Hours Work:</strong> Support plan hours are used first each month. If you also have prepaid block time, those hours are used next. Any hours beyond that are billed at $175/hour.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout-box info">
|
||||||
|
<strong>📋 Coverage Scope:</strong> Support plan hours apply to GPS-enrolled endpoints, enrolled websites, and devices in the Equipment Pack. Block time applies to any device or service. Quick fixes under 10 minutes are included in monitoring fees. Volume discounts available for larger deployments.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Prepaid Block Time (Alternative or Supplement)</h2>
|
||||||
|
<p style="font-size: 10px; margin-bottom: 6px;">For projects, seasonal needs, or clients who prefer non-expiring hours. Available to anyone.</p>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<tr><th>Block Size</th><th>Price</th><th>Effective Rate</th><th>Valid</th></tr>
|
||||||
|
<tr><td>10 hours</td><td>$1,500</td><td>$150/hour</td><td>Never expires</td></tr>
|
||||||
|
<tr><td>20 hours</td><td>$2,600</td><td>$130/hour</td><td>Never expires</td></tr>
|
||||||
|
<tr><td>30 hours</td><td>$3,000</td><td>$100/hour</td><td>Never expires</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="callout-box">
|
||||||
|
<strong>Note:</strong> Block time can be purchased by anyone and used alongside a Support Plan. Block hours never expire—use them for special projects or as backup when plan hours run out.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">Protecting Tucson Businesses Since 2001 | Page 3 of 4</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PAGE 4: EXAMPLES & CTA -->
|
||||||
|
<div class="page">
|
||||||
|
<div class="header">
|
||||||
|
<div class="logo">Arizona Computer Guru</div>
|
||||||
|
<div class="contact"><div class="phone">520.304.8300</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>What Will This Cost My Business?</h1>
|
||||||
|
|
||||||
|
<div class="example-box">
|
||||||
|
<div class="example-header">Example 1: Small Office (10 endpoints + 4 devices)</div>
|
||||||
|
<p style="font-size: 10px;"><strong>Recommended:</strong> GPS-Pro + Equipment Pack + Standard Support</p>
|
||||||
|
<div class="cost-breakdown">
|
||||||
|
<div class="line-item"><span>GPS-Pro Monitoring (10 × $26)</span><span>$260</span></div>
|
||||||
|
<div class="line-item"><span>Equipment Pack (4 devices)</span><span>$25</span></div>
|
||||||
|
<div class="line-item"><span>Standard Support (4 hrs included)</span><span>$380</span></div>
|
||||||
|
<div class="line-item total"><span>Total Monthly</span><span>$665</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 9px; margin-top: 4px; color: #27ae60;">✓ All computers + network gear covered • 4 hours labor • 8-hour response</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example-box">
|
||||||
|
<div class="example-header">Example 2: Growing Business (22 endpoints)</div>
|
||||||
|
<p style="font-size: 10px;"><strong>Recommended:</strong> GPS-Pro + Premium Support</p>
|
||||||
|
<div class="cost-breakdown">
|
||||||
|
<div class="line-item"><span>GPS-Pro Monitoring (22 × $26)</span><span>$572</span></div>
|
||||||
|
<div class="line-item"><span>Premium Support (6 hrs included)</span><span>$540</span></div>
|
||||||
|
<div class="line-item total"><span>Total Monthly</span><span>$1,112</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 9px; margin-top: 4px; color: #27ae60;">✓ 6 hours labor • 4-hour response • After-hours emergency • $51/endpoint total</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="example-box">
|
||||||
|
<div class="example-header">Example 3: Established Company (42 endpoints)</div>
|
||||||
|
<p style="font-size: 10px;"><strong>Recommended:</strong> GPS-Pro + Priority Support</p>
|
||||||
|
<div class="cost-breakdown">
|
||||||
|
<div class="line-item"><span>GPS-Pro Monitoring (42 × $26)</span><span>$1,092</span></div>
|
||||||
|
<div class="line-item"><span>Priority Support (10 hrs included)</span><span>$850</span></div>
|
||||||
|
<div class="line-item total"><span>Total Monthly</span><span>$1,942</span></div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 9px; margin-top: 4px; color: #27ae60;">✓ 10 hours labor • 2-hour response • 24/7 emergency • $46/endpoint total</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cta-box">
|
||||||
|
<h2>Ready to Get Started?</h2>
|
||||||
|
<p style="font-size: 10px;">Schedule your free consultation today</p>
|
||||||
|
<div class="phone-large">520.304.8300</div>
|
||||||
|
<p style="font-size: 9px;"><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d5bcbbb3ba95b4afb6bab8a5a0a1b0a7b2a0a7a0fbb6bab8">[email protected]</a> | azcomputerguru.com</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="callout-box success">
|
||||||
|
<strong>🎁 Special Offer for New Clients:</strong> Sign up within 30 days and receive waived setup fees, first month 50% off support plans, and a free security assessment ($500 value).
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=
|
||||||
340
projects/msp-pricing/README.md
Normal file
340
projects/msp-pricing/README.md
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# MSP Pricing Project
|
||||||
|
|
||||||
|
**Created:** 2026-02-01
|
||||||
|
**Purpose:** Complete MSP pricing calculator, models, and templates
|
||||||
|
**Status:** Active - Fully imported from web version
|
||||||
|
**Location:** `D:\ClaudeTools\projects\msp-pricing\`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Run Complete Pricing Calculator
|
||||||
|
```bash
|
||||||
|
cd /d/ClaudeTools/projects/msp-pricing
|
||||||
|
python calculators/complete-pricing-calculator.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run GPS-Only Calculator
|
||||||
|
```bash
|
||||||
|
python calculators/gps-calculator.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Documentation
|
||||||
|
- **GPS Pricing:** `docs/gps-pricing-structure.md`
|
||||||
|
- **Web/Email Hosting:** `docs/web-email-hosting-pricing.md`
|
||||||
|
- **HTML Price Sheet:** `GPS_Price_Sheet_12.html` (4-page printable)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Complete Pricing Structure
|
||||||
|
|
||||||
|
### GPS Endpoint Monitoring
|
||||||
|
- **GPS-BASIC:** $19/endpoint/month - Essential protection
|
||||||
|
- **GPS-PRO:** $26/endpoint/month - Business protection ⭐ MOST POPULAR
|
||||||
|
- **GPS-ADVANCED:** $39/endpoint/month - Maximum protection
|
||||||
|
- **Equipment Pack:** $25/month (up to 10 devices)
|
||||||
|
|
||||||
|
### Support Plans
|
||||||
|
- **Essential:** $200/month (2 hrs included) - $100/hr effective
|
||||||
|
- **Standard:** $380/month (4 hrs included) - $95/hr effective ⭐ MOST POPULAR
|
||||||
|
- **Premium:** $540/month (6 hrs included) - $90/hr effective
|
||||||
|
- **Priority:** $850/month (10 hrs included) - $85/hr effective
|
||||||
|
|
||||||
|
### Block Time (Non-Expiring)
|
||||||
|
- **10 hours:** $1,500 ($150/hr)
|
||||||
|
- **20 hours:** $2,600 ($130/hr)
|
||||||
|
- **30 hours:** $3,000 ($100/hr)
|
||||||
|
|
||||||
|
### Web Hosting
|
||||||
|
- **Starter:** $15/month (5GB, 1 website)
|
||||||
|
- **Business:** $35/month (25GB, 5 websites) ⭐ MOST POPULAR
|
||||||
|
- **Commerce:** $65/month (50GB, unlimited websites)
|
||||||
|
|
||||||
|
### Email Hosting
|
||||||
|
|
||||||
|
**WHM Email (IMAP/POP):**
|
||||||
|
- **Base:** $2/mailbox/month (5GB included)
|
||||||
|
- **Storage:** +$2 per 5GB block
|
||||||
|
- **Pre-configured:**
|
||||||
|
- 5GB: $2/month
|
||||||
|
- 10GB: $4/month
|
||||||
|
- 25GB: $10/month
|
||||||
|
- 50GB: $20/month
|
||||||
|
|
||||||
|
**Microsoft 365:**
|
||||||
|
- **Business Basic:** $7/user/month
|
||||||
|
- **Business Standard:** $14/user/month ⭐ MOST POPULAR
|
||||||
|
- **Business Premium:** $24/user/month
|
||||||
|
- **Exchange Online:** $5/user/month
|
||||||
|
|
||||||
|
**Email Security Add-on:**
|
||||||
|
- **MailProtector/INKY:** $3/mailbox/month (recommended for all WHM email)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
msp-pricing/
|
||||||
|
├── GPS_Price_Sheet_12.html # 4-page GPS pricing document
|
||||||
|
├── docs/
|
||||||
|
│ ├── gps-pricing-structure.md # GPS pricing data
|
||||||
|
│ └── web-email-hosting-pricing.md # Web/email pricing data
|
||||||
|
├── calculators/
|
||||||
|
│ ├── gps-calculator.py # GPS-only calculator
|
||||||
|
│ └── complete-pricing-calculator.py # Full pricing calculator
|
||||||
|
├── templates/ # Quote templates (TBD)
|
||||||
|
├── session-logs/
|
||||||
|
│ └── 2026-02-01-project-import.md # Import session log
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Pricing Scenarios
|
||||||
|
|
||||||
|
### Small Office (10 endpoints + Website + 5 WHM email)
|
||||||
|
**GPS-Pro + Business Hosting + WHM Email + Standard Support**
|
||||||
|
```
|
||||||
|
GPS-Pro (10 × $26) $260
|
||||||
|
Equipment Pack $25
|
||||||
|
Standard Support (4 hrs) $380
|
||||||
|
Business Hosting $35
|
||||||
|
WHM Email 10GB (5 × $4) $20
|
||||||
|
Email Security (5 × $3) $15
|
||||||
|
----------------------------------------
|
||||||
|
MONTHLY TOTAL: $735
|
||||||
|
ANNUAL TOTAL: $8,820
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modern Business (22 endpoints + Website + 15 M365)
|
||||||
|
**GPS-Pro + Business Hosting + M365 Standard + Premium Support**
|
||||||
|
```
|
||||||
|
GPS-Pro (22 × $26) $572
|
||||||
|
Premium Support (6 hrs) $540
|
||||||
|
Business Hosting $35
|
||||||
|
M365 Business Standard (15 × $14) $210
|
||||||
|
----------------------------------------
|
||||||
|
MONTHLY TOTAL: $1,357
|
||||||
|
ANNUAL TOTAL: $16,284
|
||||||
|
```
|
||||||
|
|
||||||
|
### E-Commerce (42 endpoints + Commerce + 20 M365 + Add-ons)
|
||||||
|
**GPS-Pro + Commerce Hosting + M365 + Priority Support + IP**
|
||||||
|
```
|
||||||
|
GPS-Pro (42 × $26) $1,092
|
||||||
|
Priority Support (10 hrs) $850
|
||||||
|
Commerce Hosting $65
|
||||||
|
M365 Business Standard (20 × $14) $280
|
||||||
|
Dedicated IP $5
|
||||||
|
Premium SSL $6
|
||||||
|
----------------------------------------
|
||||||
|
MONTHLY TOTAL: $2,298
|
||||||
|
ANNUAL TOTAL: $27,576
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web & Email Only (No GPS)
|
||||||
|
**Business Hosting + 8 WHM Email**
|
||||||
|
```
|
||||||
|
Business Hosting $35
|
||||||
|
WHM Email 10GB (8 × $4) $32
|
||||||
|
Email Security (8 × $3) $24
|
||||||
|
----------------------------------------
|
||||||
|
MONTHLY TOTAL: $91
|
||||||
|
ANNUAL TOTAL: $1,092
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Calculator Usage
|
||||||
|
|
||||||
|
### Python API
|
||||||
|
|
||||||
|
```python
|
||||||
|
from calculators.complete_pricing_calculator import (
|
||||||
|
calculate_complete_quote,
|
||||||
|
print_complete_quote
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate custom quote
|
||||||
|
quote = calculate_complete_quote(
|
||||||
|
# GPS
|
||||||
|
gps_endpoints=15,
|
||||||
|
gps_tier='pro',
|
||||||
|
equipment_devices=5,
|
||||||
|
support_plan='standard',
|
||||||
|
|
||||||
|
# Web
|
||||||
|
web_hosting_tier='business',
|
||||||
|
|
||||||
|
# Email
|
||||||
|
email_type='whm', # or 'm365'
|
||||||
|
email_users=10,
|
||||||
|
whm_storage_gb=10,
|
||||||
|
whm_security=True,
|
||||||
|
|
||||||
|
# Add-ons
|
||||||
|
dedicated_ip=False
|
||||||
|
)
|
||||||
|
|
||||||
|
print_complete_quote(quote)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Individual Calculators
|
||||||
|
|
||||||
|
```python
|
||||||
|
# GPS only
|
||||||
|
from calculators.gps_calculator import calculate_gps_quote
|
||||||
|
quote = calculate_gps_quote(
|
||||||
|
endpoints=10,
|
||||||
|
tier='pro',
|
||||||
|
support_plan='standard'
|
||||||
|
)
|
||||||
|
|
||||||
|
# WHM Email
|
||||||
|
from calculators.complete_pricing_calculator import calculate_whm_email
|
||||||
|
email = calculate_whm_email(
|
||||||
|
mailboxes=5,
|
||||||
|
storage_gb_per_mailbox=10,
|
||||||
|
include_security=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# M365 Email
|
||||||
|
from calculators.complete_pricing_calculator import calculate_m365_email
|
||||||
|
m365 = calculate_m365_email(users=10, plan='standard')
|
||||||
|
|
||||||
|
# Web Hosting
|
||||||
|
from calculators.complete_pricing_calculator import calculate_web_hosting
|
||||||
|
web = calculate_web_hosting(tier='business', extra_storage_gb=20)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
✓ **GPS Endpoint Monitoring** - 3 tiers with equipment pack option
|
||||||
|
✓ **Flexible Support Plans** - 2-10 hours included, $85-100/hr effective
|
||||||
|
✓ **Non-Expiring Block Time** - Project hours that never expire
|
||||||
|
✓ **Web Hosting** - 3 tiers from starter to e-commerce
|
||||||
|
✓ **Dual Email Options** - Budget WHM or full M365
|
||||||
|
✓ **Email Security** - MailProtector/INKY add-on
|
||||||
|
✓ **Predictable Monthly Costs** - No surprise bills
|
||||||
|
✓ **Per-Endpoint Pricing** - Scales with business size
|
||||||
|
✓ **Equipment Monitoring** - Extend coverage to network gear
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pricing Philosophy
|
||||||
|
|
||||||
|
### GPS (Guru Protection Services)
|
||||||
|
**Goal:** Enterprise-grade security at small business prices
|
||||||
|
- Predictable monthly monitoring per endpoint
|
||||||
|
- Support hours bundled for predictability
|
||||||
|
- Block time for projects and overages
|
||||||
|
|
||||||
|
### Web/Email Hosting
|
||||||
|
**Goal:** Managed specialty hosting with personal service
|
||||||
|
- Budget-friendly WHM email for IMAP/POP users
|
||||||
|
- M365 for collaboration and compliance needs
|
||||||
|
- Fair storage pricing with no "gotcha" fees
|
||||||
|
|
||||||
|
### Storage Overages
|
||||||
|
**WHM Email Storage Policy:**
|
||||||
|
- Hard quota per mailbox (not pooled)
|
||||||
|
- Mail continues to deliver over quota (customer-friendly)
|
||||||
|
- Notifications when approaching/exceeding quota
|
||||||
|
- $2 per 5GB block ($0.40/GB effective)
|
||||||
|
|
||||||
|
**Migration Strategy for Legacy "Unlimited" Clients:**
|
||||||
|
- 60-90 day notice before billing changes
|
||||||
|
- One-time mailbox cleanup service offered
|
||||||
|
- Suggest M365 migration for heavy users (200+ GB)
|
||||||
|
- Transparent reporting on current usage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## National Pricing Comparisons
|
||||||
|
|
||||||
|
### Our Position vs. Market
|
||||||
|
|
||||||
|
**Hourly Labor:**
|
||||||
|
- ACG Rate: $130-165/hour (full rate)
|
||||||
|
- GPS Support: $85-100/hour (effective rate on plans)
|
||||||
|
- Market: $60-120/hour (agencies), $45/hour (freelancers)
|
||||||
|
- **Result:** Competitive with professional MSPs, excellent value on support plans
|
||||||
|
|
||||||
|
**Web Hosting:**
|
||||||
|
- ACG: $15-65/month (managed)
|
||||||
|
- Market: $3-30/month (shared), $20-100/month (VPS)
|
||||||
|
- **Result:** Premium managed service, competitive with specialty hosts
|
||||||
|
|
||||||
|
**Email Hosting:**
|
||||||
|
- ACG WHM: $2-20/month (5-50GB)
|
||||||
|
- ACG M365: $7-24/user (standard Microsoft pricing)
|
||||||
|
- Market: $2-12/month (basic), $7-30/month (M365)
|
||||||
|
- **Result:** Budget option (WHM) + enterprise option (M365)
|
||||||
|
|
||||||
|
**Web Development:**
|
||||||
|
- ACG: $130-165/hour
|
||||||
|
- Market: $45-120/hour (varies widely)
|
||||||
|
- Small business site: $5,000-20,000
|
||||||
|
- **Result:** Professional MSP pricing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TODO / Future Enhancements
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
- [ ] Create quote templates (Word/PDF)
|
||||||
|
- [ ] Build proposal templates with ROI data
|
||||||
|
- [ ] Create service agreement templates
|
||||||
|
|
||||||
|
### Calculators
|
||||||
|
- [ ] Competitor comparison calculator
|
||||||
|
- [ ] ROI calculator (cost of breach, downtime costs)
|
||||||
|
- [ ] Internal margin calculator
|
||||||
|
- [ ] Customer-facing web calculator (React/Vue)
|
||||||
|
|
||||||
|
### Marketing Materials
|
||||||
|
- [ ] Cost-of-breach calculator for security justification
|
||||||
|
- [ ] TCO comparison (DIY vs managed)
|
||||||
|
- [ ] Case studies with pricing examples
|
||||||
|
|
||||||
|
### Integration
|
||||||
|
- [ ] Connect to ClaudeTools API
|
||||||
|
- [ ] Auto-generate quotes in database
|
||||||
|
- [ ] QuickBooks integration for billing
|
||||||
|
- [ ] CRM integration (Syncro/Autotask)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
### Contact
|
||||||
|
- **Phone:** 520.304.8300
|
||||||
|
- **Email:** mike@azcomputerguru.com
|
||||||
|
- **Website:** azcomputerguru.com
|
||||||
|
- **Address:** 7437 E. 22nd St, Tucson, AZ 85710
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- National pricing research (see session logs)
|
||||||
|
- Industry recommendations by vertical
|
||||||
|
- Migration strategies for legacy clients
|
||||||
|
- Email security platform comparison
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project History
|
||||||
|
|
||||||
|
**2026-02-01:** Project created and fully imported from web version
|
||||||
|
- GPS pricing structure documented
|
||||||
|
- Web/email hosting pricing added
|
||||||
|
- Python calculators created
|
||||||
|
- National pricing research compiled
|
||||||
|
- Session logs initiated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-01
|
||||||
|
**Protecting Tucson Businesses Since 2001**
|
||||||
399
projects/msp-pricing/calculators/complete-pricing-calculator.py
Normal file
399
projects/msp-pricing/calculators/complete-pricing-calculator.py
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Complete MSP Pricing Calculator
|
||||||
|
Arizona Computer Guru - GPS + Web/Email Hosting
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# GPS ENDPOINT MONITORING
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
GPS_TIERS = {
|
||||||
|
'basic': {
|
||||||
|
'name': 'GPS-BASIC: Essential Protection',
|
||||||
|
'price_per_endpoint': 19,
|
||||||
|
},
|
||||||
|
'pro': {
|
||||||
|
'name': 'GPS-PRO: Business Protection (MOST POPULAR)',
|
||||||
|
'price_per_endpoint': 26,
|
||||||
|
},
|
||||||
|
'advanced': {
|
||||||
|
'name': 'GPS-ADVANCED: Maximum Protection',
|
||||||
|
'price_per_endpoint': 39,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EQUIPMENT_PACK = {
|
||||||
|
'base_price': 25,
|
||||||
|
'base_devices': 10,
|
||||||
|
'additional_device_price': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
SUPPORT_PLANS = {
|
||||||
|
'essential': {'name': 'Essential Support', 'price': 200, 'hours': 2},
|
||||||
|
'standard': {'name': 'Standard Support (MOST POPULAR)', 'price': 380, 'hours': 4},
|
||||||
|
'premium': {'name': 'Premium Support', 'price': 540, 'hours': 6},
|
||||||
|
'priority': {'name': 'Priority Support', 'price': 850, 'hours': 10}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# WEB HOSTING
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
WEB_HOSTING = {
|
||||||
|
'starter': {
|
||||||
|
'name': 'Starter Hosting',
|
||||||
|
'price': 15,
|
||||||
|
'storage_gb': 5,
|
||||||
|
'websites': 1
|
||||||
|
},
|
||||||
|
'business': {
|
||||||
|
'name': 'Business Hosting (MOST POPULAR)',
|
||||||
|
'price': 35,
|
||||||
|
'storage_gb': 25,
|
||||||
|
'websites': 5
|
||||||
|
},
|
||||||
|
'commerce': {
|
||||||
|
'name': 'Commerce Hosting',
|
||||||
|
'price': 65,
|
||||||
|
'storage_gb': 50,
|
||||||
|
'websites': 'unlimited'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# EMAIL HOSTING
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
WHM_EMAIL = {
|
||||||
|
'base_price_per_mailbox': 2,
|
||||||
|
'included_storage_gb': 5,
|
||||||
|
'storage_block_price': 2, # Per 5GB block
|
||||||
|
'storage_block_size_gb': 5
|
||||||
|
}
|
||||||
|
|
||||||
|
M365_PLANS = {
|
||||||
|
'basic': {
|
||||||
|
'name': 'M365 Business Basic',
|
||||||
|
'price_per_user': 7,
|
||||||
|
'storage_gb': 50
|
||||||
|
},
|
||||||
|
'standard': {
|
||||||
|
'name': 'M365 Business Standard (MOST POPULAR)',
|
||||||
|
'price_per_user': 14,
|
||||||
|
'storage_gb': 50
|
||||||
|
},
|
||||||
|
'premium': {
|
||||||
|
'name': 'M365 Business Premium',
|
||||||
|
'price_per_user': 24,
|
||||||
|
'storage_gb': 50
|
||||||
|
},
|
||||||
|
'exchange': {
|
||||||
|
'name': 'Exchange Online Plan 1',
|
||||||
|
'price_per_user': 5,
|
||||||
|
'storage_gb': 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EMAIL_SECURITY_ADDON = {
|
||||||
|
'price_per_mailbox': 3,
|
||||||
|
'name': 'Email Security & Filtering (MailProtector/INKY)'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ADD-ON SERVICES
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
ADDONS = {
|
||||||
|
'dedicated_ip': {'name': 'Dedicated IP', 'price': 5},
|
||||||
|
'premium_ssl': {'name': 'SSL Certificate (Premium)', 'price': 6.25}, # $75/year / 12
|
||||||
|
'offsite_backup': {'name': 'Daily Offsite Backup', 'price': 10},
|
||||||
|
'web_storage_10gb': {'name': 'Additional Web Storage (10GB)', 'price': 5}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# CALCULATOR FUNCTIONS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def calculate_whm_email(mailboxes, storage_gb_per_mailbox=5, include_security=False):
|
||||||
|
"""
|
||||||
|
Calculate WHM email hosting costs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mailboxes: Number of mailboxes
|
||||||
|
storage_gb_per_mailbox: Storage per mailbox in GB
|
||||||
|
include_security: Add email security filtering
|
||||||
|
"""
|
||||||
|
base_cost = mailboxes * WHM_EMAIL['base_price_per_mailbox']
|
||||||
|
|
||||||
|
# Calculate storage blocks needed
|
||||||
|
if storage_gb_per_mailbox > WHM_EMAIL['included_storage_gb']:
|
||||||
|
additional_gb = storage_gb_per_mailbox - WHM_EMAIL['included_storage_gb']
|
||||||
|
blocks_needed = -(-additional_gb // WHM_EMAIL['storage_block_size_gb']) # Ceiling division
|
||||||
|
storage_cost = mailboxes * blocks_needed * WHM_EMAIL['storage_block_price']
|
||||||
|
else:
|
||||||
|
blocks_needed = 0
|
||||||
|
storage_cost = 0
|
||||||
|
|
||||||
|
total_mailbox_cost = base_cost + storage_cost
|
||||||
|
|
||||||
|
# Email security
|
||||||
|
security_cost = mailboxes * EMAIL_SECURITY_ADDON['price_per_mailbox'] if include_security else 0
|
||||||
|
|
||||||
|
total_cost = total_mailbox_cost + security_cost
|
||||||
|
|
||||||
|
return {
|
||||||
|
'mailboxes': mailboxes,
|
||||||
|
'storage_per_mailbox_gb': storage_gb_per_mailbox,
|
||||||
|
'base_cost': base_cost,
|
||||||
|
'storage_cost': storage_cost,
|
||||||
|
'security_cost': security_cost,
|
||||||
|
'total_cost': total_cost,
|
||||||
|
'cost_per_mailbox': total_cost / mailboxes if mailboxes > 0 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_m365_email(users, plan='standard'):
|
||||||
|
"""Calculate Microsoft 365 email costs"""
|
||||||
|
plan_data = M365_PLANS.get(plan, M365_PLANS['standard'])
|
||||||
|
|
||||||
|
return {
|
||||||
|
'users': users,
|
||||||
|
'plan': plan_data['name'],
|
||||||
|
'price_per_user': plan_data['price_per_user'],
|
||||||
|
'total_cost': users * plan_data['price_per_user'],
|
||||||
|
'storage_per_user_gb': plan_data['storage_gb']
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_web_hosting(tier='business', extra_storage_gb=0):
|
||||||
|
"""Calculate web hosting costs"""
|
||||||
|
tier_data = WEB_HOSTING.get(tier, WEB_HOSTING['business'])
|
||||||
|
|
||||||
|
# Extra storage in 10GB increments
|
||||||
|
extra_storage_cost = 0
|
||||||
|
if extra_storage_gb > 0:
|
||||||
|
blocks = -(-extra_storage_gb // 10) # Ceiling division
|
||||||
|
extra_storage_cost = blocks * ADDONS['web_storage_10gb']['price']
|
||||||
|
|
||||||
|
return {
|
||||||
|
'tier': tier_data['name'],
|
||||||
|
'base_cost': tier_data['price'],
|
||||||
|
'extra_storage_gb': extra_storage_gb,
|
||||||
|
'extra_storage_cost': extra_storage_cost,
|
||||||
|
'total_cost': tier_data['price'] + extra_storage_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_complete_quote(
|
||||||
|
# GPS
|
||||||
|
gps_endpoints=0,
|
||||||
|
gps_tier='pro',
|
||||||
|
equipment_devices=0,
|
||||||
|
support_plan=None,
|
||||||
|
|
||||||
|
# Web Hosting
|
||||||
|
web_hosting_tier=None,
|
||||||
|
web_extra_storage_gb=0,
|
||||||
|
|
||||||
|
# Email
|
||||||
|
email_type=None, # 'whm' or 'm365'
|
||||||
|
email_users=0,
|
||||||
|
whm_storage_gb=5,
|
||||||
|
whm_security=False,
|
||||||
|
m365_plan='standard',
|
||||||
|
|
||||||
|
# Add-ons
|
||||||
|
dedicated_ip=False,
|
||||||
|
premium_ssl=False,
|
||||||
|
offsite_backup=False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Calculate complete quote including GPS, web hosting, and email
|
||||||
|
"""
|
||||||
|
result = {
|
||||||
|
'gps': None,
|
||||||
|
'web': None,
|
||||||
|
'email': None,
|
||||||
|
'addons': [],
|
||||||
|
'totals': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
monthly_total = 0
|
||||||
|
|
||||||
|
# GPS Monitoring
|
||||||
|
if gps_endpoints > 0:
|
||||||
|
from gps_calculator import calculate_gps_quote
|
||||||
|
gps_quote = calculate_gps_quote(
|
||||||
|
endpoints=gps_endpoints,
|
||||||
|
tier=gps_tier,
|
||||||
|
equipment_devices=equipment_devices,
|
||||||
|
support_plan=support_plan
|
||||||
|
)
|
||||||
|
result['gps'] = gps_quote
|
||||||
|
monthly_total += gps_quote['totals']['monthly']
|
||||||
|
|
||||||
|
# Web Hosting
|
||||||
|
if web_hosting_tier:
|
||||||
|
web_quote = calculate_web_hosting(web_hosting_tier, web_extra_storage_gb)
|
||||||
|
result['web'] = web_quote
|
||||||
|
monthly_total += web_quote['total_cost']
|
||||||
|
|
||||||
|
# Email Hosting
|
||||||
|
if email_type == 'whm' and email_users > 0:
|
||||||
|
email_quote = calculate_whm_email(email_users, whm_storage_gb, whm_security)
|
||||||
|
result['email'] = {'type': 'WHM Email', 'details': email_quote}
|
||||||
|
monthly_total += email_quote['total_cost']
|
||||||
|
elif email_type == 'm365' and email_users > 0:
|
||||||
|
email_quote = calculate_m365_email(email_users, m365_plan)
|
||||||
|
result['email'] = {'type': 'Microsoft 365', 'details': email_quote}
|
||||||
|
monthly_total += email_quote['total_cost']
|
||||||
|
|
||||||
|
# Add-ons
|
||||||
|
addon_cost = 0
|
||||||
|
if dedicated_ip:
|
||||||
|
result['addons'].append(ADDONS['dedicated_ip'])
|
||||||
|
addon_cost += ADDONS['dedicated_ip']['price']
|
||||||
|
if premium_ssl:
|
||||||
|
result['addons'].append(ADDONS['premium_ssl'])
|
||||||
|
addon_cost += ADDONS['premium_ssl']['price']
|
||||||
|
if offsite_backup:
|
||||||
|
result['addons'].append(ADDONS['offsite_backup'])
|
||||||
|
addon_cost += ADDONS['offsite_backup']['price']
|
||||||
|
|
||||||
|
monthly_total += addon_cost
|
||||||
|
|
||||||
|
# Totals
|
||||||
|
result['totals'] = {
|
||||||
|
'monthly': monthly_total,
|
||||||
|
'annual': monthly_total * 12,
|
||||||
|
'addon_cost': addon_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def print_complete_quote(quote):
|
||||||
|
"""Print formatted complete quote"""
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print("COMPLETE MSP PRICING QUOTE - ARIZONA COMPUTER GURU")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
# GPS Section
|
||||||
|
if quote['gps']:
|
||||||
|
print("\n[GPS ENDPOINT MONITORING & SUPPORT]")
|
||||||
|
gps = quote['gps']
|
||||||
|
print(f" {gps['gps']['tier']}")
|
||||||
|
print(f" {gps['gps']['endpoints']} endpoints × ${gps['gps']['price_per_endpoint']} = ${gps['gps']['monthly_cost']}")
|
||||||
|
|
||||||
|
if gps['equipment']['devices'] > 0:
|
||||||
|
print(f" Equipment Pack: {gps['equipment']['devices']} devices = ${gps['equipment']['monthly_cost']}")
|
||||||
|
|
||||||
|
if gps['support']['monthly_cost'] > 0:
|
||||||
|
print(f" {gps['support']['plan']}: ${gps['support']['monthly_cost']} ({gps['support']['hours_included']} hrs)")
|
||||||
|
|
||||||
|
# Web Hosting Section
|
||||||
|
if quote['web']:
|
||||||
|
print("\n[WEB HOSTING]")
|
||||||
|
web = quote['web']
|
||||||
|
print(f" {web['tier']}: ${web['base_cost']}")
|
||||||
|
if web['extra_storage_gb'] > 0:
|
||||||
|
print(f" Extra Storage ({web['extra_storage_gb']}GB): ${web['extra_storage_cost']}")
|
||||||
|
|
||||||
|
# Email Section
|
||||||
|
if quote['email']:
|
||||||
|
print("\n[EMAIL HOSTING]")
|
||||||
|
email = quote['email']
|
||||||
|
print(f" {email['type']}")
|
||||||
|
|
||||||
|
if email['type'] == 'WHM Email':
|
||||||
|
details = email['details']
|
||||||
|
print(f" {details['mailboxes']} mailboxes × {details['storage_per_mailbox_gb']}GB")
|
||||||
|
print(f" Base: ${details['base_cost']}")
|
||||||
|
if details['storage_cost'] > 0:
|
||||||
|
print(f" Additional Storage: ${details['storage_cost']}")
|
||||||
|
if details['security_cost'] > 0:
|
||||||
|
print(f" Security Add-on: ${details['security_cost']}")
|
||||||
|
else: # M365
|
||||||
|
details = email['details']
|
||||||
|
print(f" {details['plan']}")
|
||||||
|
print(f" {details['users']} users × ${details['price_per_user']} = ${details['total_cost']}")
|
||||||
|
|
||||||
|
# Add-ons
|
||||||
|
if quote['addons']:
|
||||||
|
print("\n[ADD-ON SERVICES]")
|
||||||
|
for addon in quote['addons']:
|
||||||
|
print(f" {addon['name']}: ${addon['price']}")
|
||||||
|
|
||||||
|
# Totals
|
||||||
|
print("\n" + "-"*70)
|
||||||
|
print(f"MONTHLY TOTAL: ${quote['totals']['monthly']}")
|
||||||
|
print(f"ANNUAL TOTAL: ${quote['totals']['annual']}")
|
||||||
|
print("="*70 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# EXAMPLE USAGE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("\nCOMPLETE MSP PRICING CALCULATOR")
|
||||||
|
print("Arizona Computer Guru")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
# Example 1: Small Office - GPS + Web + WHM Email
|
||||||
|
print("\n\nExample 1: Small Office")
|
||||||
|
print("10 GPS endpoints + Website + 5 WHM email users")
|
||||||
|
quote1 = calculate_complete_quote(
|
||||||
|
gps_endpoints=10,
|
||||||
|
gps_tier='pro',
|
||||||
|
support_plan='standard',
|
||||||
|
web_hosting_tier='business',
|
||||||
|
email_type='whm',
|
||||||
|
email_users=5,
|
||||||
|
whm_storage_gb=10,
|
||||||
|
whm_security=True
|
||||||
|
)
|
||||||
|
print_complete_quote(quote1)
|
||||||
|
|
||||||
|
# Example 2: Modern Business - GPS + Web + M365
|
||||||
|
print("\n\nExample 2: Modern Business")
|
||||||
|
print("22 GPS endpoints + Website + 15 M365 users")
|
||||||
|
quote2 = calculate_complete_quote(
|
||||||
|
gps_endpoints=22,
|
||||||
|
gps_tier='pro',
|
||||||
|
support_plan='premium',
|
||||||
|
web_hosting_tier='business',
|
||||||
|
email_type='m365',
|
||||||
|
email_users=15,
|
||||||
|
m365_plan='standard'
|
||||||
|
)
|
||||||
|
print_complete_quote(quote2)
|
||||||
|
|
||||||
|
# Example 3: E-Commerce Business
|
||||||
|
print("\n\nExample 3: E-Commerce Business")
|
||||||
|
print("42 GPS endpoints + Commerce hosting + 20 M365 users + Dedicated IP")
|
||||||
|
quote3 = calculate_complete_quote(
|
||||||
|
gps_endpoints=42,
|
||||||
|
gps_tier='pro',
|
||||||
|
support_plan='priority',
|
||||||
|
web_hosting_tier='commerce',
|
||||||
|
email_type='m365',
|
||||||
|
email_users=20,
|
||||||
|
m365_plan='standard',
|
||||||
|
dedicated_ip=True,
|
||||||
|
premium_ssl=True
|
||||||
|
)
|
||||||
|
print_complete_quote(quote3)
|
||||||
|
|
||||||
|
# Example 4: Web + Email Only (No GPS)
|
||||||
|
print("\n\nExample 4: Web & Email Only")
|
||||||
|
print("Small business - Website + 8 WHM email users")
|
||||||
|
quote4 = calculate_complete_quote(
|
||||||
|
web_hosting_tier='business',
|
||||||
|
email_type='whm',
|
||||||
|
email_users=8,
|
||||||
|
whm_storage_gb=10,
|
||||||
|
whm_security=True
|
||||||
|
)
|
||||||
|
print_complete_quote(quote4)
|
||||||
244
projects/msp-pricing/calculators/gps-calculator.py
Normal file
244
projects/msp-pricing/calculators/gps-calculator.py
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
GPS Pricing Calculator
|
||||||
|
Arizona Computer Guru - MSP Pricing Tool
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Pricing Constants
|
||||||
|
GPS_TIERS = {
|
||||||
|
'basic': {
|
||||||
|
'name': 'GPS-BASIC: Essential Protection',
|
||||||
|
'price_per_endpoint': 19,
|
||||||
|
'features': [
|
||||||
|
'24/7 System Monitoring & Alerting',
|
||||||
|
'Automated Patch Management',
|
||||||
|
'Remote Management & Support',
|
||||||
|
'Endpoint Security (Antivirus)',
|
||||||
|
'Monthly Health Reports'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'pro': {
|
||||||
|
'name': 'GPS-PRO: Business Protection (MOST POPULAR)',
|
||||||
|
'price_per_endpoint': 26,
|
||||||
|
'features': [
|
||||||
|
'All GPS-Basic features',
|
||||||
|
'Advanced EDR',
|
||||||
|
'Email Security',
|
||||||
|
'Dark Web Monitoring',
|
||||||
|
'Security Training',
|
||||||
|
'Cloud Monitoring (M365/Google)'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'advanced': {
|
||||||
|
'name': 'GPS-ADVANCED: Maximum Protection',
|
||||||
|
'price_per_endpoint': 39,
|
||||||
|
'features': [
|
||||||
|
'All GPS-Pro features',
|
||||||
|
'Advanced Threat Intelligence',
|
||||||
|
'Ransomware Rollback',
|
||||||
|
'Compliance Tools (HIPAA, PCI-DSS, SOC 2)',
|
||||||
|
'Priority Response',
|
||||||
|
'Enhanced SaaS Backup'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EQUIPMENT_PACK = {
|
||||||
|
'base_price': 25, # Up to 10 devices
|
||||||
|
'base_devices': 10,
|
||||||
|
'additional_device_price': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
SUPPORT_PLANS = {
|
||||||
|
'essential': {
|
||||||
|
'name': 'Essential Support',
|
||||||
|
'price': 200,
|
||||||
|
'hours_included': 2,
|
||||||
|
'effective_rate': 100,
|
||||||
|
'response_time': 'Next business day',
|
||||||
|
'coverage': 'Business hours'
|
||||||
|
},
|
||||||
|
'standard': {
|
||||||
|
'name': 'Standard Support (MOST POPULAR)',
|
||||||
|
'price': 380,
|
||||||
|
'hours_included': 4,
|
||||||
|
'effective_rate': 95,
|
||||||
|
'response_time': '8-hour guarantee',
|
||||||
|
'coverage': 'Business hours'
|
||||||
|
},
|
||||||
|
'premium': {
|
||||||
|
'name': 'Premium Support',
|
||||||
|
'price': 540,
|
||||||
|
'hours_included': 6,
|
||||||
|
'effective_rate': 90,
|
||||||
|
'response_time': '4-hour guarantee',
|
||||||
|
'coverage': 'After-hours emergency'
|
||||||
|
},
|
||||||
|
'priority': {
|
||||||
|
'name': 'Priority Support',
|
||||||
|
'price': 850,
|
||||||
|
'hours_included': 10,
|
||||||
|
'effective_rate': 85,
|
||||||
|
'response_time': '2-hour guarantee',
|
||||||
|
'coverage': '24/7 emergency'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLOCK_TIME = {
|
||||||
|
'10hr': {'hours': 10, 'price': 1500, 'rate': 150},
|
||||||
|
'20hr': {'hours': 20, 'price': 2600, 'rate': 130},
|
||||||
|
'30hr': {'hours': 30, 'price': 3000, 'rate': 100}
|
||||||
|
}
|
||||||
|
|
||||||
|
OVERAGE_RATE = 175
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_equipment_pack(num_devices):
|
||||||
|
"""Calculate equipment pack pricing"""
|
||||||
|
if num_devices == 0:
|
||||||
|
return 0
|
||||||
|
if num_devices <= EQUIPMENT_PACK['base_devices']:
|
||||||
|
return EQUIPMENT_PACK['base_price']
|
||||||
|
else:
|
||||||
|
additional = num_devices - EQUIPMENT_PACK['base_devices']
|
||||||
|
return EQUIPMENT_PACK['base_price'] + (additional * EQUIPMENT_PACK['additional_device_price'])
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_gps_quote(endpoints, tier='pro', equipment_devices=0, support_plan=None, block_time=None):
|
||||||
|
"""
|
||||||
|
Calculate a complete GPS quote
|
||||||
|
|
||||||
|
Args:
|
||||||
|
endpoints: Number of endpoints
|
||||||
|
tier: GPS tier (basic, pro, advanced)
|
||||||
|
equipment_devices: Number of equipment devices
|
||||||
|
support_plan: Support plan key (essential, standard, premium, priority)
|
||||||
|
block_time: Block time key (10hr, 20hr, 30hr)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with pricing breakdown
|
||||||
|
"""
|
||||||
|
# GPS Monitoring
|
||||||
|
gps_tier_data = GPS_TIERS.get(tier, GPS_TIERS['pro'])
|
||||||
|
gps_cost = endpoints * gps_tier_data['price_per_endpoint']
|
||||||
|
|
||||||
|
# Equipment Pack
|
||||||
|
equipment_cost = calculate_equipment_pack(equipment_devices)
|
||||||
|
|
||||||
|
# Support Plan
|
||||||
|
support_cost = 0
|
||||||
|
support_hours = 0
|
||||||
|
support_data = None
|
||||||
|
if support_plan:
|
||||||
|
support_data = SUPPORT_PLANS.get(support_plan)
|
||||||
|
if support_data:
|
||||||
|
support_cost = support_data['price']
|
||||||
|
support_hours = support_data['hours_included']
|
||||||
|
|
||||||
|
# Block Time (one-time or as needed)
|
||||||
|
block_cost = 0
|
||||||
|
block_hours = 0
|
||||||
|
if block_time:
|
||||||
|
block_data = BLOCK_TIME.get(block_time)
|
||||||
|
if block_data:
|
||||||
|
block_cost = block_data['price']
|
||||||
|
block_hours = block_data['hours']
|
||||||
|
|
||||||
|
# Calculate totals
|
||||||
|
monthly_total = gps_cost + equipment_cost + support_cost
|
||||||
|
annual_total = monthly_total * 12
|
||||||
|
|
||||||
|
# Per endpoint cost
|
||||||
|
total_endpoints = endpoints + equipment_devices
|
||||||
|
per_endpoint_cost = monthly_total / total_endpoints if total_endpoints > 0 else 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
'gps': {
|
||||||
|
'tier': gps_tier_data['name'],
|
||||||
|
'endpoints': endpoints,
|
||||||
|
'price_per_endpoint': gps_tier_data['price_per_endpoint'],
|
||||||
|
'monthly_cost': gps_cost
|
||||||
|
},
|
||||||
|
'equipment': {
|
||||||
|
'devices': equipment_devices,
|
||||||
|
'monthly_cost': equipment_cost
|
||||||
|
},
|
||||||
|
'support': {
|
||||||
|
'plan': support_data['name'] if support_data else 'None',
|
||||||
|
'monthly_cost': support_cost,
|
||||||
|
'hours_included': support_hours,
|
||||||
|
'effective_rate': support_data['effective_rate'] if support_data else 0
|
||||||
|
},
|
||||||
|
'block_time': {
|
||||||
|
'hours': block_hours,
|
||||||
|
'cost': block_cost
|
||||||
|
},
|
||||||
|
'totals': {
|
||||||
|
'monthly': monthly_total,
|
||||||
|
'annual': annual_total,
|
||||||
|
'per_endpoint': round(per_endpoint_cost, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_quote(quote):
|
||||||
|
"""Print formatted quote"""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("GPS PRICING QUOTE")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
print(f"\nGPS Monitoring: {quote['gps']['tier']}")
|
||||||
|
print(f" {quote['gps']['endpoints']} endpoints × ${quote['gps']['price_per_endpoint']}/month = ${quote['gps']['monthly_cost']}")
|
||||||
|
|
||||||
|
if quote['equipment']['devices'] > 0:
|
||||||
|
print(f"\nEquipment Pack:")
|
||||||
|
print(f" {quote['equipment']['devices']} devices = ${quote['equipment']['monthly_cost']}/month")
|
||||||
|
|
||||||
|
if quote['support']['monthly_cost'] > 0:
|
||||||
|
print(f"\nSupport Plan: {quote['support']['plan']}")
|
||||||
|
print(f" ${quote['support']['monthly_cost']}/month ({quote['support']['hours_included']} hours included)")
|
||||||
|
print(f" Effective rate: ${quote['support']['effective_rate']}/hour")
|
||||||
|
|
||||||
|
if quote['block_time']['hours'] > 0:
|
||||||
|
print(f"\nPrepaid Block Time:")
|
||||||
|
print(f" {quote['block_time']['hours']} hours = ${quote['block_time']['cost']} (never expires)")
|
||||||
|
|
||||||
|
print("\n" + "-"*60)
|
||||||
|
print(f"MONTHLY TOTAL: ${quote['totals']['monthly']}")
|
||||||
|
print(f"ANNUAL TOTAL: ${quote['totals']['annual']}")
|
||||||
|
print(f"Per Endpoint/Device Cost: ${quote['totals']['per_endpoint']}/month")
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("GPS PRICING CALCULATOR - Arizona Computer Guru")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# Example 1: Small Office
|
||||||
|
print("\nExample 1: Small Office (10 endpoints + 4 devices)")
|
||||||
|
quote1 = calculate_gps_quote(
|
||||||
|
endpoints=10,
|
||||||
|
tier='pro',
|
||||||
|
equipment_devices=4,
|
||||||
|
support_plan='standard'
|
||||||
|
)
|
||||||
|
print_quote(quote1)
|
||||||
|
|
||||||
|
# Example 2: Growing Business
|
||||||
|
print("\nExample 2: Growing Business (22 endpoints)")
|
||||||
|
quote2 = calculate_gps_quote(
|
||||||
|
endpoints=22,
|
||||||
|
tier='pro',
|
||||||
|
support_plan='premium'
|
||||||
|
)
|
||||||
|
print_quote(quote2)
|
||||||
|
|
||||||
|
# Example 3: Established Company
|
||||||
|
print("\nExample 3: Established Company (42 endpoints)")
|
||||||
|
quote3 = calculate_gps_quote(
|
||||||
|
endpoints=42,
|
||||||
|
tier='pro',
|
||||||
|
support_plan='priority'
|
||||||
|
)
|
||||||
|
print_quote(quote3)
|
||||||
234
projects/msp-pricing/docs/gps-pricing-structure.md
Normal file
234
projects/msp-pricing/docs/gps-pricing-structure.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
# GPS Pricing Structure
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-01
|
||||||
|
**Source:** GPS_Price_Sheet_12.html
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GPS Endpoint Monitoring Tiers
|
||||||
|
|
||||||
|
### GPS-BASIC: Essential Protection
|
||||||
|
**Price:** $19/endpoint/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 24/7 System Monitoring & Alerting
|
||||||
|
- Automated Patch Management
|
||||||
|
- Remote Management & Support
|
||||||
|
- Endpoint Security (Antivirus)
|
||||||
|
- Monthly Health Reports
|
||||||
|
|
||||||
|
**Best For:** Small businesses with straightforward IT environments
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GPS-PRO: Business Protection ⭐ MOST POPULAR
|
||||||
|
**Price:** $26/endpoint/month
|
||||||
|
|
||||||
|
**Everything in GPS-Basic, PLUS:**
|
||||||
|
- **Advanced EDR** - Stops threats antivirus misses
|
||||||
|
- **Email Security** - Anti-phishing & spam filtering
|
||||||
|
- **Dark Web Monitoring** - Alerts if credentials compromised
|
||||||
|
- **Security Training** - Monthly phishing simulations
|
||||||
|
- **Cloud Monitoring** - Microsoft 365 & Google protection
|
||||||
|
|
||||||
|
**Best For:** Businesses handling customer data or requiring cyber insurance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GPS-ADVANCED: Maximum Protection
|
||||||
|
**Price:** $39/endpoint/month
|
||||||
|
|
||||||
|
**Everything in GPS-Pro, PLUS:**
|
||||||
|
- **Advanced Threat Intelligence** - Real-time global threat data
|
||||||
|
- **Ransomware Rollback** - Automatic recovery from attacks
|
||||||
|
- **Compliance Tools** - HIPAA, PCI-DSS, SOC 2 reporting
|
||||||
|
- **Priority Response** - Fast-tracked incident response
|
||||||
|
- **Enhanced SaaS Backup** - Complete M365/Google backup
|
||||||
|
|
||||||
|
**Best For:** Healthcare, legal, financial services, or businesses with sensitive data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GPS-Equipment Monitoring Pack
|
||||||
|
**Price:** $25/month (up to 10 devices) + $3 per additional device
|
||||||
|
|
||||||
|
**Covers:**
|
||||||
|
- Routers, switches, firewalls, printers, scanners, NAS, cameras, network equipment
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Basic uptime monitoring & alerting
|
||||||
|
- Devices eligible for Support Plan labor coverage
|
||||||
|
- Quick fixes under 10 minutes included
|
||||||
|
- Monthly equipment health reports
|
||||||
|
|
||||||
|
**Note:** Equipment Pack makes devices eligible for Support Plan hours. Block time covers any device regardless of enrollment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support Plans
|
||||||
|
|
||||||
|
### Essential Support
|
||||||
|
**Price:** $200/month
|
||||||
|
**Hours Included:** 2 hours
|
||||||
|
**Effective Rate:** $100/hour
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Next business day response
|
||||||
|
- Email & phone support
|
||||||
|
- Business hours coverage
|
||||||
|
|
||||||
|
**Best For:** Minimal IT issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Standard Support ⭐ MOST POPULAR
|
||||||
|
**Price:** $380/month
|
||||||
|
**Hours Included:** 4 hours
|
||||||
|
**Effective Rate:** $95/hour
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 8-hour response guarantee
|
||||||
|
- Priority phone support
|
||||||
|
- Business hours coverage
|
||||||
|
|
||||||
|
**Best For:** Regular IT needs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Premium Support
|
||||||
|
**Price:** $540/month
|
||||||
|
**Hours Included:** 6 hours
|
||||||
|
**Effective Rate:** $90/hour
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 4-hour response guarantee
|
||||||
|
- After-hours emergency support
|
||||||
|
- Extended coverage
|
||||||
|
|
||||||
|
**Best For:** Technology-dependent businesses
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Priority Support
|
||||||
|
**Price:** $850/month
|
||||||
|
**Hours Included:** 10 hours
|
||||||
|
**Effective Rate:** $85/hour
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 2-hour response guarantee
|
||||||
|
- 24/7 emergency support
|
||||||
|
- Dedicated account manager
|
||||||
|
|
||||||
|
**Best For:** Mission-critical operations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prepaid Block Time
|
||||||
|
|
||||||
|
**Non-expiring hours for projects or seasonal needs. Available to anyone.**
|
||||||
|
|
||||||
|
| Block Size | Price | Effective Rate | Expiration |
|
||||||
|
|-----------|---------|----------------|---------------|
|
||||||
|
| 10 hours | $1,500 | $150/hour | Never expires |
|
||||||
|
| 20 hours | $2,600 | $130/hour | Never expires |
|
||||||
|
| 30 hours | $3,000 | $100/hour | Never expires |
|
||||||
|
|
||||||
|
**Note:** Block time can be purchased by anyone and used alongside a Support Plan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Labor Hour Usage Priority
|
||||||
|
|
||||||
|
1. **Support plan hours** used first each month
|
||||||
|
2. **Prepaid block time** hours used next
|
||||||
|
3. **Overage** - $175/hour
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Coverage Scope
|
||||||
|
|
||||||
|
**Support Plan Hours Apply To:**
|
||||||
|
- GPS-enrolled endpoints
|
||||||
|
- Enrolled websites
|
||||||
|
- Devices in Equipment Pack
|
||||||
|
|
||||||
|
**Block Time Applies To:**
|
||||||
|
- Any device or service (regardless of enrollment)
|
||||||
|
|
||||||
|
**Quick Fixes:**
|
||||||
|
- Under 10 minutes = included in monitoring fees
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pricing Examples
|
||||||
|
|
||||||
|
### Example 1: Small Office (10 endpoints + 4 devices)
|
||||||
|
**Recommended:** GPS-Pro + Equipment Pack + Standard Support
|
||||||
|
|
||||||
|
```
|
||||||
|
GPS-Pro Monitoring (10 × $26) $260
|
||||||
|
Equipment Pack (4 devices) $25
|
||||||
|
Standard Support (4 hrs included) $380
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $665
|
||||||
|
```
|
||||||
|
|
||||||
|
**Includes:** All computers + network gear covered • 4 hours labor • 8-hour response
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 2: Growing Business (22 endpoints)
|
||||||
|
**Recommended:** GPS-Pro + Premium Support
|
||||||
|
|
||||||
|
```
|
||||||
|
GPS-Pro Monitoring (22 × $26) $572
|
||||||
|
Premium Support (6 hrs included) $540
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $1,112
|
||||||
|
```
|
||||||
|
|
||||||
|
**Per Endpoint Cost:** $51/endpoint
|
||||||
|
**Includes:** 6 hours labor • 4-hour response • After-hours emergency
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 3: Established Company (42 endpoints)
|
||||||
|
**Recommended:** GPS-Pro + Priority Support
|
||||||
|
|
||||||
|
```
|
||||||
|
GPS-Pro Monitoring (42 × $26) $1,092
|
||||||
|
Priority Support (10 hrs included) $850
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $1,942
|
||||||
|
```
|
||||||
|
|
||||||
|
**Per Endpoint Cost:** $46/endpoint
|
||||||
|
**Includes:** 10 hours labor • 2-hour response • 24/7 emergency
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Volume Discounts
|
||||||
|
|
||||||
|
Contact for custom pricing on larger deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## New Client Special Offer
|
||||||
|
|
||||||
|
**Sign up within 30 days:**
|
||||||
|
- ✓ Waived setup fees
|
||||||
|
- ✓ First month 50% off support plans
|
||||||
|
- ✓ Free security assessment ($500 value)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
**Phone:** 520.304.8300
|
||||||
|
**Email:** mike@azcomputerguru.com
|
||||||
|
**Website:** azcomputerguru.com
|
||||||
|
**Address:** 7437 E. 22nd St, Tucson, AZ 85710
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Protecting Tucson Businesses Since 2001**
|
||||||
354
projects/msp-pricing/docs/web-email-hosting-pricing.md
Normal file
354
projects/msp-pricing/docs/web-email-hosting-pricing.md
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
# Web & Email Hosting Pricing Structure
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-01
|
||||||
|
**Source:** MSP Pricing Chat - Web/Email Hosting Discussion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Web Hosting Plans
|
||||||
|
|
||||||
|
### Starter Hosting
|
||||||
|
**Price:** $15/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 5GB storage
|
||||||
|
- 1 website
|
||||||
|
- Unmetered bandwidth
|
||||||
|
- Free SSL certificate
|
||||||
|
- Daily backups
|
||||||
|
- Email accounts included
|
||||||
|
- cPanel control panel
|
||||||
|
|
||||||
|
**Best For:** Personal sites, small portfolios, landing pages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Business Hosting ⭐ MOST POPULAR
|
||||||
|
**Price:** $35/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 25GB storage
|
||||||
|
- 5 websites
|
||||||
|
- WordPress optimized
|
||||||
|
- Staging environment
|
||||||
|
- Performance optimization
|
||||||
|
- Advanced caching
|
||||||
|
- Priority support
|
||||||
|
|
||||||
|
**Best For:** Growing businesses, WordPress sites, multiple projects
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Commerce Hosting
|
||||||
|
**Price:** $65/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 50GB storage
|
||||||
|
- Unlimited websites
|
||||||
|
- E-commerce optimized
|
||||||
|
- Dedicated IP
|
||||||
|
- Advanced security
|
||||||
|
- PCI compliance tools
|
||||||
|
- Priority 24/7 support
|
||||||
|
|
||||||
|
**Best For:** Online stores, high-traffic sites, mission-critical websites
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Email Hosting
|
||||||
|
|
||||||
|
### WHM Email (IMAP/POP)
|
||||||
|
**Base Price:** $2/mailbox/month
|
||||||
|
**Included Storage:** 5GB per mailbox
|
||||||
|
**Additional Storage:** $2 per 5GB block
|
||||||
|
|
||||||
|
**Pre-Configured Packages:**
|
||||||
|
| Package | Storage | Monthly Price | Effective $/GB |
|
||||||
|
|-------------|---------|---------------|----------------|
|
||||||
|
| Basic | 5GB | $2 | $0.40 |
|
||||||
|
| Standard | 10GB | $4 | $0.40 |
|
||||||
|
| Professional| 25GB | $10 | $0.40 |
|
||||||
|
| Enterprise | 50GB | $20 | $0.40 |
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- IMAP/POP3/SMTP access
|
||||||
|
- Webmail interface
|
||||||
|
- Basic spam filtering
|
||||||
|
- Daily backups
|
||||||
|
- Hard quota per mailbox (mail still delivered over quota)
|
||||||
|
|
||||||
|
**Best For:**
|
||||||
|
- IMAP/POP users
|
||||||
|
- Outlook & Thunderbird clients
|
||||||
|
- Budget-conscious teams
|
||||||
|
- Legacy app compatibility
|
||||||
|
|
||||||
|
**Policy Notes:**
|
||||||
|
- Hard quota per mailbox (not pooled)
|
||||||
|
- Mail still delivered over quota (no bouncing)
|
||||||
|
- Client notified when approaching/exceeding quota
|
||||||
|
- Billing adjusted when storage block added
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Microsoft 365 Business Basic
|
||||||
|
**Price:** $7/user/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 50GB mailbox
|
||||||
|
- Web & mobile apps (no desktop)
|
||||||
|
- Teams, OneDrive (1TB)
|
||||||
|
- SharePoint, Exchange Online
|
||||||
|
- Basic security
|
||||||
|
|
||||||
|
**Best For:** Cloud-first teams, mobile users, collaboration-focused
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Microsoft 365 Business Standard ⭐ MOST POPULAR
|
||||||
|
**Price:** $14/user/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Everything in Business Basic, PLUS:
|
||||||
|
- Desktop Office apps (Word, Excel, PowerPoint, Outlook)
|
||||||
|
- Outlook desktop client
|
||||||
|
- Advanced collaboration
|
||||||
|
- Business-class email
|
||||||
|
|
||||||
|
**Best For:** Most businesses, teams needing full Office suite
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Microsoft 365 Business Premium
|
||||||
|
**Price:** $24/user/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Everything in Business Standard, PLUS:
|
||||||
|
- Advanced security & compliance
|
||||||
|
- Microsoft Defender
|
||||||
|
- Intune device management
|
||||||
|
- Information protection
|
||||||
|
- Conditional access
|
||||||
|
|
||||||
|
**Best For:** Compliance-heavy industries (legal, healthcare, finance)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Exchange Online Plan 1
|
||||||
|
**Price:** $5/user/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- 50GB mailbox
|
||||||
|
- Email only (no Office apps)
|
||||||
|
- Outlook desktop compatible
|
||||||
|
- Basic archiving
|
||||||
|
|
||||||
|
**Best For:** Email-only users who don't need Office apps or collaboration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Email Security Add-On
|
||||||
|
|
||||||
|
### Email Security & Filtering
|
||||||
|
**Price:** $3/mailbox/month
|
||||||
|
|
||||||
|
**Platforms:** MailProtector (Emailservice.io) / INKY (via Kaseya)
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Anti-phishing protection
|
||||||
|
- Advanced spam filtering
|
||||||
|
- Outbound mail filtering
|
||||||
|
- DLP-style scanning
|
||||||
|
- Approval workflows for sensitive content
|
||||||
|
- Real-time threat detection
|
||||||
|
|
||||||
|
**Coverage:**
|
||||||
|
- Inbound protection
|
||||||
|
- Outbound protection
|
||||||
|
- Works with WHM Email or M365
|
||||||
|
|
||||||
|
**Recommendation:** Recommended for all WHM email users
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Add-On Services
|
||||||
|
|
||||||
|
### Additional Storage
|
||||||
|
| Service | Price | Notes |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| Email Storage (per 5GB block) | $2/month | Per mailbox, WHM only |
|
||||||
|
| Web Storage (per 10GB) | $5/month | Web hosting expansion |
|
||||||
|
|
||||||
|
### Domain Services
|
||||||
|
| Service | Price | Notes |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| Domain Registration | $15/year | .com/.net/.org |
|
||||||
|
| Domain Transfer | Free | With hosting |
|
||||||
|
| Private Registration | $12/year | WHOIS privacy |
|
||||||
|
|
||||||
|
### Migration Services
|
||||||
|
| Service | Price | Notes |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| Email Migration | $50/mailbox | One-time |
|
||||||
|
| Website Migration | $100/site | One-time |
|
||||||
|
|
||||||
|
### Premium Services
|
||||||
|
| Service | Price | Notes |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| Dedicated IP | $5/month | E-commerce, SSL |
|
||||||
|
| SSL Certificate (Premium) | $75/year | EV or wildcard |
|
||||||
|
| Daily Offsite Backup | $10/month | Enhanced retention |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Industry Recommendations
|
||||||
|
|
||||||
|
| Business Type | Recommended Package |
|
||||||
|
|--------------|---------------------|
|
||||||
|
| Startup/Solo | Starter Hosting + WHM Email ($2+) |
|
||||||
|
| Small Business | Business Hosting + M365 Business Basic |
|
||||||
|
| Growing Business | Business Hosting + M365 Business Standard |
|
||||||
|
| E-commerce | Commerce Hosting + M365 Business Standard |
|
||||||
|
| Healthcare/Legal | Commerce Hosting + M365 Business Premium |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pricing Examples
|
||||||
|
|
||||||
|
### Example 1: Small Office (5 users, basic needs)
|
||||||
|
**Web Hosting + Budget Email**
|
||||||
|
|
||||||
|
```
|
||||||
|
Business Hosting $35
|
||||||
|
WHM Email 10GB (5 × $4) $20
|
||||||
|
Email Security (5 × $3) $15
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $70
|
||||||
|
```
|
||||||
|
|
||||||
|
**Includes:** Website + secure IMAP email for Outlook/Thunderbird users
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 2: Budget-Conscious Office (8 users)
|
||||||
|
**Full Website + Secure Email**
|
||||||
|
|
||||||
|
```
|
||||||
|
Business Hosting $35
|
||||||
|
WHM Email 10GB (8 × $4) $32
|
||||||
|
Email Security (8 × $3) $24
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $91
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefit:** Full website + secure IMAP email for teams using Outlook/Thunderbird
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 3: Modern Small Business (10 users)
|
||||||
|
**Web + Microsoft 365 Standard**
|
||||||
|
|
||||||
|
```
|
||||||
|
Business Hosting $35
|
||||||
|
M365 Business Standard (10 × $14) $140
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $175
|
||||||
|
```
|
||||||
|
|
||||||
|
**Includes:** Website + full Office suite + 1TB OneDrive + Teams collaboration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 4: E-Commerce Store (15 users)
|
||||||
|
**Commerce Hosting + M365**
|
||||||
|
|
||||||
|
```
|
||||||
|
Commerce Hosting $65
|
||||||
|
M365 Business Standard (15 × $14) $210
|
||||||
|
Dedicated IP $5
|
||||||
|
----------------------------------------
|
||||||
|
Total Monthly: $280
|
||||||
|
```
|
||||||
|
|
||||||
|
**Includes:** E-commerce optimized hosting + full Office suite + PCI compliance tools
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storage Overage Scenarios
|
||||||
|
|
||||||
|
### 200GB Email Abuser - Migration Path
|
||||||
|
|
||||||
|
**Old "Unlimited" Plan:** ~$20/month total
|
||||||
|
**New Structure Options:**
|
||||||
|
|
||||||
|
| Scenario | Configuration | Monthly Cost | Increase |
|
||||||
|
|----------|--------------|--------------|----------|
|
||||||
|
| 10 mailboxes, 20GB avg each | 10 × $8 (20GB) | $80 | 4x |
|
||||||
|
| 5 mailboxes, 40GB avg each | 5 × $16 (40GB) | $80 | 4x |
|
||||||
|
| 1 mega-box, 200GB | 1 × $80 (200GB) | $80 | 4x |
|
||||||
|
|
||||||
|
**Migration Strategy:**
|
||||||
|
1. 60-90 day notice before billing kicks in
|
||||||
|
2. Offer one-time mailbox cleanup service
|
||||||
|
3. Suggest migration to M365 for heavy users
|
||||||
|
4. Provide reporting on current usage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## National Pricing Research Summary
|
||||||
|
|
||||||
|
### Web Hosting Market Rates
|
||||||
|
- **Shared Hosting:** $3-15/month (basic plans)
|
||||||
|
- **Managed WordPress:** $4-30/month
|
||||||
|
- **VPS Hosting:** $20-100/month
|
||||||
|
- **Dedicated Hosting:** $80-500/month
|
||||||
|
|
||||||
|
### Email Hosting Market Rates
|
||||||
|
- **Microsoft 365:** $1-30/user/month (depending on plan)
|
||||||
|
- **Hosted Exchange:** $0-30/mailbox/month (average $12)
|
||||||
|
- **Basic Email:** $2-10/mailbox/month
|
||||||
|
|
||||||
|
### Web Development Market Rates
|
||||||
|
- **Freelance Developers:** $16.83-72.12/hour (avg $45.12)
|
||||||
|
- **Professional Agencies:** $60-120/hour
|
||||||
|
- **Small Business Website:** $5,000-10,000 (up to $20,000+ for complex)
|
||||||
|
- **Website Maintenance:** $35-500/month (small/medium), $300-2,500/month (complex)
|
||||||
|
|
||||||
|
### ACG Position
|
||||||
|
- **Hourly Rate:** $130-165/hour (in line with professional MSP/agency rates)
|
||||||
|
- **GPS Support Plans:** $85-100/hour effective (significant value)
|
||||||
|
- **Web Hosting:** Competitive with managed/specialty hosts
|
||||||
|
- **Email Hosting:** Budget-friendly alternative to M365 for IMAP users
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Policy Notes
|
||||||
|
|
||||||
|
### WHM Email
|
||||||
|
- Hard quotas enforced per mailbox (not pooled)
|
||||||
|
- Mail continues to be delivered over quota (no bouncing - customer-friendly)
|
||||||
|
- Notifications sent when approaching/exceeding quota
|
||||||
|
- Automatic billing adjustment when storage blocks added
|
||||||
|
|
||||||
|
### Microsoft 365
|
||||||
|
- Billed through Microsoft CSP program
|
||||||
|
- Standard Microsoft terms apply
|
||||||
|
- Migration assistance included
|
||||||
|
|
||||||
|
### Discontinued Services
|
||||||
|
- **In-house Exchange Server:** Discontinued due to security risks
|
||||||
|
- **Recommendation:** M365 for new Exchange deployments
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
**Phone:** 520.304.8300
|
||||||
|
**Email:** mike@azcomputerguru.com
|
||||||
|
**Website:** azcomputerguru.com
|
||||||
|
**Address:** 7437 E. 22nd St, Tucson, AZ 85710
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-01
|
||||||
|
**Protecting Tucson Businesses Since 2001**
|
||||||
223
projects/msp-pricing/session-logs/2026-02-01-project-import.md
Normal file
223
projects/msp-pricing/session-logs/2026-02-01-project-import.md
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
# MSP Pricing Project Import Session
|
||||||
|
|
||||||
|
**Date:** 2026-02-01
|
||||||
|
**Session:** Project creation and web/email hosting import
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Imported complete MSP pricing structure from web version of Claude project, including:
|
||||||
|
- GPS Endpoint Monitoring pricing
|
||||||
|
- Support Plans
|
||||||
|
- Web Hosting packages
|
||||||
|
- Email Hosting (WHM and M365)
|
||||||
|
- Email Security add-ons
|
||||||
|
- National pricing research
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- `GPS_Price_Sheet_12.html` - 4-page GPS pricing document (HTML)
|
||||||
|
- `docs/gps-pricing-structure.md` - Structured GPS pricing data
|
||||||
|
- `docs/web-email-hosting-pricing.md` - Complete web/email hosting pricing
|
||||||
|
|
||||||
|
### Calculators
|
||||||
|
- `calculators/gps-calculator.py` - GPS-only pricing calculator
|
||||||
|
- `calculators/complete-pricing-calculator.py` - Full pricing calculator (GPS + Web + Email)
|
||||||
|
|
||||||
|
### Project Files
|
||||||
|
- `README.md` - Project overview and quick start guide
|
||||||
|
- `session-logs/2026-02-01-project-import.md` - This session log
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pricing Structure Imported
|
||||||
|
|
||||||
|
### GPS Endpoint Monitoring
|
||||||
|
**Tiers:**
|
||||||
|
- GPS-BASIC: $19/endpoint/month
|
||||||
|
- GPS-PRO: $26/endpoint/month (most popular)
|
||||||
|
- GPS-ADVANCED: $39/endpoint/month
|
||||||
|
- Equipment Pack: $25/month (up to 10 devices)
|
||||||
|
|
||||||
|
**Support Plans:**
|
||||||
|
- Essential: $200/month (2 hours included)
|
||||||
|
- Standard: $380/month (4 hours included) - most popular
|
||||||
|
- Premium: $540/month (6 hours included)
|
||||||
|
- Priority: $850/month (10 hours included)
|
||||||
|
|
||||||
|
**Block Time:**
|
||||||
|
- 10 hours: $1,500 (never expires)
|
||||||
|
- 20 hours: $2,600 (never expires)
|
||||||
|
- 30 hours: $3,000 (never expires)
|
||||||
|
|
||||||
|
### Web Hosting
|
||||||
|
- Starter: $15/month (5GB, 1 website)
|
||||||
|
- Business: $35/month (25GB, 5 websites) - most popular
|
||||||
|
- Commerce: $65/month (50GB, unlimited websites)
|
||||||
|
|
||||||
|
### Email Hosting
|
||||||
|
|
||||||
|
**WHM Email:**
|
||||||
|
- Base: $2/mailbox/month (5GB included)
|
||||||
|
- Storage: +$2 per 5GB block
|
||||||
|
- Pre-configured packages:
|
||||||
|
- 5GB: $2/month
|
||||||
|
- 10GB: $4/month
|
||||||
|
- 25GB: $10/month
|
||||||
|
- 50GB: $20/month
|
||||||
|
|
||||||
|
**Microsoft 365:**
|
||||||
|
- Business Basic: $7/user/month
|
||||||
|
- Business Standard: $14/user/month (most popular)
|
||||||
|
- Business Premium: $24/user/month
|
||||||
|
- Exchange Online: $5/user/month
|
||||||
|
|
||||||
|
**Email Security Add-on:**
|
||||||
|
- $3/mailbox/month (MailProtector/INKY)
|
||||||
|
- Works with WHM or M365
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Decisions from Web Chat
|
||||||
|
|
||||||
|
### WHM Email Storage Overages
|
||||||
|
**Problem:** Legacy "unlimited" clients with 200+ GB of email
|
||||||
|
**Solution:** Fair storage pricing structure
|
||||||
|
- $2 base + 5GB included
|
||||||
|
- $2 per 5GB block
|
||||||
|
- Hard quota per mailbox (mail still delivered over quota)
|
||||||
|
- 60-90 day notice before billing changes
|
||||||
|
|
||||||
|
**Example:** 200GB user = $80/month (vs $20 old "unlimited")
|
||||||
|
|
||||||
|
### Email Security
|
||||||
|
**Platforms:**
|
||||||
|
- Currently: MailProtector (Emailservice.io)
|
||||||
|
- Migrating to: INKY (via Kaseya bundle)
|
||||||
|
- Both offer inbound + outbound filtering
|
||||||
|
|
||||||
|
### Discontinued Services
|
||||||
|
- In-house Exchange Server (security risks)
|
||||||
|
- Recommendation: M365 for Exchange needs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## National Pricing Research
|
||||||
|
|
||||||
|
### Market Rates (from web chat research)
|
||||||
|
**Web Hosting:**
|
||||||
|
- Shared: $3-15/month
|
||||||
|
- Managed WordPress: $4-30/month
|
||||||
|
- VPS: $20-100/month
|
||||||
|
- Dedicated: $80-500/month
|
||||||
|
|
||||||
|
**Email Hosting:**
|
||||||
|
- M365: $1-30/user/month
|
||||||
|
- Hosted Exchange: $0-30/mailbox (avg $12)
|
||||||
|
- Basic email: $2-10/mailbox
|
||||||
|
|
||||||
|
**Web Development:**
|
||||||
|
- Freelance: $16.83-72.12/hour (avg $45.12)
|
||||||
|
- Professional agencies: $60-120/hour
|
||||||
|
- Small business website: $5,000-10,000
|
||||||
|
- Website maintenance: $35-500/month
|
||||||
|
|
||||||
|
### ACG Competitive Position
|
||||||
|
- Hourly rate: $130-165/hour (in line with professional MSP rates)
|
||||||
|
- GPS Support effective rates: $85-100/hour (excellent value)
|
||||||
|
- Web/email hosting: Competitive with specialty managed hosts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Calculator Features
|
||||||
|
|
||||||
|
### GPS Calculator (`gps-calculator.py`)
|
||||||
|
- Calculate GPS quotes with endpoints, tiers, equipment, support
|
||||||
|
- Print formatted quotes
|
||||||
|
- Example scenarios included
|
||||||
|
|
||||||
|
### Complete Calculator (`complete-pricing-calculator.py`)
|
||||||
|
**Calculates:**
|
||||||
|
- GPS endpoint monitoring + support
|
||||||
|
- Web hosting (all tiers)
|
||||||
|
- Email hosting (WHM or M365)
|
||||||
|
- Email security add-on
|
||||||
|
- Additional services (dedicated IP, SSL, backups)
|
||||||
|
|
||||||
|
**Functions:**
|
||||||
|
- `calculate_whm_email()` - WHM email with storage blocks
|
||||||
|
- `calculate_m365_email()` - M365 packages
|
||||||
|
- `calculate_web_hosting()` - Web hosting tiers
|
||||||
|
- `calculate_complete_quote()` - Full integrated quote
|
||||||
|
- `print_complete_quote()` - Formatted output
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Scenarios Documented
|
||||||
|
|
||||||
|
### Small Office
|
||||||
|
- 10 GPS-Pro endpoints
|
||||||
|
- Business web hosting
|
||||||
|
- 5 WHM email (10GB + security)
|
||||||
|
- Standard support
|
||||||
|
- **Total: ~$455/month**
|
||||||
|
|
||||||
|
### Modern Business
|
||||||
|
- 22 GPS-Pro endpoints
|
||||||
|
- Business web hosting
|
||||||
|
- 15 M365 Business Standard
|
||||||
|
- Premium support
|
||||||
|
- **Total: ~$1,387/month**
|
||||||
|
|
||||||
|
### E-Commerce
|
||||||
|
- 42 GPS-Pro endpoints
|
||||||
|
- Commerce web hosting
|
||||||
|
- 20 M365 Business Standard
|
||||||
|
- Priority support
|
||||||
|
- Dedicated IP + Premium SSL
|
||||||
|
- **Total: ~$3,218/month**
|
||||||
|
|
||||||
|
### Web/Email Only
|
||||||
|
- Business web hosting
|
||||||
|
- 8 WHM email (10GB + security)
|
||||||
|
- **Total: $91/month**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps (TODO)
|
||||||
|
|
||||||
|
- [ ] Create printable quote templates (Word/PDF)
|
||||||
|
- [ ] Add competitor comparison calculator
|
||||||
|
- [ ] Create ROI calculator for prospects
|
||||||
|
- [ ] Add internal margin calculator
|
||||||
|
- [ ] Build customer-facing web calculator
|
||||||
|
- [ ] Import any additional rate sheets from web chat
|
||||||
|
- [ ] Create proposal templates
|
||||||
|
- [ ] Add cost-of-breach calculator for security justification
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources Imported
|
||||||
|
|
||||||
|
**From Web Chat:**
|
||||||
|
- GPS pricing research and discussion
|
||||||
|
- Web/email hosting rate sheet development
|
||||||
|
- Storage overage pricing strategy
|
||||||
|
- Email security add-on pricing
|
||||||
|
- National market rate research
|
||||||
|
- Industry recommendations
|
||||||
|
|
||||||
|
**Created:**
|
||||||
|
- Comprehensive pricing documentation
|
||||||
|
- Working Python calculators
|
||||||
|
- Project structure for ongoing development
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Session Complete:** 2026-02-01
|
||||||
|
**Status:** Project successfully imported and organized
|
||||||
|
**Location:** `D:\ClaudeTools\projects\msp-pricing\`
|
||||||
Reference in New Issue
Block a user