Friday, November 23, 2012

PowerShell: Exiting Admin Account(s) Check

To prevent issues with when an admin leaves an organization and we disable or delete his or her AD account(s) which would then causes a service, scheduled task, or process running under those credentials on one of servers to fail. I created a PowerShell script to remotely check a list of servers for any service, process, or scheduled task using those AD account(s). The script has to be ran from an elevated PowerShell session.

##############################################################################
# Script Name: Exiting_Admin_Check.ps1
# Version: 1.0
# Author: Dean Bunn
# Last Edited: 11/22/2012
# Description: Check Remote Systems for Any Processes, Scheduled Tasks,
#               Servics Running with Credentials of an Exiting Admin
##############################################################################

#Error Handling Setting 
$ErrorActionPreference = "SilentlyContinue";

#Array of Domain\UserIDs to Check Against
[array]$userIDs = @("ad\adminAccount1","ad\adminAccount2","ad\adminAccount3");

#Vars for Email Notice
[string]$smtpServer = "smtpServer.mycollege.edu";
[string]$fromAddress = "scriptAccount@mycollege.edu";
[string]$toAddress = "adAdmins@mycollege.edu";

#Array of Server Names
$servers = @(   
                "SERVER1.MYCOLLEGE.EDU",
                "SERVER2.MYCOLLEGE.EDU",
                "SERVER3.MYCOLLEGE.EDU",
                "SERVER4.MYCOLLEGE.EDU",
                "SERVER5.MYCOLLEGE.EDU",
                "SERVER6.MYCOLLEGE.EDU"
            );

#Function for Checking Domain\UserIDs
function Check_Domain_Account([string]$dmnID,[array]$userIDs)
{
    
    foreach($userID in $userIDs)
    {
        #Check to See If Domain User IDs Match
        if([string]::Equals($userID.ToString().ToLower(),$dmnID.ToString().ToLower()))
        {
            return $true;
        }
    }

    return $false;
    
}#End of Check_Domain_Account Function

#Array to Hold Custom Reporting Objects
$summary = @();

foreach($server in $servers)
{
    #Vars for Reporting
    [string]$pingStatus = "";
    [string]$serviceStatus = "";
    [string]$processStatus = "";
    [string]$taskStatus = "";
    
    #Display Current Server Working Against
    Write-Output ("Working on " + $server.ToString().ToLower());
    
    #Ping Computer Before Attempting Remote WMI 
    if(test-connection -computername $server -quiet) 
    {
        $pingStatus = "passed";
        
        #Pull Services on Remote System
        $uServices = Get-WMIObject win32_service -Computer $server;
        
        #Null Check on Returned Services Collection
        if($uServices)
        {
            foreach($service in $uServices)
            {
                #Check the Account the Service is Running Under
                if(Check_Domain_Account $service.StartName.ToString() $userIDs)
                {
                    $serviceStatus = "found";
                    break;
                }
            }
        }
        else
        {
            $serviceStatus = "RPC failed";
        }#End of $uServices Null Check
        
        
        #Pull Processes from Remote System
        $uProcesses = Get-WmiObject win32_Process -ComputerName $server
        
        #Null Check on Return Processes Collection
        if($uProcesses)
        {
            foreach($process in $uProcesses)
            {
                #Pull Domain and User Info from Process
                $prcOwnerDomain = $process.GetOwner().Domain;
                $prcOwnerUserID = $process.GetOwner().User;
                #Verify that Both Domain and User ID Information Exists
                if($prcOwnerDomain -ne $null -and $prcOwnerUserID -ne $null)
                {
                    #Var for Domain\UserID Format
                    [string]$tmpDmnUserID = $prcOwnerDomain.ToString() + "\" + $prcOwnerUserID.ToString();
                    
                    #Check the Account the Process is Running As
                    if(Check_Domain_Account $tmpDmnUserID $userIDs)
                    {
                        $processStatus = "found";
                        break;
                    }
                }#End of Null Check on Domain and UserID
                
            }#End of Foreach Process
        }
        else
        {
            $processStatus = "RPC failed";
        }#End of $uProcesses Null Check
        
        #Scheduled Tasks
        try
        {
            #Connect to Schedule Service on Remote System and Pull Tasks
            $schedService = New-Object -ComObject Schedule.Service;
            $schedService.Connect($server);
            $rootTasks = $schedService.GetFolder("").GetTasks("");
            
            foreach ($task in $rootTasks) 
            { 
                #Create XML Object From Task XML Settings
                [XML]$taskXML = $task.Xml;
                    
                #Check to Account the Task will Run As
                if(Check_Domain_Account $taskXML.Task.Principals.Principal.UserId.ToString() $userIDs)
                {
                    $taskStatus = "found";
                    break;
                }
                    
            }#End of Foreach Task
            
        }
        catch
        {
            $taskStatus = "COM failed";
        }#End of Tasks Try\Catch

    }
    else
    {
        $pingStatus = "failed";
    }#End of Test Connection
    
    #Create Custom Reporting Object and Assign Values
    $uEntry = New-Object PSObject
    $uEntry | Add-Member -MemberType NoteProperty -Name "Server" -Value $server.ToString().ToLower();
    $uEntry | Add-Member -MemberType NoteProperty -Name "Ping_Status" -Value $pingStatus;
    $uEntry | Add-Member -MemberType NoteProperty -Name "Service_Status" -Value $serviceStatus;
    $uEntry | Add-Member -MemberType NoteProperty -Name "Process_Status" -Value $processStatus;
    $uEntry | Add-Member -MemberType NoteProperty -Name "Task_Status" -Value $taskStatus;
    #Add Reporting Object to Reporting Array
    $summary += $uEntry;
    
}#End of Foreach Computer

