Powershell Convert Table To Array - powershell

I'm trying to run a query in WSUS API via Powershell that outputs computer names, needed patches, etc, and then I need to inject that into a "log" file which gets ingested into Splunk so we can make dashboards etc.
My current code is
$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) |
Select-Object $logtime,#{L=’ComputerTarget';E={($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName}},
#{L=’NeededCount';E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount | Select-String Computer
Output comes out like this:
#{05-13-2016_05-12-25=; ComputerTarget=########; NeededCount=12; DownloadedCount=0; NotApplicableCount=82245; NotInstalledCount=12; InstalledCount=23; FailedCount=0}
I need it to look like this:
05-13-2016_05-12-25=; ComputerTarget=#######; NeededCount=12; DownloadedCount=0; NotApplicableCount=82245; NotInstalledCount=12; InstalledCount=23; FailedCount=0
If you want to try the root of the problem, I'm trying to convert a table into arrays so splunk can read it line by line but this gives a table which i'm trying to convert:
$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) |
Select-Object $logtime,#{L=’ComputerTarget';E={($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName}},
#{L=’NeededCount';E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount `
which gives output:
05-13-2016_05-16-04 :
ComputerTarget : ########
NeededCount : 12
DownloadedCount : 0
NotApplicableCount : 82245
NotInstalledCount : 12
InstalledCount : 23
FailedCount : 0

It looks like you only want to remove the leading #{ and trailing } which you could do with regex like this:
...allyourcode... | Select-String Computer | ForEach-Object { $_.Line -replace '^\#\{(.*?)\}$', '$1' }
However, making Select-String convert your objects to string-representations is a bad way to export data. Splunk can read CSV-files, so I would recommend using that (and also use a real property for logtime). Ex:
$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) |
Select-Object #{L="LogTime";e={ $logtime }},#{L=’ComputerTarget';E={($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName}},
#{L=’NeededCount';E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount |
Export-Csv -Path mydata.csv -NoTypeInformation

Related

Powershell - Separate output based on Server + export to Excel

Running a sql script for a list of databases.
The output on powershell is not separated. How could I separate them based on server name.
$SERVERS = gc "C:\Users\listOfServers.txt"
foreach ($SERVER in $SERVERS) {
$InvokeParams = #{
Server = $SERVER
Database = 'test'
Username = 'admin'
Password = 'testpassword'
InputFile = 'C:\business.sql'
}
Invoke-SqlCmd #InvokeParams
}
Right now my output looks like this :
ValueDate: 1/30/2019 12:00:00 AM
PrevValueDate: 1/29/2019 12:00:00 AM
Count:100
ValueDate: 3/30/2019 12:00:00 AM
PrevValueDate: 3/29/2019 12:00:00 AM
Count:200
ValueDate: 4/30/2019 12:00:00 AM
PrevValueDate: 4/29/2019 12:00:00 AM
Count:2100
ValueDate: 11/30/2019 12:00:00 AM
PrevValueDate: 11/29/2019 12:00:00 AM
Count:12200
Goal is : Server 1 (output server 1) server 2 (output server 2)
I would like to add a parameter that gives the server name for each output- or sort like an Id to separate them.
Goal is to export the output into an Excel sheet - not working at the moment
$out = Invoke-SqlCmd #InvokeParams | Format-Table
$path = 'C:\Users\test1.csv'
$out | Export-Csv -Path $path
Invoke-Item -Path $path
You can use calculated properties to achieve this:
$SERVERS = gc "C:\Users\listOfServers.txt"
$out = foreach ($SERVER in $SERVERS) {
$InvokeParams = #{
Server = $SERVER
Database = 'test'
Username = 'admin'
Password = 'testpassword'
InputFile = 'C:\business.sql'
}
Invoke-SqlCmd #InvokeParams | Select-Object -Property *, #{L='Server'; E={$SERVER}}
}
$path = 'C:\Users\test1.csv'
$out | Export-Csv -Path $path
Invoke-Item -Path $path

CSV misalignment when reading in powershell

