Receive Location Autohandling false email alerts - powershell

$exceptionList = Get-Content C:\Users\Dipen\Desktop\Exception_List.txt
$ReceiveLocations = Get-WmiObject MSBTS_ReceiveLocation -Namespace 'root\MicrosoftBizTalkServer' -Filter '(IsDisabled = True)' |
Where-Object { $exceptionList -notcontains $_.Name }
# Exit the script if there are no disabled receive locations
if ($ReceiveLocations.Count -eq 0)
{
exit
}
Example:
and
$mailBodyPT = ""
$mailTextReportPT = "There are: "
[STRING]$Subject = $SubjectPrefix + $BizTalkGroup
$mailTextReportPT += "in the BizTalk group: " + $BizTalkGroup + "."
#Send mail
foreach ($to in $EmailTo)
{
$Body = $HTMLmessage
#$SMTPClient = New-Object Net.Mail.SmtpClient($PSEmailServer)
$message = New-Object Net.Mail.MailMessage($from, $to, $Subject, $Body)
$message.IsBodyHtml = $true;
$SMTPClient.Send($message)
}
Question: when all RLs have the status "disabled" and all of these RLs are included in the exception list the value of the variable $ReceiveLocations should be false and I need to stop further processing in my script. (do nothing if all RLs are found in exception list, just exit)
But I'm still getting false email alerts. How can we set logic for not getting email alerts if there were no extra RLs found in $ReceiveLocations?

The value of the variable $ReceiveLocations is $null when your Get-WmiObject statement doesn't return results. $null doesn't have a property Count, hence the check $ReceiveLocations.Count -eq 0 fails and your script doesn't terminate before sending an e-mail.
You can avoid this issue in a number of ways, e.g. by putting $ReceiveLocations in the array subexpression operator:
if (#($ReceiveLocations).Count -eq 0) {
exit
}
or you could use the way PowerShell interprets values in boolean expressions (non-empty arrays become $true, $null becomes $false):
if (-not $ReceiveLocations) {
exit
}

Related

Sending email with CSV from a Running Process check

I'm trying to check if a process is running on a remote computer (Eventually will be about 100 computers). If the process is not running, I'd like it to put the computername/IP into a CSV and then email that out. If the process is running on all machines, I'd like the script to not send an email out at all. To do this, I'd like to test the machines first to check they're online (If they're offline, we've either got bigger problems or it's off for a reason, but that's not what this process is checking for.
I'm going to be testing this script on a few machines with just the notepad process at the moment as it's something I can do on a test machines reletively quickly.
I'm a little stuck at the moment, because I don't know how to get the results from the process check to be put into a CSV and then emailed. In the code snippet below, it's not generating the outfile, but have left the variable I was testing with and the path to where the attachment would be in the send-mailmessage. Any advice will be appreciated, I'm still learning powershell at the moment so don't know all the tricks and tips yet.
Cheers
# Mail server Configuration
$MailServer = "mail.server.co.uk"
$MailFrom = MailFrom#server.co.uk"
# Mail Content Configuration
$MailTo = "Recipient#Server.co.uk"
$MailSubjectFail = "INS Process not running on $DAU"
$MailBodyFail = "The INS Process on the DAU $DAU is not running. Please manually start process on DAU $DAU"
# Process Info
$Process = "Notepad"
$ProcessIsRunning = { Get-Process $Process -ErrorAction SilentlyContinue }
#Results Info
$Exportto = "C:\Scripts\Content\INSChecker\Results.csv"
# Get DAU Information
foreach($line in (Get-Content C:\Scripts\Content\INSChecker\INSList.cfg)){
$line = $line.split(",")
$DAU = $line[0]
$DAUIP = $line[1]
# Test Connection to INS DAU
write-host "Testing: $DAU / $DAUIP"
$TestDAU = Test-Connection $DAU -quiet
$TestDAUIP = Test-Connection $DAUIP -quiet
write-host "Tests: $TestDAU / $TestDAUIP"
If($TestDAU -ne 'True'){
If($TestDAUIP -ne 'True'){
write-host "DNS Not resolved for $DAU"
Write-Output "INS $DAU/$DAUIP is OFFLINE" | Out-File C:\Scripts\Content\INSChecker\INSProcessCheck.log -append
}
}
Else{
# Get Process Running State and Send Email
if(!$ProcessIsRunning.Invoke()) {
Send-MailMessage -To $MailTo -From $MailFrom -SmtpServer $MailServer -Subject $MailSubjectFail -Body $MailBodyFail -Attachments C:\Scripts\Content\INSChecker\Results.csv
} else {
"Running"
}
}
}
Hopefully this gives a you a hint on where to begin and how to approach the problem, I have removed the irrelevant parts of the script and only left the logic I would personally follow.
The result of $report should be an object[] (object array) which should be very easy to manipulate and very easy to export to CSV:
#($report).where({ $_.SendMail }) | Export-Csv $exportTo -NoTypeInformation
I'll leave you the remaining tasks (attach the CSV, send the emails, etc) for your own research and design.
$ErrorActionPreference = 'Stop'
# Process Info
$Process = "Notepad"
$ProcessIsRunning = {
param($computer, $process)
# On Windows PowerShell -ComputerName is an option,
# this was removed on PS Core
try
{
$null = Get-Process $process -ComputerName $computer
# If process is running return 'Running'
'Running'
}
catch
{
# else return 'Not Running'
'Not Running'
# send a Warning to the console to understand why did this
# fail ( couldn't connect or the process is not running? )
Write-Warning $_.Exception.Message
}
}
#Results Info
$ExportTo = "C:\Scripts\Content\INSChecker\Results.csv"
$exportProps = 'Server', 'IP', 'Ping', 'DNSResolution', 'Process', 'SendMail'
# Get DAU Information
$report = foreach($line in Get-Content path/to/file.txt)
{
$status = [ordered]#{} | Select-Object $exportProps
$DAU, $DAUIP = $line = $line.split(",")
$status.SendMail = $false
$status.Server = $DAU
$status.IP = $DAUIP
# Test ICMP Echo Request and DNS Resolution
$ping = Test-Connection $DAUIP -Quiet
$dns = Test-Connection $DAU -Quiet
$status.Ping = ('Failed', 'Success')[$ping]
$status.DNSResolution = ('Failed', 'Success')[$dns]
$status.Process = & $ProcessIsRunning -computer $DAUIP -process $Process
if(-not $ping -or -not $dns -or $status.Process -eq 'Not Running')
{
$status.SendMail = $true
}
[pscustomobject]$status
}
#($report).where({ $_.SendMail }) # => This is what should be mailed