#Sort Report by Server Name
$summary = $summary | Sort-Object Server;

######## Configure HTML Report ########

#Var for HTML Message Body
$msgBody = "<html>
            <body>
            <h4>Exiting Admin Account(s) Report</h4>
            <span style=""font-size:8pt;font-family:Arial,sans-serif"">
            <strong>Accounts Checked:</strong>
            <br />";
            
#Add Each Account Checked to Report
foreach($usrID in $userIDs)
{
    $msgBody += $usrID.ToString().ToLower() + "<br />";
}

#Format the Report HTML Table
$msgBody += "</span><br />
             <table border=""1"" cellpadding=""4"" cellspacing=""0""
             style=""font-size:8pt;font-family:Arial,sans-serif"">
             <tr bgcolor=""#000099"">
             <td><strong><font color=""#FFFFFF"">Server</font></strong></td>
             <td align=""center""><strong><font color=""#FFFFFF"">Ping</font></strong></td>
             <td align=""center""><strong><font color=""#FFFFFF"">Services</font></strong></td>
             <td align=""center""><strong><font color=""#FFFFFF"">Processes</font></strong></td>
             <td align=""center""><strong><font color=""#FFFFFF"">Scheduled Tasks</font></strong></td>
             </tr>
             ";

#Var for Table Row Count
[int]$x = 1;

#Loop Through Custom Object Collection
foreach($srv in $summary)
{
    #Determine Even\Odd Row 
    if($x%2)
    {
        $msgBody += "<tr bgcolor=""#FFFFFF"">";
    }
    else
    {
        $msgBody += "<tr bgcolor=""#E8E8E8"">";
    }
    
    $x++;
    
    $msgBody += "<td>" `
                + $srv.Server `
                + "</td><td align=""center"">" `
                + $srv.Ping_Status `
                + "</td><td align=""center"">" `
                + $srv.service_Status `
                + "</td><td align=""center"">" `
                + $srv.process_Status `
                + "</td><td align=""center"">" `
                + $srv.task_Status `
                + "</td></tr>
                
                ";
}

#Close HTML Table and Message
$msgBody += "</table>
            </body>
            </html>";

#Settings for Email Message
$messageParameters = @{                        
                          Subject = "Exiting Admin Account(s) Report"
                           Body = $msgBody                       
                           From = $fromAddress                        
                           To = $toAddress                        
                           SmtpServer = $smtpServer                       
                       };                      
#Send Report Email Message 
Send-MailMessage @messageParameters –BodyAsHtml;