I have a CSV with a lot of headers. It ranges from Column A to AZ. I need to read only the first column of the CSV.
My issue is the last column (comments) bleeds into the first column when opened in notepad++ and it appears to show that it is apart of Column one (host name). When I run my code it reads it as notepad++ does.
I originally convert this from an .xls to a CSV file, is there a way to ensure the last column does not wrap around?
#region - Convert to CVS
$strFileName = "C:\working\Server Count & Status Check ARC 0219183.xlsx"
$strSheetName = 'SCI$'
$strProvider = "Provider=Microsoft.ACE.OLEDB.12.0"
$strDataSource = "Data Source = $strFileName"
$strExtend = "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1';"
$strQuery = "Select * from [$strSheetName]"
$objConn = New-Object
System.Data.OleDb.OleDbConnection("$strProvider;$strDataSource;$strExtend")
$sqlCommand = New-Object System.Data.OleDb.OleDbCommand($strQuery)
$sqlCommand.Connection = $objConn
$objConn.open()
$da = New-Object system.Data.OleDb.OleDbDataAdapter($sqlCommand)
$dt = New-Object system.Data.datatable
$da.fill($dt)
$dt | Export-Csv "C:\working\Server Count & Status Check ARC 0219183.csv" -
NoTypeInformation
$objConn.close()
#endregion
Original Spreadsheet
After trying to gather first column(hostname)
$array=#{}
$ServerStatus = Get-Content "C:\Users\user\Desktop\Scripts\Linux Server
Compare\Server Count & Status Check ARC 0219183.csv" | select -skip 1 |
ConvertFrom-Csv -Header "HostName","Business Application","Technical
Function","Location","Environment","Day Number","Slot
Number","SlotTime","Description","DNS Name","Key Server","Host Name","Public
IP","Server Type","Build Date","Builder","Product Name","Serial
No","Enclosure","Bay","CPU Model","CPU Socket","CPU Cores","Logical
CPU","Memory","Up Time","Kernel Version","OS Type","Version","Patch
level","Multipath Disks?","Multipath Checker?","Customized Fsck?","Splx
Installed?","Splx Running?","Suse Manager Status","RHN Running ?","RHN
Autostart?","Installed Kernel","Available Kernel in
/boot","Hardened","Syslog
Config correct ?","Syslog Running ?","Patrol Running?","Splunk
Installed?","Splunk Running?","Puppet Installed?","Puppet
Running?","Centrify
Version","VM Tools Installed?","VM Tools Running?","Comments"
$ServerStatusObj = new-object Object
$ServerStatusObj | add-member -membertype NoteProperty "Name" -value ""
$ServerStatusObj | add-member -membertype NoteProperty "Origin" -value ""
$array=#{name = $ServerStatus.HostName}
foreach ($name in $array.name){
if($name -ne $null){
$Value = $name
if($name -ne $null){
$ServerStatusObj.name =$Value
$ServerStatusObj.Origin = " Linux Servers"
} #End of IF
else {Write-Output "null"}
Write-Output $ServerStatusObj | export-csv
"C:\Users\user\Desktop\Scripts\Linux Server Compare\ServerStatusParse.csv" -notypeinformation -Append
}

Retrieve data from last line in vmware.log file?

I currently have a script that retrieves the last modified date of the .vmx in a VM's datastore in vCenter. I need to make changes to instead use and display the last date in the vmware.log file (located in the same datastore as the .vmx)
I'm not sure how to grab that line and convert it to a XX/XX/XXXX format. In the log file, it shows it as Dec 23 10 for example. If this is not possible, no worries. I just need to pull the last line in the log file and export it to a .csv file. Below is my current code:
add-pssnapin VMware.VimAutomation.Core
# ---------- Only modify the fields in this area -------------
$vCenter = 'qlab-copsmgr' #name of the vCenter
$dataCenter = 'Fly-away Kit' #name of the DataCenter
$outputFile = $vCenter + '-LastDateUsed.csv' #desired output file name
# ---------- No modification is needed in the below code. Do not edit -------------
$columnName = "Name,DataStore,Date Last Used" | Out-File .\$OutputFile -Encoding ascii
Connect-VIServer $vCenter -WarningAction SilentlyContinue
$vmList = Get-VM | where { $_.PowerState -eq “PoweredOff”} | select Name
$vmList = $vmList -replace 'Name : ', '' -replace '#{Name=', '' -replace '}', ''
ForEach ($VM in $vmList)
{
# Get configuration and path to vmx file
$VMconfig = Get-VM $VM | Get-View | select config
$VMXpath = $VMconfig.config.files.VMpathName
# Remove and/or replace unwanted strings
$VMXpath = $VMXpath -replace '\[','' -replace '\] ','\' -replace '#{Filename=','/' -replace '}','' -replace '/','\'
# List the vmx file in the datastore
$VMXinfo = ls vmstores:\$VCenter#443\$DataCenter\$VMXpath | Where {$_.LastWriteTime} | select -first 1 | select FolderPath, LastWriteTime
# Remove and/or replace unwanted strings
$VMXinfo = $VMXinfo -replace 'DatastoreFullPath=', '' -replace '#{', '' -replace '}', '' -replace ';', ',' -replace 'LastWriteTime=', ''
# Output vmx information to .csv file
$output = $VM + ', ' + $VMXinfo
$output
echo $output >> $OutputFile
}
I also needed to pull the last event from the vmware.log file in order to backtrack the power off time for VMs where there is no vCenter event history. I looked at file timestamps but found that some VM processes and possibly backup solutions can make them useless.
I tried reading the file in place but ran into issues with the PSDrive type not supporting Get-Content in place. So for better or worse for my solution I started with one of LucD's scripts - the 'Retrieve the logs' script from http://www.lucd.info/2011/02/27/virtual-machine-logging/ which pulls a VMs vmware.log file and copies it to local storage. I then modified it to copy the vmware.log file to a local temp folder, read the last line from the file before deleting the file and return the last line of the log as a PS object.
Note, this is slow and I'm sure my hacks to LucD's script are not elegant, but it does work and I hope if helps someone.
Note: This converts the time value from the log to a PS date object by simple piping the string timestamp from the file into Get-Date. I've read that this does not work as expected for non-US date formatting. For those outside of the US you might want to look into this or just pass the raw timestamp string from the log instead of converting it.
#Examples:
#$lastEventTime = (Get-VM -Name "SomeVM" | Get-VMLogLastEvent).EventTime
#$lastEventTime = Get-VMLogLastEvent -VM "SomeVM" -Path "C:\alternatetemp\"
function Get-VMLogLastEvent{
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)][PSObject[]]$VM,
[string]$Path=$env:TEMP
)
process{
$report = #()
foreach($obj in $VM){
if($obj.GetType().Name -eq "string"){
$obj = Get-VM -Name $obj
}
$logpath = ($obj.ExtensionData.LayoutEx.File | ?{$_.Name -like "*/vmware.log"}).Name
$dsName = $logPath.Split(']')[0].Trim('[')
$vmPath = $logPath.Split(']')[1].Trim(' ')
$ds = Get-Datastore -Name $dsName
$drvName = "MyDS" + (Get-Random)
$localLog = $Path + "\" + $obj.Name + ".vmware.log"
New-PSDrive -Location $ds -Name $drvName -PSProvider VimDatastore -Root '\' | Out-Null
Copy-DatastoreItem -Item ($drvName + ":" + $vmPath) -Destination $localLog -Force:$true
Remove-PSDrive -Name $drvName -Confirm:$false
$lastEvent = Get-Content -Path $localLog -Tail 1
Remove-Item -Path $localLog -Confirm:$false
$row = "" | Select VM, EventType, Event, EventTime
$row.VM = $obj.Name
($row.EventTime, $row.EventType, $row.Event) = $lastEvent.Split("|")
$row.EventTime = $row.EventTime | Get-Date
$report += $row
}
$report
}
}
That should cover your request, but to expound further on why I needed the detail, which reading between the lines may also benefit you, I'll continue.
I inherited hundreds of legacy VMs that have been powered off from various past acquisitions and divestitures and many of which have been moved between vCenter instances losing all event log detail. When I started my cleanup effort in just one datacenter I had over 60TB of powered off VMs. With the legacy nature of these there was also no detail available on who owned or had any knowledge of these old VMs.
For this I hacked another script I found, also from LucD here: https://communities.vmware.com/thread/540397.
This will take in all the powered off VMs, attempt to determine the time powered off via vCenter event history. I modified it to fall back to the above Get-VMLogLastEvent function to get the final poweroff time of the VM if event log detail is not available.
Error catching could be improved - this will error on VMs where for one reason or another there is no vmware.log file. But quick and dirty I've found this to work and provides the detail on what I need for over 90%.
Again this relies on the above function and for me at least the errors just fail through passing through null values. One could probably remove the errors by adding a check for vmware.log existance before attempting to copy it though this would add a touch more latency in execution due to the slow PSDrive interface to datastores.
$Report = #()
$VMs = Get-VM | Where {$_.PowerState -eq "PoweredOff"}
$Datastores = Get-Datastore | Select Name, Id
$PowerOffEvents = Get-VIEvent -Entity $VMs -MaxSamples ([int]::MaxValue) | where {$_ -is [VMware.Vim.VmPoweredOffEvent]} | Group-Object -Property {$_.Vm.Name}
foreach ($VM in $VMs) {
$lastPO = ($PowerOffEvents | Where { $_.Group[0].Vm.Vm -eq $VM.Id }).Group | Sort-Object -Property CreatedTime -Descending | Select -First 1
$lastLogTime = "";
# If no event log detail, revert to vmware.log last entry which takes more time...
if (($lastPO.PoweredOffTime -eq "") -or ($lastPO.PoweredOffTime -eq $null)){
$lastLogTime = (Get-VMLogLastEvent -VM $VM).EventTime
}
$row = "" | select VMName,Powerstate,OS,Host,Cluster,Datastore,NumCPU,MemMb,DiskGb,PoweredOffTime,PoweredOffBy,LastLogTime
$row.VMName = $vm.Name
$row.Powerstate = $vm.Powerstate
$row.OS = $vm.Guest.OSFullName
$row.Host = $vm.VMHost.name
$row.Cluster = $vm.VMHost.Parent.Name
$row.Datastore = $Datastores | Where{$_.Id -eq ($vm.DatastoreIdList | select -First 1)} | Select -ExpandProperty Name
$row.NumCPU = $vm.NumCPU
$row.MemMb = $vm.MemoryMB
$row.DiskGb = Get-HardDisk -VM $vm | Measure-Object -Property CapacityGB -Sum | select -ExpandProperty Sum
$row.PoweredOffTime = $lastPO.CreatedTime
$row.PoweredOffBy = $lastPO.UserName
$row.LastLogTime = $lastLogTime
$report += $row
}
# Output to screen
$report | Sort Cluster, Host, VMName | Select VMName, Cluster, Host, NumCPU, MemMb, #{N='DiskGb';E={[math]::Round($_.DiskGb,2)}}, PoweredOffTime, PoweredOffBy | ft -a
# Output to CSV - change path/filename as appropriate
$report | Sort Cluster, Host, VMName | Export-Csv -Path "output\Powered_Off_VMs_Report.csv" -NoTypeInformation -UseCulture
Cheers!
I pray this pays back some of the karma I've used.
Meyeaard
I have made a script that checks line by line and if string is found changes it to desired format
#example input you can use get-content PATH to txt or any file and assign it to $lines variable
$lines = #"
ernfoewnfnsf
ernfoewnfnsf
Dec 23 10 sgdsgdfgsdadasd
"# -split "\r\n"
#checks line by line and if find anything that maches start of the line, one Big letter two small, space, two digits, space, two digits, space
$lines | ForEach-Object{
if ($_ -match "^[A-Z][a-z]{2}\s\d{2}\s\d{2}\s")
{
$match = [convert]::ToDateTime($matches[0])
$_ -replace $matches[0], "$($match.ToShortDateString()) " | out-file { PATH } -APPEND
}
else
{
$_ | out-file { PATH } -APPEND
}
}
just change {PATH} with a filenamePAth and this should work for you

