Get-SPContentDatabase export to CSV and include web application url - powershell

I'm trying to get a powershell one liner to export the information of all my SharePoint content databases into a CSV file that could then be used as the input for a mounting script.
If i run:
Get-SPContentDatabase | Select Name, DatabaseServer, MaximumSiteCount, WarningSiteCount | Export-CSV DatabaseExport.CSV
I get most of the information I need but I can't get the Web Application url. The WebApplication exports as "SPWebApplication Name=webappname", where I need the url or ID.
I'm thinking I will need to come up with it in a script, that will add loop through and add the url to the exported CSV... but it would be nice to have this in a one liner.

You can use Calculate property (a really cool feature of powershell) ! for you one line script ;)
Get-SPContentDatabase | select Name, Server, MaximumSiteCount, WarningSiteCount , #{Name="URL";Expression={$_.WebApplication.Url}} | Export-Csv TestDB.csv -NoTypeInformation

Well, in case nobody or you come up with a one-line solution, I will share my version of it:
$dbs = Get-SPContentDatabase
$result = #();
foreach($db in $dbs){
$obj = New-Object PSObject
$obj | Add-Member -Type NoteProperty -Name "URL" -Value $db.WebApplication.URL
$obj | Add-Member -Type NoteProperty -Name "Name" -Value $db.Name
$obj | Add-Member -Type NoteProperty -Name "DatabaseServer" -Value $db.Server
$obj | Add-Member -Type NoteProperty -Name "MaximumSiteCount" -Value $db.MaximumSiteCount
$obj | Add-Member -Type NoteProperty -Name "WarningSiteCount" -Value $db.WarningSiteCount
$result += $obj
}
$result | Export-Csv .\DatabaseExport.csv -NoTypeInformation
Just add/remove properties if you need. Check out this list for all the properties that you can use :)

Related

How to export file properties in csv using powershell