################################################################
#Export to CSV Section (Leaving In Case Needed Later)
#Get Current Short Date
#$rptDate = Get-Date -Format d;
#Configure Report Name
#$reportName = "Exiting_Admin_Check_" `
#              + $rptDate.ToString().Replace("/","-") + ".csv";
#Export Report to CSV
#$summary | Export-CSV $reportName -NoTypeInformation;
################################################################


Reference:
http://myitforum.com/cs2/blogs/yli628/archive/2008/07/28/powershell-script-to-retrieve-scheduled-tasks-on-a-remote-machine-task-scheduler-api.aspx

Thursday, November 15, 2012

PowerShell: Create Server Side Spam Rule

A few days ago I was tasked with creating a PowerShell script that would create a server side spam rule for all mailboxes in an 2010 environment. Using the Get-InboxRule and New-InboxRule cmdlets I was easily able to create a script that looked to see if the rule didn't already exist on the mailbox before it created it. The spam rule in this environment looks for a specific text in the header of the message. If it finds the text it will move the item to the Junk E-mail Folder.

Please be warned that if you use the New-InboxRule cmdlet on a mailbox it will remove any client side rules

##########################################################
# Script Name: Ex_Set_Inbox_Rule.ps1
# Version: 1.0
# Author: Dean Bunn
# Last Edited: 11/09/2012
# Description: Adds Spam Score Rule to Mailboxes
##########################################################

#Pull Collection of All Mailboxes 
$mbxs = Get-Mailbox -resultsize unlimited;

foreach($mbx in $mbxs)
{

    #Null Check on Primary SMTP Address
    if($mbx.PrimarySmtpAddress)
    {
        #Vars for New Inbox Rule
        [string]$primSMTP = $mbx.PrimarySmtpAddress.ToString();
        [string]$junkFolder = $mbx.PrimarySmtpAddress.ToString() + ":\Junk E-Mail";
        [string]$xscore = "X-Spam-Score: ****";
        [boolean]$existingRule = $false;
        
        #Retrieve Mailbox Rules
        $inboxRules = Get-InboxRule -mailbox $mbx.PrimarySmtpAddress.ToString();
        
        #Check to See If Any Rules Exist on Mailbox
        #If So Check for the SpamRule -ne $null -and $inboxRules.Count -gt 0
        if($inboxRules)
        {
             foreach($ibxr in $inboxRules)
            {
                if($ibxr.Name.ToString().Trim() -eq "SpamRule")
                {
                    $existingRule = $true;
                }
            }
            
        }
        
        #If Rule Doesn't Exist Create It
        if($existingRule -eq $false)
        {
            New-InboxRule -mailbox $primSMTP -Name "SpamRule" -Confirm:$False -MoveToFolder 
           $junkFolder -HeaderContainsWords @{add=$xscore} -StopProcessingRules $true;
        }
        
    }#End of Primary Address Check
    
}#End of Foreach Mailbox


References:
http://technet.microsoft.com/en-us/library/dd335170(v=exchg.141).aspx
http://technet.microsoft.com/en-us/library/bb684908.aspx

Wednesday, September 5, 2012

PowerShell: Get Report of Installed Applications Using Remoting

Below is a PowerShell script that uses PowerShell Remoting to pull a list of installed applications on a Windows system. Since the list is kept in the registry, you can easily use the Get-ChildItem cmdlet to take care of business. Found some resource links that helped me remove the Windows updates and hotfixes from the report.

##############################################################
# Script Name: PS_Remoting_Installed_Applications.ps1
# Version: 1.0
# Description: Using PowerShell Remoting Queries Remote
#               Systems for Installed Software
##############################################################