Error when checking array element in Powershell script : unexpected token 'eq'

I wish to go through a sharepoint list and if a property (a choice field named Status) is a certain value then change the author of that item.
Add-PSSnapin Microsoft.SharePoint.Powershell
$web = Get-SPWeb "http://site"
$library = $web.Lists["UserInfo"]
$newUser = $web.EnsureUser("user1")
$oldUser = $web.EnsureUser("user2")
foreach ($item in $library.Items)
{
#$userfield = New-Object Microsoft.SharePoint.SPFieldUserValue($web,$item["DocumentAuthorColumnInternalName"].ToString())
$userfield = New-Object Microsoft.SharePoint.SPFieldUserValue($web,$item["Author"].ToString())
$userfield = New-Object Microsoft.SharePoint.SPFieldUserValue($web,$item["Author"].ToString())
$login = $userfield.User.LoginName
#if ($login -eq $oldUser.LoginName)
if ($login -eq $oldUser.LoginName)
{
#if($item["Status"] eq 'Fully Implemented')
#{
$item["Author"] = $newUser
#if you are using default "Author" column, you need to set the following as well:
$item.Properties["vti_author"] = $newUser.LoginName
$item.UpdateOverwriteVersion() #this saves changes without incrementing the version
#}
}
$login = $null
}
$web.Dispose();
I can get it working but when I reach the line
if($item["Status"] eq 'Fully Implemented')
It causes an error
unexpected token 'eq'
Have you just missed the hyphen in eq? So it should be:
if($item["Status"] -eq 'Fully Implemented')
{
}

Get-Process output value if it it does not find the process specified

