Output arrays to CSV - arrays

I have two CSVs:
Total 19_01_16.csv:
hostname,user,path,size,creation,LastAccess,Copied,NewName,Completed
comp1,user1,\\comp1\users1\file.pst,100,17/02/2015,17/01/2016,Yes,file_user1_.pst,
comp1,user1,\\comp1\users1\file2.pst,200,17/02/2015,17/01/2016,Yes,file2_user1_.pst,
comp2,user2,\\comp2\users2\file.pst,100,17/02/2015,17/01/2016,Yes,file_user2_.pst,
PST Passwords.csv:
user,Path,Password1,Password2,Password3,Error
user1,\\comp1\users1\file.pst,openme,openme,openme,
I'm trying to merge the two with different headers and additional content.
This is what I have so far:
$a = Import-Csv "$PST_PARENT\DailyReports\Total 19_01_16.csv"
"Hostname,User,PST_Name,Original_Path,New_Path,Size,AcceptableLoss,Password1,Password2,Password3" |
Set-Content "$PST_PARENT\DailyReports\New Build.csv"
$a | foreach {
$HOSTNAME = $_.hostname
$USER = $_.User
$PATH = $_.path
$NEW_NAME = $_.NewName
$NEWPATH = "$PST_SHARE\$USER\$NEW_NAME"
$SIZE = $_.Size
$SIZE_FAIL = ( [convert]::ToSingle( $SIZE ) / 2 )
$b = Import-Csv "$PST_PARENT\DailyReports\PST Passwords.csv"
$b | foreach {
if ( $USER -like $b.user ) {
$PASSWORD1 = $b.password1
$PASSWORD2 = $b.password2
$PASSWORD3 = $b.password3
} else {
$PASSWORD1 = "none"
$PASSWORD2 = "none"
$PASSWORD3 = "none"
}
}
$HOSTNAME,$USER,$NEW_NAME,$PATH,$NEWPATH,$SIZE,$SIZE_FAIL,$PASSWORD1,$PASSWORD2,$PASSWORD3 |
Add-Content "$PST_PARENT\DailyReports\New Build.csv"
}
The output of New Build.csv looks like this:
Hostname,User,PST_Name,Original_Path,New_Path,Size,AcceptableLoss,Password1,Password2,Password3
comp1
user1
file.pst
\\comp1\users1\file.pst
\\share\PST_Storage\file_user1_.pst
100
5
none
none
none
In essence the output is working, it's just not scrolling for each line, it's putting each array onto a new line.
I tried adding | ConvertTo-Csv -NoTypeInformation but all that did was convert the arrays to numbers, they still went down not across.
Any ideas? Am I on the right line or doing the whole thing so very wrong?

$HOSTNAME,$USER,... defines an array, which is written to the output file one element per line. You need to put the list in double quotes to turn it into a comma-separated string that you can write to the output file as a single line.
"Hostname,User,PST_Name,Original_Path,New_Path,Size,AcceptableLoss,Password1,Password2,Password3" |
Set-Content "$PST_PARENT\DailyReports\New Build.csv"
$a | foreach {
...
"$HOSTNAME,$USER,$NEW_NAME,$PATH,$NEWPATH,$SIZE,$SIZE_FAIL,$PASSWORD1,$PASSWORD2,$PASSWORD3"
} | Add-Content "$PST_PARENT\DailyReports\New Build.csv"
or you construct a custom object from the elements that you can export via Export-Csv.
$a | foreach {
...
New-Object -Type PSCustomObject -Property #{
'Hostname' = $HOSTNAME
'User' = $USER
'NewName' = $NEW_NAME
'Path' = $PATH
'NewPath' = $NEWPATH
'Size' = $SIZE
'SizeFail' = $SIZE_FAIL
'Password1' = $PASSWORD1
'Password2' = $PASSWORD2
'Password3' = $PASSWORD3
}
} | Export-Csv "$PST_PARENT\DailyReports\New Build.csv" -NoType

Related

Slow array operations after DB resultset

