I am trying to gather data from eventlogs of logons, disconnect, logoff etc... this data will be stored in a csv format.
This is the script i am working which got from Microsoft Technet and i have modified to meet my requirement. Script is working as it should be but there is looping going on which i can't figure out how it should be stopped.
$ServersToQuery = Get-Content "C:\Users\metho.HOME\Desktop\computernames.txt"
$cred = "home\Administrator"
$StartTime = "September 19, 2018"
#$Yesterday = (Get-Date) - (New-TimeSpan -Days 1)
foreach ($Server in $ServersToQuery) {
$LogFilter = #{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
ID = 21, 23, 24, 25
StartTime = (Get-Date).AddDays(-1)
}
$AllEntries = Get-WinEvent -FilterHashtable $LogFilter -ComputerName $Server -Credential $cred
$AllEntries | Foreach {
$entry = [xml]$_.ToXml()
$Output += New-Object PSObject -Property #{
TimeCreated = $_.TimeCreated
User = $entry.Event.UserData.EventXML.User
IPAddress = $entry.Event.UserData.EventXML.Address
EventID = $entry.Event.System.EventID
ServerName = $Server
}
}
}
$FilteredOutput += $Output | Select TimeCreated, User, ServerName, IPAddress, #{Name='Action';Expression={
if ($_.EventID -eq '21'){"logon"}
if ($_.EventID -eq '22'){"Shell start"}
if ($_.EventID -eq '23'){"logoff"}
if ($_.EventID -eq '24'){"disconnected"}
if ($_.EventID -eq '25'){"reconnection"}
}
}
$Date = (Get-Date -Format s) -replace ":", "-"
$FilePath = "$env:USERPROFILE\Desktop\$Date`_RDP_Report.csv"
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
Write-host "Writing File: $FilePath" -ForegroundColor Cyan
Write-host "Done!" -ForegroundColor Cyan
#End
First time when i run the script, it runs fine and i get the csv output as it should be. When i run the script again than a new CSV is created (as it should be) but the same event log enteries are created twice and run it again than three enteries are created for the same event. This is very strange as a new csv is created each time and i dont not have -append switch for export-csv configured.
$FilteredOutput = #()
$Output = #()
I did try adding these two lines in above script as i read somewhere that it is needed if i am mixing multiple variables into a array (i do not understand this so applogies if i get this wrong).
Can someone please help me this, more importantly, I need to understand this as it is good to know for my future projects.
Thanks agian.
mEtho
It sounds like the$Output and $FilteredOutput variables aren't getting cleared when you run the script subsequent times (nothing in the current script looks to do that), so the results are just getting appended to these variables each time.
As you've already said, you could add these to the top of your script:
$FilteredOutput = #()
$Output = #()
This will initialise them as empty arrays at the beginning, which will ensure they start empty as well as make it possible for them to be appended to (which happens at the script via +=). Without doing this on the first run the script likely failed, so I assume you must have done this in your current session at some point for it to be working at all.
Related
I got the following variable $listofusers which returns the below objects in two columns:
SourceUser DestinationUser
---------- ---------------
username1#legacy.company.corp username1#modern.company.corp
username2#legacy.company.corp username2#modern.company.corp
username3#legacy.company.corp username3#modern.company.corp
username4#legacy.company.corp username4#modern.company.corp
I now need to process this list of users in a foreach loop. I have tried so far the following but without luck yet:
$Results = ForEach ($User in $listofusers) {
Write-Host "Processing SourceUser $($User.SourceUser)"
Write-Host "Processing DestinationUser $($User.DestinationUser)"
#Assign the content to variables
$SourceUsers = $User.SourceUser
$DestinationUsers = $User.DestinationUser
}
It only returns me the last line of the objects:
$SourceUsers
RETURN ONLY: username4#legacy.company.corp
$DestinationUsers
RETURN ONLY: username4#modern.company.corp
How can I add all the objects in the variable $listofusers for further processing?
UPDATE:
I am trying to achieve the following that's why I have broken the association in listofusers
$SourceUser = #()
$DestinationUser = #()
$Results = ForEach ($User in $listofusers)
{
Write-Host "Processing SourceUser $($User.SourceUser)"
Write-Host "Processing DestinationUser $($User.DestinationUser)"
#Assign the content to variables
$SourceUser += $User.SourceUser
$DestinationUser += $User.DestinationUser
#Cannot get that variables working yet
$sourceusername, $sourcedomain = $SourceUser -split ("#")
$DestinationUsername, $destinationDomain = $DestinationUser -split ("#")
$SourceAccount = Get-ADUser $sourceusername -server $sourcedomain -Properties objectSid
$TargetAccount = Get-ADUser $DestinationUsername -Server $destinationDomain
}
Is there any better way to achieve that and get those variables to that point?
NEW UPDATE:
The purpose of the script would be to achieve the following cmdlets for processing ad objects:
#get the objectSid of the source account
$objectSid = $SourceAccount.objectSid
#copy source account objectSid to target account msExchMasterAccountSid
$TargetAccount | Set-ADUser -Replace #{"msExchMasterAccountSid"=$objectSid}
#enable targetaccount
$TargetAccount | Enable-ADAccount
#disable the source account
$SourceAccount | Disable-ADAccount
#move the migrated user into prod OU
$TargetAccount | Move-ADObject -TargetPath "ou=test,dc=contoso,dc=com"
Thanks
here is a demo of the concept i was trying to get across. [grin] it keeps the association of the objects in your CSV in the original object for as long as possible. the code has NOT been tested since i have no AD access.
what it does ...
fakes reading in a CSV file
when you are ready to use real data, replace the entire "region" with a call to Import-CSV.
iterates thru the list
builds a splat of the parameters for the AD calls
see Get-Help about_Splatting for more info on that wonderfully useful idea.
calls Get-AdUser with each to the Source/Target user data sets
stores the above
uses the stored account info to ...
== replace the .objectSid of the Target account
== enable the Target account
== disable the Source account
== Move the Target account to the desired OU
the hard coded OU could be set with a variable to make this a tad more flexible. however, this seems to be a one-off operation - so there is likely no benefit.
if you want to add logging, do so in the same loop.
there is no error handling, either. that likely should be added with a try/catch around each AD call & logging of both success and failure.
the code ...
#region >>> fake reading in a CSV file
# in real life, use Import-CSV
$UserList = #'
SourceUser, DestUser
ABravo#Old.com, ABravo#NewDomain.com
BCharlie#Old.com, BCharlie#NewDomain.com
CDelta#Old.com, CDelta#NewDomain.com
DEcho#Old.com, DEcho#NewDomain.com
EFoxtrot#Old.com, EFoxtrot#NewDomain.com
'# | ConvertFrom-Csv
#endregion >>> fake reading in a CSV file
ForEach ($UL_Item in $UserList)
{
Write-Host 'Processing ...'
Write-Host (' SourceUser {0}' -f $UL_Item.SourceUser)
Write-Host (' DestinationUser {0}' -f $UL_Item.DestUser)
Write-Host '__ Source Account __'
$GADU_Params_1 = [ordered]#{
Identity = $UL_Item.SourceUser.Split('#')[0]
Server = $UL_Item.SourceUser.Split('#')[1]
Properties = 'objectSid'
}
$SourceAccount = Get-ADUser #GADU_Params_1
Write-Host '__ Target Account __'
$GADU_Params_2 = [ordered]#{
Identity = $UL_Item.DestUser.Split('#')[0]
Server = $UL_Item.DestUser.Split('#')[1]
}
$TargetAccount = Get-ADUser #GADU_Params_2
Write-Host 'Making changes ...'
# all these piped objects are slower than making _direct_ calls
# however, i don't have any way to test the code, so i can't use what likely is faster
# something like >>>
# Set-AdUser -Identity $TargetAccount -Replace #{
# 'msExchMasterAccountSid' = $objectSid
# }
# note that i also replaced the unneeded _double_ quotes with the safer _single_ quotes
$TargetAccount |
Set-AdUser -Replace #{
'msExchMasterAccountSid' = $SourceAccount.objectSid
}
$TargetAccount |
Enable-AdAccount
$SourceAccount |
Disable-AdAccount
$TargetAccount |
Move-AdObject -TargetPath 'ou=test,dc=contoso,dc=com'
Write-Host '=' * 30
Write-Host ''
}
no output shown since i can't actually run this AD stuff. [grin]
$SourceUsers and $DestinationUsers contain only the last ones becasue youa re replacing the value on each foreach iteration.
if you want it to separate the properties try this:
$SourceUsers = $User | select SourceUser -ExpandProperty SourceUser
$DestinationUsers = $User | select DestinationUser -ExpandProperty DestinationUser
That will create a collection of only those strings. you wont be able to access those values by property anymore, meaning that is a simple String[] after the -ExpandProperty.
$SourceUsers = #()
$DestinationUsers = #()
$Results = ForEach ($User in $listofusers) {
Write-Host "Processing SourceUser $($User.SourceUser)"
Write-Host "Processing DestinationUser $($User.DestinationUser)"
#Assign the content to variables
$SourceUsers += $User.SourceUser
$DestinationUsers += $User.DestinationUser
}
$SourceUsers = #() and $DestinationUsers = #() creates two empty
arrays which we will use in the loop
+= is an assignment operator which enables us to assign more than
one value to a variable. According to the documentation: Increases
the value of a variable by the specified value, or appends the
specified value to the existing value.
I have been struggling to come up with a working solution for days on this
What am I trying to achieve?
Foreach ($item in $webApps){
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $item -ResourceGroupName $resourceGroup -ApiVersion $APIVersion)
}
The issue is that "-resourceName" will not accept objects, but rather only a string
I am looking for a way to take the output of the following command, convert it to a string, so that it can satisfy –ResourceName, and loop through each item in the string
$webApps = (Get-AzureRmResourceGroup -Name $resourceGroup | Get-AzureRmWebApp).name
This returns a nice list of Azure WebApps that exist in a specified ResourceGroup, however they are in object form, which –ResourceName will not take
I have tried several ways to convert the output of $webApps to a string, add a comma to the end, then do a –split ',' but nothing seems to work for properly, where –ResourceName will accept it
Method 1:
[string]$webAppsArrays =#()
Foreach ($webApp in $webApps){
$webAp+',' -split ','
}
Method 2:
$
webApps | ForEach-Object {
$webApp = $_ + ","
Write-Host $webApp
}
Method 3:
$csvPath2 = 'C:\Users\Giann\Documents\_Git Repositorys\QueriedAppList2.csv'
$webApps = (Get-AzureRmResourceGroup -Name $resourceGroup | Get-AzureRmWebApp).name | out-file -FilePath $csvPath1 -Append
$csvFile2 = import-csv -Path $csvPath1 -Header Name
This ouputs a list in a CSV, however these are still objects, so I cannot pass each item into –ResourceName
I am going in circles trying to make the below a repeatable, looping script
The desired end result would be to use the below script, with an array of webApps, being queried from the provided resource group variable:
Any help would be greatly appreciated for how to use this script, but pull a dynamic list of WebApps from a specified Resource Group, keeping in mind the -ResourceName "String" restrictions in the $WebAppConfig variable
Here is the original script to create IP Restrictions for 1 Web App and 1 Resource Group, using properties from a CSV file:
#Create a Function to create IP Restrictions for 1 Web App and 1 Resource Group, using properties from the CSV file:
#Variables
$WebApp = ""
$resourceGroup =""
$subscription_Id = ''
#Login to Azure
Remove-AzureRmAccount -ErrorAction SilentlyContinue | Out-Null
Login-AzureRmAccount -EnvironmentName AzureUSGovernment -Subscription $subscription_Id
Function CreateIpRestriction {
Param (
[string] $name,
[string] $ipAddress,
[string] $subnetMask,
[string] $action,
[string] $priority
)
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp -ResourceGroupName $ResourceGroup -ApiVersion $APIVersion)
$ipRestriction = $WebAppConfig.Properties.ipSecurityRestrictions
$ipRestriction.name = $name
$ipRestriction.ipAddress = $ipAddress
$ipRestriction.subnetMask = $subnetMask
$ipRestriction.action = $action
$ipRestriction.priority = $priority
return $ipRestriction
}
#Set csv file path:
$csvPath5 = 'C:\Users\Giann\Documents\_Git Repositorys\ipRestrictions5.csv'
#import CSV Contents
$ipRestrictionArray = Import-Csv -Path $csvPath5
$ipRestrictions = #()
foreach($item in $ipRestrictionArray){
Write-Host "Adding ipRestriction properties for" $item.name
$newIpRestriction = CreateIpRestriction -name $item.name -ipAddress $item.ipAddress -subnetMask $item.subnetMask -action $item.action -priority $item.priority
$ipRestrictions += $newIpRestriction
}
#Set the new ipRestriction on the WebApp
Set-AzureRmResource -ResourceGroupName $resourceGroup -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp/web -ApiVersion $APIVersion -PropertyObject $ipRestrictions
As continuation on the comments, I really need multiline, so here as an answer.
Note that I cannot test this myself
This page here shows that the Set-AzureRmResource -Properties parameter should be of type PSObject.
(instead of -Properties you may also use the alias -PropertyObject)
In your code, I don't think the function CreateIpRestriction returns a PSObject but tries to do too much.
Anyway, try like this:
Function CreateIpRestriction {
Param (
[string] $name,
[string] $ipAddress,
[string] $subnetMask,
[string] $action,
[string] $priority
)
# There are many ways to create a PSObject (or PSCustomObject if you like).
# Have a look at https://social.technet.microsoft.com/wiki/contents/articles/7804.powershell-creating-custom-objects.aspx for instance.
return New-Object -TypeName PSObject -Property #{
name = $name
ipAddress = $ipAddress
subnetMask = $subnetMask
action = $action
priority = $priority
}
}
#Set csv file path:
$csvPath5 = 'C:\Users\Giann\Documents\_Git Repositorys\ipRestrictions5.csv'
#import CSV Contents
$ipRestrictionArray = Import-Csv -Path $csvPath5
# create an new array of IP restrictions (PSObjects)
$newIpRestrictions = #()
foreach($item in $ipRestrictionArray){
Write-Host "Adding ipRestriction properties for" $item.name
$newIpRestrictions += (CreateIpRestriction -name $item.name -ipAddress $item.ipAddress -subnetMask $item.subnetMask -action $item.action -priority $item.priority )
}
# here we set the restrictions we collected in $newIpRestrictions in the $WebAppConfig.Properties.ipSecurityRestrictions array
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp -ResourceGroupName $ResourceGroup -ApiVersion $APIVersion)
$WebAppConfig.Properties.ipSecurityRestrictions = $newIpRestrictions
$WebAppConfig | Set-AzureRmResource -ApiVersion $APIVersion -Force | Out-Null
The code above will replace the ipSecurityRestrictions by a new set. You may want to consider first getting them and adding to the already existing list.
I found examples for Getting, Adding and Removing ipSecurityRestrictions here, but I can imagine there are more examples to be found.
Hope that helps.
I'm trying write a script that will grab the fortune 100 URLs from here, put those into an array, and then write a runspace that uses Invoke-WebRequest to get the content of those URLs and writes that content to a file. This is the code that I have so far:
#Importing Modules
Import-Module PoshRSJob
#variable declaration
$page = Invoke-WebRequest https://www.zyxware.com/articles/4344/list-of-fortune-500-companies-and-their-websites
$links = $page.Links
$tables = #($page.ParsedHtml.GetElementsByTagName("TABLE"))
$tableRows = $tables[0].Rows
#loops through the table to get only the top 100 urls.
$urlArray = #()
foreach ($tablerow in $tablerows) {
$urlArray += New-Object PSObject -Property #{'URLName' = $tablerow.InnerHTML.Split('"')[1]}
#Write-Host ($tablerow.innerHTML).Split('"')[1]
$i++
if ($i -eq 101) {break}
}
#Number of Runspaces to use
#$RunspaceThreads = 1
#Declaring Variables
$ParamList = #($urlArray)
$webRequest = #()
$urlArray | start-rsjob -ScriptBlock {
#$webRequest = (Invoke-WebRequest $using:ParamList)
#Invoke-WebRequest $urlArray
#Invoke-WebRequest {$urlArray}
#Get-Content $urlArray
}
The problem that I'm running into right now is that I can't get Invoke-WebRequest or Get-Content to give me the contents of the URLs that are actually contained in the array. You can see that in the scriptblock, I commented out some lines that didn't work.
My question is: using a runspace, what do I need to do to pull the data from all the URLs in the array using Get-Content, and then write that to a file?
You can adjust your current query to get the first 100 company names. This skips the empty company at the front. Consider using [PSCustomObject] #{ URLName = $url } which replaces the legacy New-Object PSObject.
$urlArray = #()
$i = 0
foreach ($tablerow in $tablerows) {
$url = $tablerow.InnerHTML.Split('"')[1]
if ($url) {
# Only add an object when the url exists
$urlArray += [PSCustomObject] #{ URLName = $url }
$i++
if ($i -eq 100) {break}
}
}
To run the requests in parallel use Start-RSJob with a script block. Invoke-Webrequest is then run in parallel. Note that in this example $_ refers to the current array element that is piped which consists of an object with a URLName property, but you need to be a little careful what variables you use inside the scriptblock because they might not be resovled they way you expect them to be.
# Run the webrequests in parallel
# $_ refers to a PSCustomObject with the #{ URLName = $url } property
$requests = ($urlArray | start-rsjob -ScriptBlock { Invoke-WebRequest -Uri $_.URLName })
You can then wait for all the jobs to complete and do some post processing of the results.
Here only the length of the website contents are written because the pages themself are lengthy.
# Get the results
# $_.Content.Length gets the length of the content to not spam the output with garbage
$result = Get-RSjob | Receive-RSJob | ForEach { $_.Content.Length }
Write-Host $result
I'm trying to optimize my Powershell Script a little.
I have a lot of log (text) files, that i need to search through the content of, for a specific text entry.
If the entry is found, I need the script to trigger with an inset to an sql databse.
This is what I have for now:
$tidnu = (Get-Date -f dd.MM.yyyy)
$Text = "ERROR MESSAGE STACK"
$PathArray = #()
$NodeName = "SomeName"
$Logfil = "SomeLogFile"
Get-ChildItem $Path -Filter "*ORA11*.log" |
Where-Object { $_.Attributes -ne "Directory"} |
ForEach-Object {
If (Get-Content $_.FullName | Select-String -Pattern $Text)
{
$PathArray += $_.FullName
$cmd.commandtext = "INSERT INTO ErrorTabel (Datotid, Nodename, Logfil, ErrorFound) VALUES('{0}','{1}','{2}','{3}')" -f $tidnu, $NodeName, $Logfil, "Yes"
$cmd.ExecuteNonQuery()
}
else
{
$cmd.commandtext = "INSERT INTO ErrorTabel (Datotid, Nodename, ErrorFound) VALUES('{0}','{1}','{2}')" -f $tidnu, $NodeName, "No"
$cmd.ExecuteNonQuery()
}
}
This is working okay, but when i need to move to another log file name, i have simply made the same code again with different inputs.
What i would like to do, is to use an Array, and a foreach loop, so i could specify all the log files in one array, like:
$LogArray = #(Log1.log, log2.log, log3.log)
And specify all the Nodenames like:
$NodeArray = #(Node1, Node2, Node3)
And then make a foreach loop that will go through the logfiles one by one and insert into the databse, with it's matching nodename every time the loop runs through.
Can someone help me to make this happen? I have the idea on how it should be done, but I can't figure out how to write the code. All help would be much appreciated.
EDIT:
Ok, this is what i have now then, but i'm not sure that it's correct put together. Its giving me some strange results.
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=PCDK03918;Initial Catalog=Rman;Integrated Security=SSPI;"
$conn.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $conn
$tidnu = (Get-Date -f dd.MM.yyyy)
$Path = "C:\RMAN"
$Text = "ERROR MESSAGE STACK"
$nodes = #{
'NodeName1' = 'Node1log1.log', 'Node1log2.log', 'Node1log3.log'
'NodeName2' = 'Node2log1.log', 'Node2log2.log'
}
foreach ($NodeName in $nodes.Keys) {
foreach ($Logfil in $nodes[$NodeName]) {
Get-ChildItem $Path -Filter "*.log" |
ForEach-Object {
If (Get-Content $_.FullName | Select-String -Pattern $Text)
{
$cmd.commandtext = "INSERT INTO Error (Datotid, Nodename, Logfil, Error) VALUES('{0}','{1}','{2}','{3}')" -f $tidnu, $NodeName, $Logfil, "Yes"
$cmd.ExecuteNonQuery()
}
else
{
$cmd.commandtext = "INSERT INTO Error (Datotid, Nodename, Logfil, Error) VALUES('{0}','{1}','{2}','{3}')" -f $tidnu, $NodeName, $Logfil, "No"
$cmd.ExecuteNonQuery()
}
}
}
}
$conn.close()
I have created the log files mentioned in $nodes, in the folder, and put the "ERROR MESSAGE STACK" into Node1log1.log and Node1log2.log The rest of the log files are with no "ERROR MESSAGE STACK" inside.
But the result in the database is strange. It says Error = Yes to log files with no "ERROR MESSAGE STACK" inside, and it says Error = No to the same log files some rows down. Plus its inserting double rows and all in all its not doing as intended.
could it be because my
Get-ChildItem $Path -Filter "*.log" |
is wrong by using *.log ?
Or am I simply going completely wrong about this?
EDIT Once more:
Not sure what I was thinking yesterday, but I believe i have solved it now.
Get-ChildItem $Path -Filter "*.log" |
Will of course not work.
Get-ChildItem $Path -Filter $logfil |
Gives much more sense, and now my databse output is looking much more correct.
#Ansgar Wiechers - Thank you for pointing me in the right direction. I learned alot from this.
Consider using a hashtable for this:
$logs = #{
'Log1.log' = 'Node1'
'Log2.log' = 'Node2'
'Log3.log' = 'Node3'
}
That way you can iterate over the logs like this:
foreach ($Logfil in $logs.Keys) {
$NodeName = $logs[$Logfil]
...
}
If you have more than one log file per node name, it would be more efficient to reverse the mapping and store the log file names in an array:
$nodes = #{
'Node1' = 'Log1.log', 'Log2.log', 'Log3.log'
'Node2' = 'Log4.log', 'Log5.log'
}
Then you can process the logfiles with a nested loop like this:
foreach ($NodeName in $nodes.Keys) {
foreach ($Logfil in $nodes[$NodeName]) {
...
}
}
You should be able to fit your pipeline into either loop without further modifications.
Edit: As an optimization you could do something like this to avoid needlessly fetchin logs with each iteration of the outer loop:
$logs = Get-ChildItem $Path -Filter '*.log'
foreach ($NodeName in $nodes.Keys) {
$logs | ? { $nodes[$NodeName] -contains $_.Name } | % {
...
}
}
I feel like perhaps I am overlooking something simple here, but I am having trouble with a ForEach loop in PowerShell actually returning all items that I expect. I have a script that will query an Oracle database and gather up the base data set. Once this is gathered, I will need to perform some adjustments to what is returned and build an additional bit of information (not in the script currently, working through the basics so far)
What I am doing is adding the data to an array, then trying to use a ForEach loop to examine each item in the array and pump that data out to another array that will have the new properties that I need to populate based on some calculations of the base data set. What I am getting returned to the variable $finaloutput is only one line of the data (for the example I am posting here I simply look for one reportnumber equal to CPOD-018, which there are 5 of in the data set with varying other properties populated including the sitename which I am populate as well, but still only get one result).
I've tried going about this using nested if statements within the ForEach loop instead of the piped Where-Object, but received the same results. Below is the current version of the script, any assistance would be greatly appreciated.
param(
[parameter(mandatory=$True)]$username,
[parameter(mandatory=$True)]$password
)
# setup the finaloutput variable
$finaloutput = New-Object psobject
$finaloutput | Add-Member -MemberType NoteProperty -name ReportNumber -value NotSet
$finaloutput | Add-Member -MemberType NoteProperty -name sitename -value NotSet
# the connection string to be used by the OlEDB connection
$connString = #"
Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST="host.host.host")(PORT="1521"))
(CONNECT_DATA=(SERVICE_NAME="name.name.name")));User ID="$username";Password="$password"
"#
# the query that will be used to gather data from Oracle
$qry= #"
select VP_EXPECTED_DETAILS.REPORTNUMBER, VP_EXPECTED_DETAILS.SITE_NAME, VP_ACTUAL_FILENAME_DETAILS.FILE_NAME, VP_EXPECTED_DETAILS.MAX_EXPECTED_LOAD_TIME, VP_EXPECTED_DETAILS.EXPECTED_FREQUENCY,
VP_EXPECTED_DETAILS.DATE_TIMING, VP_EXPECTED_DETAILS.FREQUENCY_DAY, VP_EXPECTED_DETAILS.JOB_NO,
TO_CHAR(VP_ACTUAL_RPT_DETAILS.GEN_PARSE_IN,'YYYYMMDDHH24MISS') AS GEN_PARSE_IN,
TO_CHAR(VP_ACTUAL_RPT_DETAILS.GEN_PARSE_OUT,'YYYYMMDDHH24MISS') AS GEN_PARSE_OUT,
TO_CHAR(VP_ACTUAL_RPT_DETAILS.ETLLOADER_IN,'YYYYMMDDHH24MISS') AS ETLLOADER_IN,
TO_CHAR(VP_ACTUAL_RPT_DETAILS.ETLLOADER_OUT,'YYYYMMDDHH24MISS') AS ETLLOADER_OUT
from MONITOR.VP_EXPECTED_DETAILS
LEFT JOIN MONITOR.VP_ACTUAL_RPT_DETAILS on VP_EXPECTED_DETAILS.REPORTNUMBER = VP_ACTUAL_RPT_DETAILS.REPORTNUMBER and VP_EXPECTED_DETAILS.SITE_NAME = VP_ACTUAL_RPT_DETAILS.SITE_NAME
LEFT JOIN MONITOR.VP_ACTUAL_FILENAME_DETAILS on VP_ACTUAL_RPT_DETAILS.FNKEY = VP_ACTUAL_FILENAME_DETAILS.FNKEY where VP_EXPECTED_DETAILS.EXPECTED_FREQUENCY = 'DAILY' or
(VP_EXPECTED_DETAILS.EXPECTED_FREQUENCY = 'MONTHLY' AND VP_EXPECTED_DETAILS.FREQUENCY_DAY = EXTRACT(DAY from SYSDATE))
"#
# the function that will open the database connection and execute the query
function Get-OLEDBData ($connectstring, $sql) {
$OLEDBConn = New-Object System.Data.OleDb.OleDbConnection($connectstring)
$OLEDBConn.open()
$readcmd = New-Object system.Data.OleDb.OleDbCommand($sql,$OLEDBConn)
$readcmd.CommandTimeout = '300'
$da = New-Object system.Data.OleDb.OleDbDataAdapter($readcmd)
$dt = New-Object System.Data.DataTable
[void]$da.fill($dt)
$OLEDBConn.close()
return $dt
}
# populate $output with the data from the Get-OLEDBData function
$output = Get-OLEDBData $connString $qry
# build the final output that will generate alerts
ForEach ($lines in $output | Where-Object {$_.reportnumber -eq "CPOD-018"})
{
$finaloutput.reportnumber = $lines.reportnumber
$finaloutput.sitename = $lines.SITE_NAME
}
$finaloutput
It looks like what is happening is you are doing the object creation incorrectly. Inside the foreach loop you keep overwriting the same values rather than appending new objects
It should look more like this:
$FinalOutput = ForEach ($lines in $output | Where-Object {$_.reportnumber -eq "CPOD-018"})
{
$Prop = #{
'reportnumber' = $lines.reportnumber
'sitename' = $lines.SITE_NAME
}
New-Object -Type PSObject -Property $Prop
}
$FinalOutput
You would need to comment out the finaloutput lines earlier in your script.