What is the Get-Process output value if it does not find a process specified? For example, I am checking if Outlook is closed and if it is, I back a PST file. Here is my code:
$source = "C:\Users\----\AppData\Local\Microsoft\Outlook\Outlook.pst"
$destination = "\\----\users\----\outlook"
$isOutlookOpen = Get-Process outlook*
$isOutlookOpen
if($isOutlookOpen = $true){
# Outlook is already closed:
Copy-Item -Path $source -Destination $destination
$messageParameters = #{
Subject = "Daily Outlook Backup Report computer"
Body = "Outlook was closed. Backup was complete."
From = "---"
To = "---"
SmtpServer = "---"
Port = ---
}
Send-MailMessage #messageParameters -BodyAsHtml
} else {
$messageParameters = #{
Subject = "Daily Outlook Backup Report computer"
Body = "Outlook was not closed. Backup was not initiated."
From = "---"
To = "---"
SmtpServer = "---"
Port = ---
}
Send-MailMessage #messageParameters -BodyAsHtml
}
It always goes to the else statement.
You use an assignment operator (=) in the condition, so it will always evaluate to $true. The equality comparison operator in PowerShell is -eq.
With that said, you don't need an operator there in the first place. Get-Process returns a list of System.Diagnostics.Process objects (or $null if no matching process is found). You can use the value of the variable $isOutlookOpen like a boolean value, because PowerShell will interpret a non-empty array as a boolean value $true and $null as a boolean value $false.
This should work:
$isOutlookOpen = Get-Process outlook*
if($isOutlookOpen) {
# ...
} else {
# ...
}
In your code you are checking if something is $true when it's value would be the details of the process:
$isOutlookOpen = Get-Process outlook*
$isOutlookOpen
if($isOutlookOpen = $true){
Even if it did give a true or false response you are actually assigning a value of $true in your if statement. It should be:
if ($isOutlookOpen -eq $true)
A better way to do it would be to put it in a try and catch block:
try {
get-process outlook* -errorAction stop
}
catch {
write-host "Outlook not running..."
}

Powershell and System Center "You cannot call a method on a null-valued expression

I'm trying to create a script that runs on my clients computers and checks for a pending reboot. If there is a pending reboot it pops up a message that asks the user to reboot or defer. This works great when I try it on my local computer. However, once I put it into System Center and deploy it, it does not run and I get the error "Warning: You cannot call a method on a null-valued expression" Any ideas? Here is the script
[CmdletBinding()]
param(
[Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[Alias("CN","Computer")]
[String[]]$ComputerName="$env:COMPUTERNAME",
[String]$ErrorLog
)
Begin
{
# Adjusting ErrorActionPreference to stop on all errors, since using [Microsoft.Win32.RegistryKey]
# does not have a native ErrorAction Parameter, this may need to be changed if used within another
# function.
$TempErrAct = $ErrorActionPreference
$ErrorActionPreference = "Stop"
}#End Begin Script Block
Process
{
Foreach ($Computer in $ComputerName)
{
Try
{
# Setting pending values to false to cut down on the number of else statements
$PendFileRename,$Pending,$SCCM = $false,$false,$false
# Setting CBSRebootPend to null since not all versions of Windows has this value
$CBSRebootPend = $NULL
# Querying WMI for build version
$WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer
# Making registry connection to the local/remote computer
$RegCon = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"LocalMachine",$Computer)
# If Vista/2008 & Above query the CBS Reg Key
If ($WMI_OS.BuildNumber -ge 6001)
{
$RegSubKeysCBS = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\").GetSubKeyNames()
$CBSRebootPend = $RegSubKeysCBS -contains "RebootPending"
}#End If ($WMI_OS.BuildNumber -ge 6001)
# Query WUAU from the registry
$RegWUAU = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\")
$RegWUAURebootReq = $RegWUAU.GetSubKeyNames()
$WUAURebootReq = $RegWUAURebootReq -contains "RebootRequired"
# Closing registry connection
$RegCon.Close()
# Determine SCCM 2012 Client Reboot Pending Status
# To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0
$CCMClientSDK = $null
$CCMSplat = #{
NameSpace='ROOT\ccm\ClientSDK'
Class='CCM_ClientUtilities'
Name='DetermineIfRebootPending'
ComputerName=$Computer
ErrorAction='SilentlyContinue'
}
$CCMClientSDK = Invoke-WmiMethod #CCMSplat
If ($CCMClientSDK)
{
If ($CCMClientSDK.ReturnValue -ne 0)
{
Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)"
}#End If ($CCMClientSDK -and $CCMClientSDK.ReturnValue -ne 0)
If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)
{
$SCCM = $true
}#End If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending)
}#End If ($CCMClientSDK)
Else
{
$SCCM = $null
}
# If any of the variables are true, set $Pending variable to $true
If ($CBSRebootPend -or $WUAURebootReq -or $SCCM)
{
$Pending = $true
}#End If ($CBS -or $WUAU)
If ($Pending = $CBSRebootPend -or $WUAURebootReq -or $SCCM)
{
$a = new-object -comobject wscript.shell
$b = $a.popup(“A Reboot is Pending, Press ""OK"" to reboot now or ""Cancel"" to reboot later.“,240,”CRISTA IT”,1)
if($b -eq 1) {
Restart-Computer
}
if($b -eq 2) {
#cancle was selected in the box. So should exit
exit(1)
}
}
}#End Try
Catch
{
Write-Warning "$Computer`: $_"
# If $ErrorLog, log the file to a user specified location/path
If ($ErrorLog)
{
Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append
}#End If ($ErrorLog)
}#End Catch
}#End Foreach ($Computer in $ComputerName)
}#End Process
End
{
# Resetting ErrorActionPref
$ErrorActionPreference = $TempErrAct
}#End End
PLEASE NOTE: I did not create this script fully but used different free scripts online to create this.