I need help optimizing my PowerShell script.
$sorted = #()
$firsttime = 0
$j = 0
$zaehler = $results.Count-1
for ($i=0; $i -le $results.Count-1; $i++) {
$j = $i+1
while ($results.GUID[$i] -eq $results.GUID[$j]) {
$klassen = ""
$rec = $results | where {$_.GUID -eq $results.GUID[$i]}
if ($firsttime -eq 0 -or !$sorted.GUID.contains($rec[0].GUID)) {
$firsttime = 1
foreach ($item in $rec.Klasse) {
if ($klassen -eq "") {
$klassen += $item
} else {
if (!$klassen.Contains($item)) {
$klassen += "," + $item
}
}
}
$rec[0].Klasse = $klassen
$sorted += $rec[0]
}
$j = $j+1
}
Write-Host ($i/$zaehler).ToString("P") "von Schule" $schule
}
if (!$sorted) {
$results
} else {
$sorted
}
Basically in my resultset ($results) I got duplicate lines of teachers and the only difference is the class ("Klasse/Klassen") they are teaching at.
To minimize the output I am checking if the first GUID is the same as the second and then the script appends the second class to the first one. So the $sorted array has just one line per teacher with a comma-seperated string which shows all classes.
Sample line of $results:
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9A;
Schule=123456
}
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9B;
Schule=123456
}
Sample line of $sorted[0]:
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9A,9B,9C,5A;
Schule=123456
}
The sorting process (check if contains, foreach $item, add to $klassen) is pretty slow.
I would be very grateful for any kind of ideas how to optimize the script.
Maybe something like this would work:
$results | ForEach-Object {
New-Object -Type PSObject -Property $_
} | Group-Object GUID | ForEach-Object {
$teacher = $_.Group
$teacher | Select-Object -First 1 -Exclude Klasse -Property *, #{n='Klasse';e={
$teacher | Select-Object -Expand Klasse
}}
}
Convert your hashtables into custom objects, group them by GUID, then replace the original "Klasse" property with a new one containing an array of the values from all objects in that group, and select the first result.
Should $results already be a list of objects you can skip the first ForEach-Object:
$results | Group-Object GUID | ForEach-Object {
$teacher = $_.Group
$teacher | Select-Object -First 1 -Exclude Klasse -Property *, #{n='Klasse';e={
$teacher | Select-Object -Expand Klasse
}}
}

Runspace scriptblock array cannot be passed

I'm tinkering with the code below. It should create a PoshRSJob and run the function foo again in the runspace there.
I want to be able to turn the $list parameter into an [array] or [string[]], but when I do it throws errors. I considered flattening my array into a string, but if I change $list3 to include a space or comma in the string it also throws an error. I believe it is this line that is causing the issue, but I don't know why or what to do to circumvent this issue:
ScriptBlock = [scriptblock]::Create("`$_ | $($PSCmdlet.MyInvocation.MyCommand.Name) -Parallel:`$false -fn:$fn -sqlQuery:$SQLQuery -option:$option -List:$List")
Code:
function foo {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("ComputerName")]
[PSObject] $InputObject,
[switch] $Parallel = $true,
[string] $fn,
[string] $sqlQuery = "none",
[string] $option = "none",
[int] $number,
[int] $Throttle = 100,
#Want this to be [array] or [string[]] :
[string] $List = "none"
)
begin {
$batch = [System.Guid]::NewGuid().Guid #run all jobs under same batch number
}
process {
if ($Global:debugging -eq $true){$host.ui.WriteDebugLine("fn:$fn | SQLQuery:$sqlQuery")}
if (!$Parallel) {
$server = $InputObject.name
switch($fn){
Manage{ return $list }
} #end switch
} else {#region Parallel run
$jobArguments = #{
Throttle = $Throttle
Batch = $batch
FunctionsToLoad = $PSCmdlet.MyInvocation.MyCommand.Name
#This is the problematic line:
ScriptBlock = [scriptblock]::Create("`$_ | $($PSCmdlet.MyInvocation.MyCommand.Name) -Parallel:`$false -fn:$fn -sqlQuery:$SQLQuery -option:$option -List:$List")
}
if ($_ -and $_ -isnot [string]) { $serverName=$_ } else { $serverName=$InputObject.name }
#(if ($_ -and $_ -isnot [string]) { $_ } else { $InputObject }) | Start-RSJob #jobArguments | Out-Null
} #endregion
}
end {#region Wait for results and return them
if ($Parallel) {
Get-RSJob -batch $batch | Wait-RSJob -ShowProgress | Out-Null
}#endregion
}
}
$obj = New-Object -TypeName PSObject -Property #{
Name = 'server1'
Other = 'other'
}
$list2 = $obj
$list3 = "item1-item2"
$list2 | foo -fn 'Manage' -number 2 -option Q -List $list3
This is the error:
The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
Does anyone know how to get this working so I can pass a list into the runspace?

How can I get a output from a powershell function into a output file for example txt?

I have made a create function in windows powershell:
function create(){
param(
$searchBase = "OU=Customers,DC=test,DC=nl",
$NewOUs = #(Import-csv -Path $txt_csv.Text -Delimiter ";"),
[switch]$ProtectOU
)
$Protect = $true
If ($ProtectOU){$Protect = $true}
<# ------- CREATE OU ------- #>
foreach ($NewOU in $NewOUs) {
try {
New-ADOrganizationalUnit -Name $NewOU.company -Description $NewOU.description -Path $searchBase -ProtectedFromAccidentalDeletion $Protect
}
catch {
Write-Host "OU already exists"
}
}
$UserList = Import-Csv -Path $txt_csv.Text -Delimiter ";"
<# ------- CREATE USERS ------- #>
foreach ($User in $UserList) {
$OU = $User.path
$UPN = $User.UPN
$Password = $User.password
$Detailedname = $User.firstname + " " + $User.Lastname
$UserFirstname = $User.Firstname
$FirstLetterFirstname = $UserFirstname.substring(0,1)
$SAM = $User.UPN
$Company = $User.company
$Description = $User.description
$AccountExpirationDate = $User.accountexpirationdate
$params = #{ 'Name'=$Detailedname;
'SamAccountName'=$SAM;
'UserPrincipalName'=$UPN+'#test.nl';
'DisplayName'=$Detailedname;
'GivenName'=$UserFirstname;
'Surname'=$User.Lastname;
'AccountPassword'=(ConvertTo-SecureString $Password -AsPlainText -Force);
'Enabled'=$True;
'PasswordNeverExpires'=$True;
'Path'=$OU;
'Company'=$Company;
'Description'=$Description;
'AccountExpirationDate'=$AccountExpirationDate
'HomeDrive' = "H:"
'HomeDirectory' = "\\home\userdata$\$SAM"
'ProfilePath'="\\dc-test\User Profiles$\$SAM"
}
New-ADUser #params
}
I want this create function as a output file for example in a txt.
This is my wpf button object:
$button_add.Add_Click({create})
When I click on the button the output must generated a output file. I already tried a lot of solutions such ass:
Create | out-file but I don't get the information I want:
source: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.utility/out-file
Is it possible to do this?
Kind regards
You would want to do the following:
"processing New-ADUser with $params" | Out-File log.txt -Append
try {
New-ADUser #params
"Created $UPN" | Out-File log.txt -Append
}
catch {
$_ | Out-File log.txt -Append
}
or you could just pass the output of New-ADUser to the Out-File:
New-ADUser #params -PassThru | Out-File log.txt -Append