In Powershell, how do I print out only the data from today(or a specific date range)?

I have a shared Outlook folder, where I need to extract 6-digit numbers from Email subject lines. So I use the following script :
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $outlook.GetNameSpace("MAPI")
$SharedMB = $NameSpace.Folders | Where{$_.Name -match "FDA UFMS User Provision"}
$OtherFldr = $SharedMB.Folders | Where{$_.Name -match "Inbox"}
[datetime]$StartDate = ([datetime]::now.ToShortDateString())
$TodaysMail = #()
for($i = ($OtherFldr.Items.count - 1);$i -ge 0;$i--){
$Current = $OtherFldr.Items.item($i)
if($Current.senton -lt (get-date $StartDate)){break}
else { $OtherFldr.Items | %{ $RESULT=[Regex]::Match ($_.TaskSubject, "Request\s\d{6}"); if ($RESULT.Success){$RESULT.
Value}} | %{$Result=[Regex]::Match($_, "\d{6}"); if ($RESULT.Success){$RESULT.Value}} | Out-File C:\Temp\powerfish4.txt
-Append }
$TodaysMail += $Current
}
But this throws up a strange error to me :
The funny thing, is that if I simply dump all the numbers into a text file, it does not have this permission error. That is, I can run this script just fine :
$OtherFldr.Items | %{ $RESULT=[Regex]::Match($_.TaskSubject, "Request\s\d{6}"); if ($RESULT.Success)
{$RESULT.Value}} | %{$Result=[Regex]::Match($_, "\d{6}"); if($RESULT.Success){$RESULT.Value}} | Out-File C:\Temp\powerfish2.txt -Append
I'm currently trying to play with Outlook's offline-mode.
any tips appreciated , thanks
This is just an assumption but maybe trying to copy the entire item requires Outlook to be online.(Maybe Outlook is not caching the entire item)
$Current = $OtherFldr.Items.item($i)
I don't have a list of properties that Outlook holds as a cache but maybe just dealing with tasksubject and senton might be available in cache.
Also modified the for loop to make it more simple.
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $outlook.GetNameSpace("MAPI")
$SharedMB = $NameSpace.Folders | Where{$_.Name -match "FDA UFMS User Provision"}
$OtherFldr = $SharedMB.Folders | Where{$_.Name -match "Inbox"}
[datetime]$StartDate = ([datetime]::now.ToShortDateString())
ForEach($Item in $OtherFldr.Items){
if($Item.senton -lt (get-date $StartDate)){break}
if($Item.TaskSubject -Match "Request\s\d{6}")
{
$Result=[Regex]::Match($Item.TaskSubject, "\d{6}");
if($RESULT.Success){
$RESULT.Value | Out-File C:\Temp\powerfish4.txt -Append
}
}
}

