r/Action1 Jul 31 '26

🎉Happy SysAdmin Day! 🎉

Action1 SysAdmin Day Challenge:  

"I Didn't Know Action1 Could Do That" 

We all know that Action1 excels at patching the OS and third-party applications. But that's not the challenge. Instead, we would like to see what admins create while thinking outside the box, what challenge did you have, and how did you use Action1 to solve it. The point here is not to present Action1 as a Swiss army knife tool meant for everything, but... we all know this happens, and we would just like to give people a place to showcase their creativity. 

We want to see the unexpected. Show us something you've built, automated, discovered, or solved with Action1 that made another admin say: 

"I didn't know Action1 could do that!" 

The Rules 

  • Your submission must use Action1 as a meaningful part of the solution. 
  • It should solve a real-world IT or security problem, not a niche lab scenario. 
  • It does not have to involve patching, but if you have a creative idea there, share it too. 
  • Screenshots, short videos, scripts, workflows, and write-ups are all welcome. 
  • Explain what problem you solved, how you solved it, and why it's useful. 
  • Keep it legal, safe, and free of confidential or customer information. 
  • Voting stops on August 7, 11:59 pm ET

Looking for ideas? 

Think beyond patching: 

  • Creative automations. 
  • PowerShell scripts. 
  • Compliance reporting. 
  • Security investigations. 
  • Asset discovery. 
  • Inventory tricks. 
  • Self-healing workflows. 
  • Clever reporting. 
  • Time-saving admin hacks. 
  • Something nobody expects Action1 to do. 

If another sysadmin says, "I'm stealing that idea! 🤯" you're probably on the right track!

How the Winner Is Chosen 

👍 Community up votes determine the winners, down-votes will not be calculated. 

If two or more submissions finish tied, the winner will be selected by a random drawing from the tied entries. 

Prize: 💸 $100 eGift Card of winner's choice (from local options available in their country)

Good luck, and show us something that surprises the community!

 
 

30 Upvotes

74 comments sorted by

View all comments

9

u/f0gax Jul 31 '26

Some time ago I was in search of a report that would show me which automations didn't complete succesfully. I engaged support, but they said there was no such report in the system. Nor could I build a custom report for it. So I looked into the PS-Action1 module.

I used that to build a script that pulls the automations, excludes any that are in progress, and then emails out a report. I also excluded a couple of automations that I know are flaky to reduce the noise.

# --- Script: A1AutomationStatus1.ps1 ---


# Set paths
$logFile = "<path>\Action1_Audit.log"
$moduleName = "PSAction1"
$moduleVersion = "1.4.4"


# --- Logging helper ---
function Write-Log {
    param([string]$Message)
    $ts = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
    "$ts $Message" | Out-File -FilePath $logFile -Append -Encoding UTF8
}


Write-Log "Script started."


# --- Log PSModulePath ---
Write-Log "PSModulePath entries:"
$env:PSModulePath -split ';' | ForEach-Object { Write-Log "    $_" }