How to add a number at end of username

I need to generate usernames that start with a letter of the alphabet, followed by the date and that ends with numbers from 1 to 20. For a total of 520 names.
Here's the variables I came up with:
$letter = 97..122 | foreach {[char]$_}
$date = get-date -UFormat %a%y%d
$name = $letter | ForEach-Object { $_ + $date }
So I managed to put my letters and my dates together, to form names like aWed1513, which is exactly what I need, but when it comes to make each to end with numbers, I'm stuck. Tried with a For loop by adding incrementing $i to $name, but doesn't work..
It seems like you're just about there. Your for loop should look like this:
$names = #()
$letter = 97..122 | foreach {[char]$_}
$date = get-date -UFormat %a%y%d
for($i = 1; $i -le 20; $i++) {
$names += $letter | ForEach-Object { '{0}{1}{2}' -f $_, $date, $i }
}
$names
An alternate approach to the answer given that just adds another ForEach-Object to the OP's original code:
$letter = 97..122 | foreach {[char]$_}
$date = get-date -UFormat %a%y%d
$names = $letter | ForEach-Object { $_ + $date } | ForEach-Object { for($i=1;$i -le 20;$i++) { $_ + $i } }
And to shorten to get same result:
$letter = 97..122 | foreach {[char]$_}
$date = get-date -UFormat %a%y%d
$names = $letter | ForEach-Object { for($i=1;$i -le 20;$i++) { $_ + $date + $i } }

Find file extension in folder and sum total size

I am trying to find all file extension in a folder and subfolders and generate a list. I found a oneliner but it do not generate the list as i want. i got mutiple of paths so i do this.
$date = get-date -Format d
$File = "C:\NoBackup\FolderPaths.txt"
foreach ($Folder in (Get-Content $File)) {
Get-ChildItem $Share -Recurse -ErrorAction SilentlyContinue | Group-Object extension | Select-Object #{Name="Folder";Expression={$Folder}}, name, #{n='TotalSize';e={$_.group | ForEach-Object -Begin {$size=0} -Process {$size += ([decimal]::round($_.Length / 1MB))} -End {"$size MB"}}} | Sort-Object -Property 'TotalSize' -Descending | Format-Table -AutoSize
}
This will give a new header foreach folder in folderpaths, and i need the result be like this
.ext1 .ext2 .ext3 .ext4
D:\Folder1 5MB 12MB 20MB 8MB
D:\Folder2 10MB 54MB 12MB 3MB
D:\Folder3 2MB 12MB 20MB 100MB
I cant find out to rewrite the code to get what i need. Hope you can help me out with this.
The script works now. I needed to change
foreach($folder in $folders)
To
foreach($folder in (Get-Content $file))
It is not short or sweet, but try this:
function ConvertTo-Units($decimal)
{
$value = [decimal]::Round($decimal/1mb,2)
$units = "MB"
if($value -eq 0)
{
$value = [decimal]::Round($decimal/1kb,2)
$units = "KB"
}
return "{0} {1}" -f $value,$units
}
$File = "C:\NoBackup\FolderPaths.txt"
$fileData = #{}
foreach ($folder in (Get-Content $file))
{
$files = Get-ChildItem $folder -Recurse -ErrorAction SilentlyContinue -File
$fileData[$folder] = $files | Select-Object extension,length | %{$h = #{}} { $h[$_.extension] += $_.length } { $h}
}
$extensions = $fileData.Keys | % { $fileData[$_].Keys } | % tolower | Select-Object -Unique | ? { $_ }
$properties = #(
#{Name="Folder";Expression={$_}}
)
$extensions | % {$properties += #{Name="$_";Expression=[scriptblock]::Create("ConvertTo-Units `$fileData[`$folder][""$_""]")}}
$result = #()
foreach($folder in $folders)
{
$result += $folder | Select-Object $properties
}
$result | ft * -AutoSize -force

Resources