If you have a corporate password manager, keeping track of who's actually using your security tools becomes crucial.
I needed a way to monitor user activity and identify potential security risks from inactive accounts. Here's how I built an automated solution using 1Password CLI and PowerShell to generate professional email reports.
The Challenge
I thought it would be a good idea to get regular visibility into:
- Which users haven't logged into 1Password recently
- Automated reporting without manual data collection
The built-in 1Password web interface provides some reporting, but I needed something more automated and customizable for our specific risk categories.
Visual Results
This is the main header of the email with the overview:
Then you have all the users broken down with "risk" as below:
Part 1: Extracting Data with 1Password CLI
Understanding the Data Structure
The first step was understanding what data 1Password CLI actually provides. You need to authenticate first, then the key insight is that op user list
doesn't include login timestamps - you need to query individual users.
# First, authenticate to 1Password
eval $(op signin)
# This gives you basic user info but NO login data
op user list --format=json
# This gives you detailed user info INCLUDING last_auth_at
op user get "user@croucher.cloud" --format=json
When I ran op user get
on a single user, I discovered the last_auth_at
field:
{
"id": "K4KF334T35F3JBAO4DTUOZXXA4",
"name": "Lee Croucher",
"email": “Lee.Croucher@croucher.cloud",
"type": "MEMBER",
"state": "ACTIVE",
"created_at": "2024-06-20T15:39:37Z",
"updated_at": "2024-07-19T09:06:54Z",
"last_auth_at": "2024-07-15T08:24:20Z"
}
Building the Data Collection Script
The script follows a simple process:
- Authenticate to 1Password
- Get all user emails from the user list
- Query each user individually to get their authentication data
#!/bin/bash
# Step 1: Authenticate
eval $(op signin)
# Step 2: Get user list and extract emails
users=$(op user list --format=json)
mapfile -t emails < <(echo "$users" | sed -n 's/.*"email"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
# Step 3: Process each user
for email in "${emails[@]}"; do
echo "Processing: $email"
user_data=$(op user get "$email" --format=json)
last_auth=$(echo "$user_data" | sed -n 's/.*"last_auth_at"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
# Calculate days since last login
if [[ -z "$last_auth" ]]; then
last_auth="Never"
days_since="Never"
else
current_date=$(date +%s)
last_date=$(date -d "$last_auth" +%s)
days_since=$(( (current_date - last_date) / 86400 ))
fi
# Write to CSV
echo "\"$email\",\"$last_auth\",\"$days_since\"" >> "$OUTPUT_FILE"
done
Part 2: Professional Email Reporting with PowerShell
I thought up four risk categories based on login patterns:
- Low Risk (< 15 days): Active users, no immediate concern
- Medium Risk (15-60 days): May need a gentle reminder
- High Risk (61-100 days): Potential security concern - where were the passwords being stored?
- Not Using 1Password (101+ days/Never): High security risk
Reading and Processing the Data
The PowerShell script automatically finds the latest CSV file and processes it:
# Find the latest CSV file
$csvFiles = Get-ChildItem -Path $Path -Filter "user_login_report_*.csv" | Sort-Object LastWriteTime -Descending
$latestCSV = $csvFiles[0]
# Read and enhance the data with risk assessment
$userData = Import-Csv -Path $latestCSV.FullName
$processedData = $userData | ForEach-Object {
$risk = Get-RiskLevel -DaysString $_.'Days Since Last Login'
[PSCustomObject]@{
Email = $_.Email
'Last Authentication' = $_.'Last Authentication'
'Days Since Last Login' = $_.'Days Since Last Login'
'Risk Level' = $risk
'Risk Color' = Get-RiskColor -Risk $risk
}
}
Creating Professional HTML Reports
Rather than plain text emails, I would choose to generate HTML reports with:
Executive Summary Table:
$summaryTable = @"
<table style="border-collapse: collapse; width: 100%;">
<tr>
<td style="background-color: #28a745; color: white;">Low</td>
<td>$lowRisk users</td>
<td>$([math]::Round(($lowRisk / $totalUsers) * 100, 1))%</td>
</tr>
<!-- More rows for other risk levels -->
</table>
"@
Detailed User Table: Shows every user with color-coded risk levels, sorted with highest risk first.
Running the Complete Solution
I typically run both scripts in sequence:
# Generate the data (authentication happens within the script)
./onepassword_report.sh
# Send the email report
./Send-1PasswordReport.ps1 -SMTPServer "mail.croucher.cloud" -FromEmail "security@croucher.cloud" -ToEmail "team@croucher.cloud"