I have this piece of code where I am extracting table names from the adventureworks.bim file using a for each loop. However, I am missing something here because I required a Table name per object and not the final table in the loop. The details are as below
cls
$BIM = "C:\Users\Desktop\adventureworks.bim"
$origmodel = (Get-Content $BIM -Raw) | Out-String | ConvertFrom-Json
ForEach($table in $origmodel.Model.tables.name)
{
$ColumnProperty = $origmodel.Model.tables.columns | ForEach-Object {
[pscustomobject] #{
'Table Name' = $table
'Object Name' = $_.name
'DataType' = $_.dataType
}
}
}
$ColumnProperty | ConvertTo-Csv -NoTypeInformation
Result I Get
"Table Name", "Object Name", "Datatype"
"Date","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Date","CurrencyKey","int64"
"Date","Currency Code","string"
"Date","CurrencyName","string"
"Date","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Date","CustomerKey","int64"
"Date","GeographyKey","int64"
"Date","Customer Id","string"
"Date","Title","string"
"Date","First Name","string"
"Date","Middle Name","string"
"Date","Last Name","string"
"Date","Name Style","boolean",
"Date","Birth Date","dateTime",
"Date","Marital Status","string"
"Date","Suffix","string"
"Date","Gender","string"
"Date","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Date","DateKey","int64"
"Date","Date","dateTime",
"Date","Day Number Of Week","int64"
"Date","Day Name Of Week","string"
"Date","Day Of Year","int64"
"Date","Week Of Year","int64"
"Date","Month Name","string"
Result I Need
"Table Name", "Object Name", "DataType"
"Currency","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Currency","CurrencyKey","int64"
"Currency","Currency Code","string"
"Currency","CurrencyName","string"
"Customer","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Customer","CustomerKey","int64"
"Customer","GeographyKey","int64"
"Customer","Customer Id","string"
"Customer","Title","string"
"Customer","First Name","string"
"Customer","Middle Name","string"
"Customer","Last Name","string"
"Customer","Name Style","boolean",
"Customer","Birth Date","dateTime",
"Customer","Marital Status","string"
"Customer","Suffix","string"
"Customer","Gender","string"
"Date","RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61","int64"
"Date","DateKey","int64"
"Date","Date","dateTime",
"Date","Day Number Of Week","int64"
"Date","Day Name Of Week","string"
"Date","Day Of Year","int64"
"Date","Week Of Year","int64"
"Date","Month Name","string"
Try this on for size:
$BIM = "C:\Users\Desktop\adventureworks.bim"
$origmodel = Get-Content $BIM -Raw | ConvertFrom-Json
ForEach ($table in $origmodel.Model.tables) {
$ColumnProperty += $table.columns | ForEach-Object {
[pscustomobject] #{
'Table Name' = $table.name
'Object Name' = $_.name
'DataType' = $_.dataType
}
}
}
$ColumnProperty | ConvertTo-Csv -NoTypeInformation
Corrections
No need to use Out-String
ColumnProperty needed a += as = was overwriting every entry
Just a few mix-ups around the foreach loops.
You need to loop over .model.tables first then an inner loop for each columns:
$req = Invoke-RestMethod https://raw.githubusercontent.com/TabularEditor/TabularEditor/master/TabularEditorTest/AdventureWorks.bim
$req.model.tables | ForEach-Object {
foreach($column in $_.columns) {
[pscustomobject]#{
Table = $_.name
ObjectName = $column.name
DataType = $column.dataType
}
}
} | Export-Csv path\to\export.csv -NoTypeInformation
Output to the console would look like this for the first few objects:
Table ObjectName DataType
----- ---------- --------
Currency RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61 int64
Currency CurrencyKey int64
Currency Currency Code string
Currency CurrencyName string
Customer RowNumber-2662979B-1795-4F74-8F37-6A1BA8059B61 int64
Customer CustomerKey int64
Customer GeographyKey int64
Customer Customer Id string
Customer Title string
...
...
Using PowerShell I need to combine two arrays $IP_Diff and $Line_Diff:
$IP_Diff = "10.1.101.17"
$Line_IP = (#{N="Initialize"}).N | Select #{N = "Problem_IP";E={$IP_Diff -join ";"}}
$ServerName = "ExServer-01"
$Exist = "False"
$Line_Diff = (#{N="Initialize"}).N | Select #{N = $ServerName;E={$Exist -join ";"}}
I need the Combined Array to be:
Problem_IP ExServer-01
---------- -------------
10.1.101.17 False
It looks like you're trying to construct a [pscustomobject] instance as follows:
$IP_Diff = "10.1.101.17"
$ServerName = "ExServer-01"
$Exist = "False"
[pscustomobject] #{
Problem_IP = $IP_Diff
$ServerName = $exist
}
The above produces the display output shown in your question.
I am attempting to display information for administrators processing user changes to easily see what changes are being made to certain fields in Active Directory users. I've written a PowerShell script to process these changes, but I'm having trouble formatting the output for the administrators who are running the script. See this post for a similar, but different problem.
My code:
$output = [pscustomobject]#{'UserName' = $user.samAccountName; 'FirstName' = $user.GivenName;'LastName' = $user.Surname;'DisplayName' = $user.DisplayName;'UPN' = $user.UserPrincipalName;'E-Mail_Address' = $user.proxyAddresses;'Title' = $user.Title;'Office' = $user.PhysicalDeliveryOfficeName;'Dept' = $user.Department;}
$output[$index++] | Add-Member -NotePropertyMembers #{'NewUserName' = $samAccountName; 'NewFirstName' = $firstName;'NewLastName' = $lastname;'NewDisplayName' = $displayName;'NewUPN' = $UPN;'NewE-Mail_Address' = $SMTP1;'NewTitle' = $Title;'Newoffice' = $office;'NewDept' = $department;}
$output
Output:
UserName : FirstLast
FirstName : First
LastName : Last
DisplayName : First Last
UPN : FirstLast#company.com
E-Mail_Address : SMTP:FirstLast#company.com
Title : CEO
Office : HQ
Dept : Executives
NewDept : Maintenance
NewE-Mail_Address : SMTP:FirstLast#company.com
NewOffice : The Dump
NewFirstName : First
NewLastName : Last
NewUserName : FirstLast
NewDisplayName : First Last
NewUPN : FirstLast#company.com
NewTitle : Trash Collector
Desired Output:
UserName : FirstLast NewUserName : FirstLast
FirstName : First NewFirstName : First
LastName : Last NewLastName : Last
DisplayName : First Last NewDisplayName : First Last
UPN : FirstLast#company.com NewUPN : FirstLast#company.com
E-Mail_Address : SMTP:FirstLast#company.com NewE-Mail_Address : SMTP:FirstLast#company.com
Title : CEO NewTitle : Trash Collector
Office : HQ Newoffice : The Dump
Dept : Executives NewDept : Maintenance
Below will create the two Format-List outputs next to eachother.
Because more than likely this will not fit the width of the console screen, the output is captured first and output using Out-String with a high number for parameter -Width.
Like this, you can also save to text file.
$outputOld = [pscustomobject]#{
'UserName' = $user.samAccountName; 'FirstName' = $user.GivenName;
'LastName' = $user.Surname;'DisplayName' = $user.DisplayName;
'UPN' = $user.UserPrincipalName;'E-Mail_Address' = $user.proxyAddresses;
'Title' = $user.Title;'Office' = $user.PhysicalDeliveryOfficeName;
'Dept' = $user.Department}
$outputNew = [pscustomobject]#{
'NewUserName' = $samAccountName; 'NewFirstName' = $firstName;
'NewLastName' = $lastname;'NewDisplayName' = $displayName;'NewUPN' = $UPN;
'NewE-Mail_Address' = $SMTP1;'NewTitle' = $Title;'Newoffice' = $office;
'NewDept' = $department}
# capture the Format-List output of each object and split into a string array
$outLeft = #(($outputOld | Format-List | Out-String) -split '\r?\n')
$outRight = #(($outputNew | Format-List | Out-String) -split '\r?\n')
# determine the maximum length of each string in the list that goes to the left
$maxLength = ($outLeft | Measure-Object -Property Length -Maximum).Maximum
# get the maximum number of lines
$maxLines = [math]::Max($outLeft.Count, $outRight.Count)
# collect the combined output
$result = for ($i = 0; $i -lt $maxLines; $i++) {
$left = if ($i -lt $outLeft.Count) { "{0,-$maxLength}" -f $outLeft[$i] } else { ' ' * $maxLength }
$right = if ($i -lt $outRight.Count) { $outRight[$i] } else {''}
('{0} {1}' -f $left, $right).TrimEnd()
}
# display on screen
$result | Out-String -Width 1000
# save to text file
$result | Out-String -Width 1000 | Set-Content -Path 'D:\Test\output.txt'
This doesn't produce exactly the same character-by-character output as you're after, but it does produce something similar using the built-in formatting methods without lots of stringifying of values.
First, set up some test data, which would need a small change to your sample code to generate as two objects, instead of your combined $output variable:
$oldValues = [pscustomobject] [ordered] #{
"UserName" = "FirstLast"
"FirstName" = "First"
"LastName" = "Last"
"DisplayName" = "First Last"
"UPN" = "FirstLast#company.com"
"E-Mail_Address" = "SMTP:FirstLast#company.com"
"Title" = "CEO"
"Office" = "HQ"
"Dept" = "Executives"
};
$newValues = [pscustomobject] [ordered] #{
"NewDept" = "Maintenance"
"NewE-Mail_Address" = "SMTP:FirstLast#company.com"
"NewOffice" = "The Dump"
"NewFirstName" = "First"
"NewLastName" = "Last"
"NewUserName" = "FirstLast"
"NewDisplayName" = "First Last"
"NewUPN" = "FirstLast#company.com"
"NewTitle" = "Trash Collector"
};
Next, we map the variables into an array of PropertyName, OldValue, NewValue objects:
$rows = $oldValues.psobject.properties `
| foreach-object {
$property = $_;
[pscustomobject] [ordered] #{
"PropertyName" = $property.Name
"OldValue" = $property.Value
"NewValue" = $newValues.("New" + $property.Name)
}
};
And then we can format it using the built-in cmdlets:
$rows | format-table
# PropertyName OldValue NewValue
# ------------ -------- --------
# UserName FirstLast FirstLast
# FirstName First First
# LastName Last Last
# DisplayName First Last First Last
# UPN FirstLast#company.com FirstLast#company.com
# E-Mail_Address SMTP:FirstLast#company.com SMTP:FirstLast#company.com
# Title CEO Trash Collector
# Office HQ The Dump
# Dept Executives Maintenance
The nice thing is you can adjust things like column widths, word wrap, etc just using the Format-Table parameters, or even use Format-List instead:
$rows | format-list
# ... etc ...
#
# PropertyName : Title
# OldValue : CEO
# NewValue : Trash Collector
#
# PropertyName : Office
# OldValue : HQ
# NewValue : The Dump
#
# ... etc ...
And you can filter it to show just changed values:
$rows | where-object { $_.NewValue -ne $_.OldValue } | format-table
# PropertyName OldValue NewValue
# ------------ -------- --------
# Title CEO Trash Collector
# Office HQ The Dump
# Dept Executives Maintenance
It relies on the property names being in the format "Xyz -> NewXyz", but there's ways around that if it's not true in all cases...
you can use ANSI ESCAPE codes to apply decorations. This sample is based in pscustomobject with all values, but you can create two pscustomobject to make clear the code.
Notes:
Change ESC to $([char]27) or `e in Ps6 (thank you Mr. mclayton)
I prefer use Add-Member item per item.
Tested with PowerShell 5.1 ($PSVersionTable command).
$output = [pscustomobject]#{'UserName' = 'User1'; 'FirstName' = '$user.GivenName';'LastName' = '$user.Surname';'DisplayName' = '$user.DisplayName';'UPN' = '$user.UserPrincipalName';'E-Mail_Address' = '$user.proxyAddresses';'Title' = '$user.Title';'Office' = '$user.PhysicalDeliveryOfficeName';'Dept' = '$user.Department';}
$output | Add-Member -NotePropertyMembers #{'NewUserName' = 'User2'}
$output | Add-Member -NotePropertyMembers #{'NewFirstName' = '$firstName'}
$output | Add-Member -NotePropertyMembers #{'NewLastName' = '$lastname'}
$output | Add-Member -NotePropertyMembers #{'NewDisplayName' = '$displayName'}
$output | Add-Member -NotePropertyMembers #{'NewUPN' = '$UPN'}
$output | Add-Member -NotePropertyMembers #{'NewE-Mail_Address' = '$SMTP1'}
$output | Add-Member -NotePropertyMembers #{'NewTitle' = '$Title'}
$output | Add-Member -NotePropertyMembers #{'Newoffice' = '$office'}
$output | Add-Member -NotePropertyMembers #{'NewDept' = '$department'}
# Counter for column 1 and column 2
$Counter =0
$Counter1 =0
# Temporally variables
$Text
$Label
# Do the magic
foreach( $property in $output.psobject.properties.name )
{
if ( $Counter -lt 9 )
{
$Counter = $Counter +1
$Text = $output.$property
$Label = $property
echo "ESC[$Counter;1H $Label : $Text "
} else
{
$Counter1 = $Counter1 +1
$Text = $output.$property
$Label = $property
echo "ESC[$Counter1;60H $Label : $Text "
}
}
#Move to end
echo "ESC[20;1H "
Result
Other ANSI commands
Happy coding my friends.
I have the following powershell code:
$data = #{};
// code to populate data, sample:
$listData = New-Object System.Collection.Generic.List[object];
$listData.Add([PSCustomObject]#{Id = 1, Name = "Employee1"});
$data.Add("KEY1", $listData);
$data.Values | Export-Csv -Path "path of file"
The result is not as expected, I get information about the the list Capacity, count, if readonly....
There were two typos in the question, but the basic idea works fine:
$data = #{}
$listData = New-Object System.Collections.Generic.List[object]
#^ missing s
$listData.Add([PSCustomObject]#{Id = 1; Name = "Employee1"})
#^ hash tables are semicolon-separated
$data.Add("KEY1", $listData)
$data.Values | Export-Csv -Path "path of file"
# outputs:
Id Name
-- ----
1 Employee1
I'm using DBATools Module Invoke-DBQQuery ( based on Invoke-SQLCMD2) to query a SQL database.
The query returns single record as a PSObject called $Results that looks like this...
FileName : C12345
BADGENUMBER : BADGENUMBER=12345
LASTNAME : LASTNAME=SMITH
FIRSTNAME : FIRSTNAME=JOHN
CIA : CIA=YES
SOCIALSECURITY : SOCIALSECURITY=999999999
DACDATE : DACDATE=07/16/2022
UIC : UIC=42158
I need to output this PSObject to a TXT file with just the values no
Field Titles one field on each row. that looks like this...
C12345
BADGENUMBER=12345
LASTNAME=SMITH
FIRSTNAME=JOHN
CIA=YES
SOCIALSECURITY=999999999
DACDATE=07/16/2022
UIC=42158
How do I go about producing the test file in the format I need?
$Results| Out-File c:\test.test.txt
produces the first output I listed.
Appreciate any assistance anyone can provide.
-MARK-
Here some different angles to solve
$result = Invoke-DbaQuery -SqlInstance $sql -Query 'select * from MyTable' -As PSObject
# Simulate a 2 rows result
$result = [PSCustomObject]#{
FileName = 'C12345'
BADGENUMBER = 'BADGENUMBER=12345'
LASTNAME = 'LASTNAME=SMITH'
FIRSTNAME = 'FIRSTNAME=JOHN'
CIA = 'CIA=YES'
SOCIALSECURITY = 'SOCIALSECURITY=999999999'
DACDATE = 'DACDATE=07/16/2022'
UIC = 'UIC=42158'
},[PSCustomObject]#{
FileName = 'C12345'
BADGENUMBER = 'BADGENUMBER=12345'
LASTNAME = 'LASTNAME=SMITH'
FIRSTNAME = 'FIRSTNAME=JOHN'
CIA = 'CIA=YES'
SOCIALSECURITY = 'SOCIALSECURITY=999999999'
DACDATE = 'DACDATE=07/16/2022'
UIC = 'UIC=42158'
}
# Using RegEx -replace to remove the front part
($result | Out-String) -replace '(?m)^[\w\s]+:\s','' | Out-File C:\Temp\test1.txt
# Looping for every properties (they will get reordered by property name)
# I just saw Mathias suggestion and it would produce the same result
$result | Get-Member -Type NoteProperty | ForEach-Object {$result.($_.Name)} | Out-File C:\Temp\test2.txt
# Simplier solution
$output = foreach ($row in $result) {
$row.FileName
$row.BADGENUMBER
$row.LASTNAME
$row.FIRSTNAME
$row.CIA
$row.SOCIALSECURITY
$row.DACDATE
$row.UIC
"" # you probably want to have an empty row seperating each record
}
$output | Out-File C:\Temp\test3.txt