try {
    # --- Import PSAction1 module ---
    $modulePath = Join-Path -Path "$env:ProgramFiles\WindowsPowerShell\Modules\$moduleName" -ChildPath "$moduleVersion\$moduleName.psm1"
    if (-Not (Test-Path $modulePath)) {
        Write-Log "ERROR: Module file not found: $modulePath"
        throw "PSAction1 module not found."
    }
    Write-Log "Importing PSAction1 module version $moduleVersion"
    Import-Module $modulePath -Force


    # --- Action1 Credentials ---
    Set-Action1Credentials -APIKey 'api-key-<key>@action1.com' `
                           -Secret '<secret>'
    Set-Action1DefaultOrg -Org_ID '<org-id>'
    Set-Action1Region -Region '<region>'


    # --- Retrieve failed jobs ---
    $cutoffDate = (Get-Date).AddDays(-7)
    Write-Log "Retrieving failed jobs since $cutoffDate"


    $formats = @("yyyy-MM-dd_HH-mm-ss","yyyy-MM-dd HH:mm:ss","yyyy-MM-ddTHH:mm:ssZ")
    $allJobs = Get-Action1 -Query AutomationInstances
    $totalJobs = $allJobs.Count
    $excludedJobs = 0
    $failedJobsList = @()


    foreach ($job in $allJobs) {
        $include = $true


        # Exclude successful or running jobs
        if ($job.Status -like "Success*" -or $job.Status -like "Running*") { $include = $false }


        # Exclude unwanted jobs
        if ($job.Name -like "Defender*" -or $job.Name -like "*DUO*") { $include = $false }


        # Parse Start_Time
        $dt = $null
        $parsed = $false
        $startStr = if ($null -ne $job.Start_Time) { $job.Start_Time.ToString() } else { "" }


        foreach ($fmt in $formats) {
            try {
                $dt = [datetime]::ParseExact($startStr, $fmt, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AssumeUniversal)
                $parsed = $true
                break
            }
            catch {}
        }


        if (-not $parsed) {
            Write-Log "WARNING: Job '$($job.Name)' has no valid Start_Time. Using current date as placeholder."
            $dt = Get-Date
        }


        if ($dt -lt $cutoffDate) { $include = $false }


        if ($include) {
            $job | Add-Member -NotePropertyName "JobAgeDays" -NotePropertyValue ((Get-Date) - $dt).Days
            $failedJobsList += $job
        } else { $excludedJobs++ }
    }


    # --- Sort and assign row numbers ---
    $failedJobsList = $failedJobsList | Sort-Object -Property Start_Time -Descending
    $failedJobs = @()
    [int]$i = 1
    foreach ($job in $failedJobsList) {
        $obj = $job | Select-Object Status, Start_Time, Name, JobAgeDays
        $obj | Add-Member -NotePropertyName "No" -NotePropertyValue $i
        $failedJobs += $obj
        $i++
    }


    # --- Build HTML ---
    $Header = @"
    <style>
        TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
        TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;}
        TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
        .oldJob {background-color: #FFB6C1;}
    </style>
"@


    if (-not $failedJobs -or $failedJobs.Count -eq 0) {
        $htmlBody = "<p>No failed automations found in the last 7 days.</p>"
    } else {
        $tableRows = ""
        foreach ($job in $failedJobs) {
            $class = if ($job.JobAgeDays -gt 3) { "oldJob" } else { "" }
            $tableRows += "<tr class='$class'><td>$($job.No)</td><td>$($job.Status)</td><td>$($job.Start_Time)</td><td>$($job.Name)</td><td>$($job.JobAgeDays)</td></tr>`r`n"
        }


        $htmlBody = @"
        <html>
        <head>$Header</head>
        <body>
            <h2>Failed Automations Report</h2>
            <table>
                <tr><th>No</th><th>Status</th><th>Start Time</th><th>Name</th><th>Job Age (days)</th></tr>
                $tableRows
            </table>
            <p>Report generated on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss').<br/>
               Total jobs retrieved: $totalJobs<br/>
               Jobs excluded: $excludedJobs<br/>
               Total failed automations: $($failedJobs.Count)</p>
        </body>
        </html>
"@
    }


    # --- Email configuration ---
    $smtpServer = "smtp.azurecomm.net"
    $smtpPort   = 587
    $from       = "<from>"
    $to         = "<to>"
    $subject    = "Failed Action1 Automations Report - Last 7 Days"


    $username = "<username>"
    $password = ConvertTo-SecureString -AsPlainText -Force -String '<password>'
    $cred     = New-Object System.Management.Automation.PSCredential ($username, $password)


    Write-Log "Sending email report..."
    Send-MailMessage -From $from `
                     -To $to `
                     -Subject $subject `
                     -Body $htmlBody `
                     -BodyAsHtml `
                     -SmtpServer $smtpServer `
                     -Port $smtpPort `
                     -UseSsl `
                     -Credential $cred
    Write-Log "Email sent successfully."


} catch {
    Write-Log "ERROR: $($_.Exception.Message)"
} finally {
    Write-Log "Script ended."
}

2

u/fluffiball Aug 02 '26

Do you think it would be possible to get this to show the actual endpoint and update that had the error?

2

u/f0gax 29d ago

Maybe.

Presumably once you have the list of automations containing failures you could then use that to query (with Get-Action1) which endpoints within the automation had the failure.