I wanted to export a csv-File with File-Properties from some tif-Files.
With this command
Get-ChildItem -Recurse C:\tifs\ |
ForEach-Object {$_ | add-member -name "Owner" -membertype noteproperty `
-value (get-acl $_.fullname).owner -passthru} | Sort-Object fullname |
Select FullName,CreationTime,LastWriteTime,Length,Dimensions |
Export-Csv -Force -NoTypeInformation C:\Test\export.csv
I can export a csv just fine. But as soon as I want to add properties like vertical resolution it fails. I don't quite understand why.
In order to get to the "extended" file properties (like Dimension and Resolution metadata) you have to resort to using the Windows Visual Basic shell options from inside PowerShell as Steven helpfully pointed out. Here is a code sample that should give you the result:
$files = #()
$folder = (New-Object -ComObject Shell.Application).namespace("C:\tifs")
# Loop through each file in folder
foreach ($f in $folder.Items()) {
$a = 0
# Print all the available properties (for debugging purposes)
for ($a ; $a -le 266; $a++) {
if($folder.GetDetailsOf($f, $a)) {
Write-Host "Property: $($folder.GetDetailsOf($folder.items, $a))"
Write-Host "Value: $($folder.GetDetailsOf($f, $a))"
Write-Host "Index: $($a)"
}
}
# Store data in custom PowerShell object
$obj = New-Object -TypeName PSOBJECT
# Fill each property with the file metadata (by index number)
$obj | Add-Member -MemberType NoteProperty -Name FullName -Value $folder.GetDetailsOf($f, 194)
$obj | Add-Member -MemberType NoteProperty -Name CreationTime -Value $folder.GetDetailsOf($f, 4)
$obj | Add-Member -MemberType NoteProperty -Name LastWriteTime -Value $folder.GetDetailsOf($f, 5)
$obj | Add-Member -MemberType NoteProperty -Name Length -Value $folder.GetDetailsOf($f, 1)
$obj | Add-Member -MemberType NoteProperty -Name Dimensions -Value $folder.GetDetailsOf($f, 31)
# Add custom object to a collection
$files += $obj
}
# Export collection to CSV
$files | Export-Csv -Force C:\Test\export.csv -NoTypeInformation -Encoding UTF8
From what I can tell there's no obvious PowerShell/.Net approach to getting additional file meta data. However. there are some COM based approaches.
Check Scripting Guys
And they reference this
You will still have to correlate the data. I usually do that by building hash table keyed of same values, in you can index the metadata using the path property, then use the FullName property of the file info objects to reference it, so you can the properties.

How do I write powershell output into specific columns repeatedly in a CSV file?

I have a Windows server for which I need to list the recursive folder paths of a given drive, and use that data to generate a CSV file that contains the following information:
Hostname, FolderPath
such that the desired outcome would look like this:
ServerName, G:\temp
ServerName, G:\temp\folderB
ServerName, G:\temp\folderB\folderC
ServerName, G:\temp\folder4\folder5\folder6
I am currently using the $env:computername environment variable to find the hostname of the server, and the following Powershell command to list the folders recursively:
Get-ChildItem G:\ -Name -Attributes D -Recurse
So far I've tested in my local command line with the following:
PS> $servername = $env:computername
PS> $list_folder = Get-ChildItem .\temp\ -Name -Attributes D -Recurse
PS> echo $servername > .\test.csv; $list_folder >> .\test.csv; echo '' >> .\test.csv
which generates output that looks like this (all in one column)
LOCAL_COMPUTERNAME
folder1
folderA
folder1\folder2
folder1\folder2\folder3
folderA\folderB
How would I write logic to place the given machine's hostname in column 1 of each line of the CSV file containing a new folder path, turning the above into something more like this:
LOCAL_COMPUTERNAME,folder1
LOCAL_COMPUTERNAME,folderA
LOCAL_COMPUTERNAME,folder1\folder2
LOCAL_COMPUTERNAME,folder1\folder2\folder3
LOCAL_COMPUTERNAME,folderA\folderB
You need to fetch the server name and folder paths into variables and do this:
$object = New-Object psobject
$object | Add-Member -MemberType NoteProperty -Name 'Hostname' -Value $SERVER_NAME
$object | Add-Member -MemberType NoteProperty -Name 'FolderPath' -Value $FOLDER_PATH
$object | Export-Csv 'path to destination csv' -Notypeinformation
As per the changed question:
$list_folder = Get-ChildItem 'path' -Name -Attributes D -Recurse
$server_name = $env:COMPUTERNAME
$array = #()
foreach( $folder in $list_folder ) {
$object = New-Object psobject
$object | Add-Member -MemberType NoteProperty -Name 'Hostname' -Value $server_name
$object | Add-Member -MemberType NoteProperty -Name 'FolderPath' -Value $folder
$array += $object
}
$array | Export-Csv 'path' -Notypeinformation

Test a Large Number of Registry Values in Powershell

Testing that individual registry values exist and checking them for the proper data is not overly-complicated in Powershell using Get-Item and Get-ItemProperty. What I would like to be able to do is check a large number of registry values for existence as well as data.
For example, given the following registry entries:
HKLM:\SOFTWARE\Breakfast\1001 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1003 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1004 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1005 = 1 [DWORD]
A big, ugly script that performs the test on each value and data individually isn't complicated, but I'd love to see if it is possible to throw a friendly name, registry path/value, and the desired data into an array so that we could have a function that would perform our tests.
The array could look something like this:
$registry_list = #()
$registry_list.gettype()
$registry_list += ,#('Poptarts','HKLM:\SOFTWARE\Breakfast\','1001','3')
$registry_list += ,#('Toast','HKLM:\SOFTWARE\Breakfast\','1002','3')
$registry_list += ,#('Muffins','HKLM:\SOFTWARE\Breakfast\','1003','3')
$registry_list += ,#('Bagels','HKLM:\SOFTWARE\Breakfast\','1004','3')
$registry_list += ,#('Biscuits','HKLM:\SOFTWARE\Breakfast\','1005','3')
Since I'm new to arrays, I have no idea how to feed these into a function that can output something showing only the errors
Toast
Value Missing (HKLM:\SOFTWARE\Breakfast\1002)
Biscuits
Value Set Incorrectly (HKLM:\SOFTWARE\Breakfast\1005) Desired: 3. Actual: 1
If anyone can weigh in to help figure out how a function or similar can iterate through each of the registry values it would be appreciated. The examples here are short, but I really want to be able to run hundreds of registry values through this test.
I've never been a huge fan of multidimensional arrays in PowerShell. They end up feeling very flaky or unstable. Arrays in PowerShell also suck because when you use the += operator, the system has to build a new array with the new element and then throw the old array away. It's computationally expensive.
For this case, I would create an ArrayList, and add the arrays to that. I would also probably use a HashTable for each item so I could use a name instead of an index number to refer to the items:
$registry_list = New-Object System.Collections.ArrayList;
# Use the Add() function to add records. The [void] type is here because the function
# normally returns the number of records in the ArrayList, and we don't want that to go to output.
[void]$registry_list.Add(#{Value='Poptarts';Path='HKLM:\SOFTWARE\Breakfast';Key='1001';Data='3'});
[void]$registry_list.Add(#{Value='Toast';Path='HKLM:\SOFTWARE\Breakfast';Key='1002';Data='3'});
$registry_list | ForEach-Object {
$RegistryPath = Join-Path -Path $_.Path -ChildPath $_.Key;
if (Test-Path -Path $RegistryPath) {
Write-Host "Path '$RegistryPath' exists."
$RegistryData = (Get-ItemProperty -Path $RegistryPath).($_.Value)
if ($RegistryData -eq $_.Data) {
Write-Host "Check OK. Value $($_.Value) data is set to '$RegistryData'. Desired data is '$($_.Data)'."
}
else {
Write-Host "Check Failed. Value $($_.Value) data is set to '$RegistryData'. Desired data is '$($_.Data)'."
}
}
else {
Write-Host "Path '$RegistryPath' does not exist."
}
}
Note that I have not rigorously tested this code. Notably, I'm a bit skeptical about how correct if ($RegistryData -eq $_.Data) is for all cases.
I would store the data in a CSV file:
friendlyName,key,value,data
Poptarts,HKLM:\SOFTWARE\Breakfast\,1001,3
Toast,HKLM:\SOFTWARE\Breakfast\,1002,3
Muffins,HKLM:\SOFTWARE\Breakfast\,1003,3
Bagels,HKLM:\SOFTWARE\Breakfast\,1004,3
Biscuits,HKLM:\SOFTWARE\Breakfast\,1005,3
Then loop over each row in the file
foreach($row in Import-Csv .\breakfast.csv)
{
# retrieve key
$key = Get-Item $row.key
# retrieve value
$value = $key |Get-ItemProperty -Name $row.value
# compare data
$valid = $value."$($row.value)" -eq $row.data
# output result
$outParams = #{
Object = if($valid){"$($row.friendlyName) is correct"} else {"$($row.friendlyName) is incorrect"}
ForegroundColor = #('Red','Green')[+$valid]
}
Write-Host #outParams
}
I'll leave implementation of error handling and nicer output an excercise for OP :-)
I'd highly suggest when using large arrays to use an array of objects. Creating objects makes referencing the different parts of your array very easy using properties.
You can also then use a template server that has the proper values already in place to then build the object/array of objects to then use to validate other systems.
Here is a basic example of building an object. It is more efficient to create a simple function that builds these objects for you so that you don't have so much code repetition, but this is basic way to go about it. If you want a more advanced method of creating objects, let me know and I'll post an example.
$registrySet = #()
$registryObj = New-Object -TypeName psobject
$registryObj | Add-Member -MemberType NoteProperty -Name Name -Value 'Toast'
$registryObj | Add-Member -MemberType NoteProperty -Name Key -Value 'HKLM:\SOFTWARE\Breakfast\'
$subKeySet = #()
$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1001'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '3'
$subKeySet += $subKeyObj
$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1002'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '3'
$subKeySet += $subKeyObj
$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1003'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '1'
$subKeySet += $subKeyObj
$registryObj | Add-Member -MemberType NoteProperty -Name SubKeySet -Value
$subKeySet
$registrySet += $registryObj
$registrySet | Where {$_.Name -ieq 'toast'} | select SubKeySet

PowerShell to fetch installed programs

I will be hosting a file on a remote server (read-only) and asking people to run the file on their machines to gather installed program information. I want the file to be saved to their Desktop in their user space, so that I can then have them send it to us.
I have the script, but I'm not managing to obtain information from both "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", and "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" in the same output file. I'm obviously missing something inherently obvious, as PowerShell is clearly able to do this, and I'm asking that someone please save me from my PEBKAC issue!
Thank you in advance, appreciated!
Here is my code;
$computers = "$env:computername"
$array = #()
foreach($pc in $computers){
$computername=$pc
$UninstallKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$UninstallKey="Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$computername)
$regkey=$reg.OpenSubKey($UninstallKey)
$subkeys=$regkey.GetSubKeyNames()
Write-Host "$computername"
foreach($key in $subkeys){
$thisKey=$UninstallKey+"\\"+$key
$thisSubKey=$reg.OpenSubKey($thisKey)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
$obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
$obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
$obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($thisSubKey.GetValue("InstallLocation"))
$obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
$array += $obj
}
}
$array | Where-Object { $_.DisplayName } | select ComputerName, DisplayName, DisplayVersion, Publisher | export-csv C:\Users\$env:username\Desktop\Installed_Apps.csv
Right now the following two lines set the same variable:
$UninstallKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$UninstallKey="Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
Use this:
$UninstallKey = #(
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)
Then wrap the real logic in:
$UninstallKey | ForEach-Object {
$regkey=$reg.OpenSubKey($_)
# the rest of your logic here
}

Odd PowerShell behavior and Add-Member

Can someone help me understand why the $_ works differently for the Add-Member function than it seems to for other PowerShell functions? There must be some nuance that I am missing here.
Using the sample CSV file below, the first 3 examples work fine. The fourth, which seems pretty straightforward, does not. The error Powershell returns is:
The variable '$_' cannot be retrieved because it has not been set.
Is this "normal" or have I miscoded something?
Thanks in advance.
Test.csv
Column1,Column2, Column3
Rock,1,abc
Paper,2,efg
Scissors,3,hij
$obj = Import-CSV "C:\test.csv"
(1) $obj | Add-Member -MemberType NoteProperty -Name IdNumber -value "ROCK" -PassThru| Format-Table
(2) $obj | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name IdNumber -value $_.Column1 ; $_} | Format-Table
(3) $obj | ForEach-Object { Add-Member -InputObject $_ -MemberType NoteProperty -Name IdNumber -value $_.Column1 ; $_ } | Format-Table
(4) $obj | Add-Member -MemberType NoteProperty -Name IdNumber -value $_.Column1 -PassThru| Format-Table
When you do this:
$obj | Add-Member -MemberType NoteProperty -Name IdNumber -value $_.Column1 -PassThru
You are accessing $_ outside of a pipeline bound context e.g. inside a foreach-object expression or within a scriptblock value specified for a pipeline bound parameter. The Value parameter is not pipeline bound. If it were you would be able to do this:
$obj | Add-Member -MemberType NoteProperty -Name IdNumber -value {$_.Column1} -PassThru
As it stands, the easiest way to do what you want is:
$obj | ForEach-Object { Add-Member -Inp $_ NoteProperty -Name IdNumber -Value $_.Column1 -PassThru } | Format-Table