Formatting Powershell output from stored procedure

I'm running into a small issue trying to get the output from a stored procedure into a text file via. Powershell.
#Connection Object
$cn = New-Object System.Data.SqlClient.SqlConnection(
"Data Source=localhost; Database=test;User ID=test;Password=xyzzy;"
)
$q = "exec usp_Users"
#Data Adapter which will gather the data using our query
$da = New-Object System.Data.SqlClient.SqlDataAdapter($q, $cn)
#DataSet which will hold the data we have gathered
$ds = New-Object System.Data.DataSet
#Out-Null is used so the number of affected rows isn't printed
$da.Fill($ds) >$null| Out-Null
#Close the database connection
$cn.Close()
if($ds.Tables[0].Rows.Count -eq 0){
write-host '0:No Data found'
exit 2
}
$file = "C:\temp\" + "users" + $(Get-Date -Format 'MM_dd_yyyy') + ".txt"
$ds.Tables[0] | out-File $file -encoding ASCII -width 255
Here is the output:
Column1
-----
USER_NAME,USER_TYPE
test#spamex.com,MasterAdministrator
foo#hotmail.com,UserAdministrator
test4#test.com,Users
How can I get rid of the 'Column1' and the underline?
select-object with expanded property might help:
ds.Tables[0] | Select-Object -expand Column1 | out-file..
You can export a datatable directly into a csv file by using export-csv:
$ds.Tables[0] | export-csv tofile.csv -notypeinformation