$allComputers = Invoke-Command -computername SERVER01,SERVER02,SERVER03 `
-scriptblock {  
                #Arrays for Holding Installed App Data 
                $installedApps = @();
                $installedAppsKeys = @();
                
                #Pull 32bit Installed Apps
                Get-ChildItem hklm:\Software\Microsoft\Windows\CurrentVersion\Uninstall `
                    | ForEach-Object { $installedAppsKeys += Get-ItemProperty $_.pspath `
                    | Where-Object {$_.DisplayName -and !$_.ReleaseType -and `
                    !$_.ParentKeyName -and ($_.UninstallString -or $_.NoRemove)} };
                #Check for 64bit Installed Apps and Pull Information
                if(Test-Path hklm:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall)
                {
                  Get-ChildItem hklm:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall `
                  | ForEach-Object { $installedAppsKeys += Get-ItemProperty $_.pspath `
                  | Where-Object {$_.DisplayName -and !$_.ReleaseType -and !$_.ParentKeyName `
                  -and ($_.UninstallString -or $_.NoRemove)} };
                }
                #Loop Through Installed Application Registry Keys and Pull Info
                foreach($instlApp in $installedAppsKeys)
                {
                    #Local Variables Used in Reporting
                    [string]$displayName = "";
                    [string]$displayVersion = "";
                    
                    #Check for DisplayName is Null or Emtpy
                    if(![string]::IsNullOrEmpty($instlApp.DisplayName))
                    {
                        $displayName = $instlApp.DisplayName.ToString();
                        #Check to See If Display Version is Null or Empty
                        if(![string]::IsNullOrEmpty($instlApp.DisplayVersion))
                        {
                           $displayVersion = $instlApp.DisplayVersion.ToString();
                        }
                        #Create Custom PSObject and Add to Reporting Array
                        $app = New-Object PSObject;
                        $app | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $displayName;
                        $app | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $displayVersion;
                        $installedApps += $app;
                    }
                 }
                #Send Back Reporting Array Sorted by Display Name
                $installedApps | Sort-Object DisplayName;                                                  
            }

$allComputers | Format-Table PSComputerName,DisplayName,DisplayVersion -AutoSize

PowerShell: WMI to Report Installed Applications on Remote Systems

Recently, I've been working with more PowerShell Remoting. Earlier this week I wrote a script that queries a list of systems for installed applications using Remoting. Tomorrow, I'm demo'ing that script (which I will post after this one) and needed to show how you would do it using just WMI in PowerShell. Since the listing of installed applications is stored in the Registery it took a good amount of time trying to figure out how to access a remote registry via PowerShell just using WMI. Below is the comparison script. Enjoy.


#########################################################
# Script Name: PS_Remote_WMI_Installed_Applications.ps1
# Version: 1.0
# Description: Using WMI Remotely Queries
#               Systems for Installed Software
#########################################################

#Array for Reporting Installed Software
$installedApps = @();

#Array for Registry Paths to Installed Apps
$appRegPaths = @("Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
                 "Software\Microsoft\Windows\CurrentVersion\Uninstall");

#Array of System Names to Run Against
$computers = @("SERVER01","SERVER02","SERVER03");

foreach($computer in $computers)
{
    #Ping System First
    if(Test-Connection -ComputerName $computer -Quiet)
    {
        #Connect to WMI Registry Class
        $uReg = [wmiclass]"\\$computer\root\default:StdRegProv";
                          
        foreach($regPath in $appRegPaths)
        {
            #Pull the Application Registry Keys 
            $iAppKeys = $uReg.EnumKey(2147483650,$regPath);
    
            #Null Check on Application Registry Keys
            if($iAppKeys)
            {
                #Loop Through Each Application Key
                foreach($appKey in $iAppKeys.sNames)
                {
                    #Construct Key Path
                    $keyPath = $regPath + "\" + $appKey.ToString();
                    
                    #Pull the Key DisplayName String Value
                    $keyDisplayName = $uReg.GetStringValue(2147483650,$keyPath,"DisplayName");
                    if(![string]::IsNullOrEmpty($keyDisplayName.sValue))
                    {
                        #Local Vars Used for Reporting
                        [string]$displayName = $keyDisplayName.sValue.ToString();
                        [string]$displayVersion = "";
                    
                        #Pull the Key DisplayVersion String Value
                        $keyDisplayVersion = $uReg.GetStringValue(2147483650,$keyPath,"DisplayVersion");
                        if(![string]::IsNullOrEmpty($keyDisplayVersion.sValue))
                        {
                            $displayVersion = $keyDisplayVersion.sValue.ToString();
                        }
                
                        #Create Custom PSObject and Add to Reporting Array
                        $app = New-Object PSObject;
                        $app | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computer;
                        $app | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $displayName;
                        $app | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $displayVersion;
                          $installedApps += $app;
                        
                    }#End of Null\Empty Check on DisplayName String Value
                    
                }#End of Foreach $iAppKeys
            
            }#End of Null Check on $iAppKeys
        
        }#End of Foreach Reg Path
    
    }#End of Test-Connection

}#End of Foreach Computer

$installedApps | Sort-Object ComputerName,DisplayName | Format-Table -AutoSize;

Friday, August 10, 2012

PowerShell: WMI Basics

During my lunch hour today, I showed a few colleagues how to use of PowerShell with WMI. We mainly covered the Get-WMIObject command and a lot of fun things you can do with it. Below are the examples we went over. Enjoy.
############################################################
# WMI PowerShell Commands
############################################################

#Use -ComputerName Option with Hostname, FQDN, or IP Address in Command for Remote Systems
#For Example to Get the BIOS Settings On a System Called DeanTestServer
Get-WmiObject -Query "SELECT * FROM Win32_BIOS" -ComputerName "DeanTestServer"
#Or Use the IP Address
Get-WmiObject -Query "SELECT * FROM Win32_BIOS" -ComputerName "192.168.2.25"

#Get-WMIObject Can Use A WOL Query, Filter, or PowerShell Where Statement to Limit Results
#For Example the Following Three Commands Have the Same Result
Get-WmiObject -Class Win32_Share | Where-Object { $_.Name -eq "C$" }
Get-WmiObject -Class Win32_Share -Filter "Name='C$'"
Get-WmiObject -Query "SELECT * FROM Win32_Share WHERE Name='C$'"

#Get All Win_32 Classes in the CIMV2 Namespace
Get-WmiObject -Namespace "root\cimv2" -List | Where-Object { $_.Name -like "Win32_*" } | Select-Object Name | Sort-Object Name | Out-File WMI_CIMV2_Classes.txt

#Get All Properties and Methods for the WMI Class
Get-WmiObject Win32_Volume | Get-Member

#Get Basic System Information
Get-WmiObject -Query "SELECT * FROM Win32_ComputerSystem"

#Get Local Accounts and Groups on a System
Get-WmiObject -Query "SELECT * FROM Win32_Account" | Select-Object Name,SID | Sort-Object Name

#Get Disk Information (Model and Size)
Get-WmiObject -Query "SELECT * FROM Win32_DiskDrive"

#Get Processor Information
Get-WmiObject -Query "SELECT * FROM Win32_Processor" | Select-Object Name,Description,NumberOfCores | Sort-Object Name 

#Get Operating System Info
Get-WmiObject -Query "SELECT * FROM Win32_OperatingSystem"

#Get MAC Addresses of All Network Adapters
Get-WmiObject -Query "SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL" | Select-Object Name,MACAddress | Sort-Object Name;

#Get All Assigned IPs 
Get-WmiObject -Query "SELECT * FROM Win32_NetworkAdapterConfiguration" | Where-Object { $_.IPAddress -ne $null} | Select-Object Description,IPAddress;

#List Number of Memory Slots on a System
Write-Output ("Number of Memory Slots: " + (Get-WmiObject -Query "SELECT * FROM win32_PhysicalMemoryArray").MemoryDevices);

#Retrieve Memory Slot Allocations
Get-WMIObject -Query "SELECT * FROM Win32_PhysicalMemory" | ForEach-Object { Write-Output ($_.DeviceLocator.ToString() + " " + ($_.Capacity/1GB) + "GB") };

#Retrieve Disk Volume Sizes (Including Mount Points, Excluding Pass Through Drives)
$sysVolumes = Get-WmiObject –Query "Select * FROM Win32_Volume WHERE DriveType=3 AND NOT Name LIKE '%?%'" | Sort-Object Name;
foreach($sv in $sysVolumes)
{
    #Var for Volume Size
    $vSize = "{0:N2}" -f ($sv.Capacity/1GB);
    #Var for Free Space 
    $vFS = "{0:N2}" -f ($sv.FreeSpace/1GB);
    #Var for Percentage Free Space
    $vPF = "{0:N2}" -f (($sv.FreeSpace/$sv.Capacity)*100);
    #Var for Drive Letter
    $vLetter = $sv.Name.ToString().TrimEnd("\");
    $VolumeStatus = "$vLetter | Size(GB): $vSize | Free Space(GB): $vFS | Percentage Free: $vPF"; 
    Write-Output $VolumeStatus;
}