I wrote script to create N number of items to custom SharePoint list in PowerShell. I made it work with fixed values, but now I would like to create items with random values.
I created internal variables that are array of values and I would like out of those to be set in list columns.
I made it work for some simple columns like date, single and multi line of text, etc. But I can't seem to make it work for lookup and people picker values. Bellow is example of script I made.
For this example, I don't get error in PowerShell I just get incorrect result in columns, example is I get username in people picker column that is not in value userName array. I get ID;#User8 (for example) that is not in array.
If you have any suggestion what I should change or add to get only values from array?
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
$webUrl = "site url"
$listName = "list name"
$numberItemsToCreate = N
# userName is array of values of usernames that would be set to people picker column
$userName = ID;#user1, ID;#User2, ID;#User3, ID;#User6, ID;#User10
# projectName is array of values of projects that are lookup column from another list
$projectName = 1;#Project1, 2;#Project2, 3;#Project3, 4;#Project4
#
$web = Get-SPWeb $webUrl
$list = $web.Lists[$listName]
for($i=1; $i -le $numberItemsToCreate; $i++)
{
$newItem = $list.AddItem()
$newItem["Title"] = "Title"
$newItem["Project"] = (Get-Random $projectName)
$newItem["DueDate"] = "2017-12-12 00:00:00"
$newItem["Assigned_x0020_To"] = (Get-Random $userName)
$newItem["EstimatedFinishDate"] = "2017-12-12 00:00:00"
#$newItem["Status"] = "ToDo"
$newItem["URLs"] = "www.google.com"
$newItem.Update()
}
$web.Dispose()
This "$userName = ID;#user1, ID;#User2, ID;#User3, ID;#User6, ID;#User10" is not a PowerShell array or hashtable
Using an array:
$userName = #("#user1", "#User2", "#User3", "#User6", "#User10")
$newItem["Assigned_x0020_To"] = Get-Random $userName
Using a hashtable:
$projectName = #{1="#Project1"; 2="#Project2"; 3="#Project3"; 4="#Project4"}
$newItem["Project"] = $ProjectName.(Get-Random #($ProjectName.Keys))
Or just:
$newItem["Project"] = Get-Random #($ProjectName.Values)
Related
I have one array of hashtables like the one below:
$hashtable1 = #{}
$hashtable1.name = "aaa"
$hashtable1.surname =#()
$hashtable1.surname += "bbb"
$hashtable2 = #{}
$hashtable2.name = "aaa"
$hashtable2.surname =#()
$hashtable2.surname += "ccc"
$hashtable3 = #{}
$hashtable3.name = "bbb"
$hashtable3.surname = #()
$hashtable3.surname += "xxx"
$A = #($hashtable1; $hashtable2; $hashtable3)
I need to iterate though the array and I need to find out duplicates based on hashtable[].name
Then I need to group those hashtable.surname to hashtable[].surname so that the result will be an array of hashtables that will group all for name all the surnames:
$hashtable1.name = "aaa"
$hashtable1.surname = ("bbb","ccc")
$hashtable3.name = "bbb"
$hashtable3.surname = ("xxx")
I was looking into iterating to empty array
+
I have found this link:
powershell compare 2 arrays output if match
but I am not sure on how to reach into the elements of the hashtable.
My options:
I was wondering if -contain can do it.
I have read about compare-object but I am not sure it can be done like that.
(It looks a bit scary in the moment)
I am on PS5.
Thanks for your help,
Aster
You can group your array items by the names using a scriptblock like so.
Once grouped, you can easily build your output to do what you seek.
#In PS 7.0+ you can use Name directly but earlier version requires the use of the scriptblock when dealing with arrays of hashtables.
$Output = $A | Group-Object -Property {$_.Name} | % {
[PSCustomObject]#{
Name = $_.Name
Surname = $_.Group.Surname | Sort-Object -Unique
}
}
Here is the output variable content.
Name Surname
---- -------
aaa {bbb, ccc}
bbb xxx
Note
Improvements have been made in PS 7.0 that allows you to use simply the property name (eg: Name) in Group-Object for arrays of hashtables, just like you would do for any other arrays type. For earlier version though, these particular arrays must be accessed by passing the property in a scriptblock, like so: {$_.Name}
References
MSDN - Group_Object
SS64 - Group Object
Dr Scripto - Use a Script block to create custom groupings in PowerShell
I have a function that exports the results of a SQL query to a json file:
# Connect to SQL Server
$SqlCommand.CommandText = $Query;
$SqlCommand.Connection = $SqlConnection;
# Execute query and get the result back
$QueryResult = $SqlCommand.ExecuteReader()
# Hold query result in data table
$QueryTable = New-Object "System.Data.DataTable"
$QueryTable.Load($QueryResult)
# Export query results to json
$QueryTable | Select-Object $QueryTable.Columns.ColumnName | ConvertTo-Json | Out-File "$OutputDirectory\$SqlInstance-$QueryName.json"
And I have multiple queries that I want to execute and have created variables for each one:
$q1 = "SELECT blah"
$q2 = "SELECT more blah"
$q3 = "SELECT even more blah"
I call the function by:
ExportQueryResultsToJson -Query $q1 -QueryName "q1"
I have around 80 queries that I want to execute so instead of having 80 lines of ExportQueryResultsToJson ... I want to use ForEach. I've created an array of variables:
$SqlServer2012QueryArray = #(
$q1,
$q2,
$q3
)
I've tried many variations of the following:
foreach ($Query in $SqlServer2012QueryArray) {
$Expression = "ExportQueryResultsToJson -Query '$Query' -QueryName $Query"
Invoke-Expression $Expresion
}
And I've tried using a splat but I can't figure out how to pass all queries in correctly.
What am I doing wrong?
You can approach this in a number of ways. Three possible ways, which are all very algorithmically similar, are below:
Using Your Array:
The solution depends on your array $sqlserver2012QueryArray having a list of sequentially numbered variables in the format q<number>. The first variable name must be q1.
for ($i = 0; $i -lt $sqlserver2012QueryArray.Count; $i++) {
ExportQueryResultsToJson -Query $sqlserver2012QueryArray[$i] -QueryName $((Get-Variable "q$($i+1)").Name)
}
Querying Already Created Variables:
This solution relies on your variables being named in the format q<number>. They do not have to be sequentially named. It could capture unwanted variables if they are named like q<number>abc.
foreach ($var in (Get-Variable -Name q[0-9]*)) {
ExportQueryResultsToJson -Query $var.Value -QueryName $var.Name
}
Using a Hash Table:
You can create a hash table with each key name being your variable name and the associated value being the query string. You can bypass creating the query variables all together with this solution by just inputting the query strings as the values.
$queryhash = #{'q1' = $q1; 'q2' = $q2; 'q3' = $q3; 'q14' = $q14}
foreach ($var in $queryhash.GetEnumerator()) {
ExportQueryResultsToJson -Query $var.Value -QueryName $var.Key
}
Note: In all cases, you should try to avoid Invoke-Expression. It is not generally a safe command to use because it welcomes code injection. I also don't see why it is necessary at all in this case either.
So if I create an array from CSV file
name,age,height,fruit
Jon,34,5,orange
Jane,23,4,apple
Dave,27,6,pear
I can read this in using
$list = Import-CSV .....
but now i have the question. "tell me about Jane"
So I could say
foreach ($val in $list)
{
if ($val.name = "jane" ) {$currentuser = $val}
}
write-host $currentuser.name
write-host $currentuser.age
write-host $currentuser.hight
write-host $currentuser.fruit
Is there a better way to do this rather than stepping through? In my actually case i have a list of staff from HR and a separate one from Active directory.
I want to step through the HR list to find the user in AD, set this user as a variable/object. and then update the user using information from HR.
The above method will work but seems very inefficient to be loping through two lists of several thousand users.
Given the array created from the CSV, I want a method that by inputting the string "jane" it will return jane's info to a object i can use.
If you have two lists, both with distinct keys by which the two can be correlated, the best way is to store one list in a lookup table (any type of dictionary will do, including a hashtable in PowerShell), and then loop sequentially through the other list:
$HRList = #'
Name,Position,Department
John,Manager,Sales
Bob,Worker,Production
Sally,Accountant,Finance
'# |ConvertFrom-Csv
$ADList = #'
Name,Username
Sally,sallysalt
Bob,bobburrows
John,johnjames
'# |ConvertFrom-Csv
# Convert the AD list to a hash table
$ADLookupTable = #{}
foreach($ADUser in $ADList)
{
$ADLookupTable[$ADUser.Name] = $ADUser
}
# Go through HR list
foreach($HRUser in $HRList)
{
# Now you can find the relevant object in the other list
$ADUser = $ADLookupTable[$HRUser.Name]
Set-ADUser -Identity $ADUser.Username -Title $ADUser.Position -Department $ADUser.Department
}
I've been struggling with this for a couple of days, and I'm not sure how to conquer it. I need to do the following:
Import a csv of users with the following values:
ID, Name, Region
Create an array based on the Region values that I can then use to populate with ID's and Names with that region, ie.
Array_SEA
AA_SCOM, Adam Andrews, SEA
Array_OAK
BB_SCOM, Bob Barker, OAK
Here's the code I've got right now:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#()
foreach ($vitem in $list2)
{
$arraylist += New-Object PsObject -Property #{'Array' = "Array_" + $vitem.bu}
}
foreach ($varray in $arraylist)
{
$arr = new-variable -Name $varray
$arr.value += $varray.array
$arr
}
This produces the following error for records with a duplicate regions:
New-Variable: A variable with name '#{Array=Array_SCA}' already exists.
I'm also getting the following when it tries to add values:
Property 'value' cannot be found on this object; make sure it exists and is settable.
I get that I'm not actually creating arrays in the second section, but I'm not sure how to pass the output of the variable to an array name without turning the variable declaration into the array name, if that makes sense.
I've tried the following with hash tables, and it gets closer:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#{}
foreach ($vitem in $list2){$arraylist[$vitem.bu] = #()}
foreach ($record in $list2)
{
$arraylist[$vitem.bu] += ($record.SCOMID,$record.Name,$record.BU)
Write-host "Array: "
$arraylist[$vitem.bu]
write-host ""
}
The output on this shows no errors, but it just keeps showing the added fields for all of the records for each iteration of the list, so I don't think that it's actually assigning each unique BU to the array name.
I like the hashtable-approach, but I would finetune it a little. Try:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist = #{}
foreach ($vitem in $list2){
if($arraylist.ContainsKey($vitem.BU)) {
#Array exists, add item
$arraylist[($vitem.BU)] += $vitem
} else {
#Array not found, creating it
$arraylist[($vitem.BU)] = #($vitem)
}
}
#TEST: List arrays and number of entries
$arraylist.GetEnumerator() | % {
"Array '$($_.Key)' has $($_.Value.Count) items"
}
You could also use Group-Object like:
$list2 = ipcsv .\TSE_Contact_List.csv | Group-Object BU
#TEST: List groups(regions) and number of entries
$list2 | % {
"Region '$($_.Name)' has $(#($_.Group).Count) items"
}
I'm having an issue trying to find the index number of an item in an array that contains Active directory users.
I create the array as follows:
$outarray = #()
$outarray = get-aduser -Filter * -Properties LastLogon | select "Name","SAMAccountName","LastLogon" | sort samaccountname
Now i have the users in an array, and i can prove it using standard variable queries
$outarray[0]
$outarray[1]
Returns exactly what i expect.
BUT
I completely fail to search for the index of a name or SAMAccountName in the array, as they are properties of the array.
$index = [array]::IndexOf($outarray.samaccountname, "testuser")
returns -1 (not found) or 0 only if testuser is the FIRST user in the array.
I cannot find any other user index in the array.
My goal after getting the index is to use it to update the property for lastlogon. This works if i do it manually
e.g.
$outarray[123].lastlogon = 12345678
The only way i can make this work is to manually build the array initially, one entry at a time instead of filling directly
foreach ($user in $outArray)
{
$myobj = #()
$myobj = "" | Select "Name","SAMAccountName","LastLogon"
#fill the object
$myobj.Name = $user.name
$myobj.SAMAccountName = $user.samaccountname
$myobj.LastLogon = $user.LastLogon
#Add the object to the array
$userarray += $myobj
}
$userarray[[array]::IndexOf($userarray.samaccountname, "testuser")].LastLogon = 12345678
Then the search works. I assume this has to do with property types, but im completely out my depth by this stage.
Thanks in advance for any help, I'm no expert on powershell arrays, they confuse me! :)
I think you're looking at this the wrong way. Instead of finding the index of a specific item and then access that item by its index you could do it the PoSh way by filtering the array for the item you want to update, like this:
$userarray | ? {
$_.SamAccountName -eq 'testuser'
} | % {
$_.LastLogon = 12345678
}
or like this:
$acct = $userarray | ? { $_.SamAccountName -eq 'testuser' } | select -First 1
$acct.LastLogon = 12345678