Add User to Local Group

This function should run on Windows Server 2003 and 2008 R2
Using the command line to execute it line by line is SUCCESSFULL! Execution by script fails.
function addUser2Group([string]$user,[string]$group)
{
$cname = gc env:computername
$objUser = [ADSI]("WinNT://$user")
$objGroup = [ADSI]("WinNT://$cname/$group,group")
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
if(-not $found)
{
$objGroup.PSBase.Invoke('Add',$objUser.PSBase.Path)
}
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
return $found
}
addUser2Group('MyGlobalMonitoringUser',"SomeDBGroup")
It should add a user to a local group. But it only gives me the following error:
Exception calling "Invoke" with "2" argument(s): "Unknown error (0x80005000)"
+ $members = #($objGroup.PSBase.Invoke <<<< ("Members"))
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Edit: the error message that occurs with /add is
The following exception occurred while retrieving member "Add": "Unknown error (0x80005000)"
Code is:
function addUser2Group([string]$user,[string]$group)
{
$cname = gc env:computername
try
{
([adsi]"WinNT://$cname/$group,group").Add("WinNT://$cname/$user,user")
}
catch
{
write2log($_)
return $false
}
return $true
}
Why go through the pain of reflection when PowerShell will do it for you? Example:
$group = [ADSI]"WinNT://./Power Users,group"
$group.Add("WinNT://SYSTEM,user")
The above adds the SYSTEM local account to the local Power Users group. I am not sure why you are getting the specific error above, you might get it with this abbreviated syntax as well. The particular COM interface that is being used is IADsGroup - reference here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa706021.aspx
Note: Because you are actually consuming COM objects wrapped in .NET objects, it is a good idea to call the Dispose method on any ADSI objects that are created when you are finished with them.
Why wouldn't you use net localgroup /add in your script instead of all that nasty looking WMI? PowerShell is a shell, not an operating system :)
Observations and assumptions
There are a couple of assumptions that are made based on the code.
You only passed ONE parameter object with two values. Use the Param statement
Call the function differently addUser2Group -user 'MyGlobalMonitoringUser' -group "SomeDBGroup"
Validate the parameters passed. They should be checked for empty/null at least.
This would only work if you had the $group variable assigned a value before the script ran.
Based on the fact that the incorrect parameter passing the value of the $group variable remains empty. It then caused the rest of the code to fail always returning the value of $False.
Proposed solution
Recommended Reading:
Simplify your PowerShell Script with Parameter Validation
Include the Param "switch" in the function
Change the way you call the function.
Here's a copy of the code that works.
function addUser2Group
{
# Added the Param Switch
Param(
[string]$user,
[string]$group
)
$cname = gc env:computername
$objUser = [ADSI]("WinNT://$user")
$objGroup = [ADSI]("WinNT://$cname/$group,group")
#$members = $objGroup.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
if(-not $found)
{
$objGroup.PSBase.Invoke('Add',$objUser.PSBase.Path)
}
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
return $found
}
addUser2Group -user 'testing' -group "Administrators"
•0x80005000 ("The specified directory service attribute or value does not exist").
Parameter binding or Environmental culprit perhaps?
As for the issue with net localgroup: examine the error message carefully:
The following exception occurred while retrieving member "Add"
Evidentally the /add flag is not being properly set as it is being interpreted as a member name, but since no code is provided, can't say why.
Just to add a more uptodate approach roughly five years later:
$group = Get-LocalGroup SomeDBGroup
$user = Get-LocalUser MyGlobalMonitoringUser
Add-LocalGroupMember $group $user