Uploading CSV to Sharepoint, choice column not uploading correctly - powershell

I am creating a script to upload contact information to a sharepoint list. One of the columns (Categories) is a choice field with checkboxes for multiple selections. I need to figure out a way to add checks to this field if I have multiple Categories in my CSV. For example, two of the check boxes are vendor and project manager if a contact in my CSV has both i need the item in the sharepoint list to have both. Here is the code I have so far:
# Setup the correct modules for SharePoint Manipulation
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin Microsoft.SharePoint.PowerShell
}
$host.Runspace.ThreadOptions = "ReuseThread"
#Open SharePoint List
$SPServer="http://SPsite/itv2"
$SPAppList="/Lists/Test CSV Upload"
$spWeb = Get-SPWeb $SPServer
$spData = $spWeb.GetList($spWeb.ServerRelativeURL + $SPAppList)
$InvFile="C:\Scripts\ContactUpload.csv"
# Get Data from Inventory CSV File
$FileExists = (Test-Path $InvFile -PathType Leaf)
if ($FileExists) {
"Loading $InvFile for processing…"
$tblData = Import-CSV $InvFile
} else {
"$InvFile not found – stopping import!"
exit
}
# Loop through Applications add each one to SharePoint
"Uploading data to SharePoint…."
foreach ($row in $tblData)
{
"Adding entry for "+$row."GivenName".ToString()
$spItem = $spData.AddItem()
$spItem["First Name"] = $row."GivenName".ToString()
$spItem["Last Name"] = $row."Surname".ToString()
$spItem["Email Address"] = $row."Email1EmailAddress".ToString()
$spItem["Business Phone"] = $row."BusinessPhone".ToString()
$spItem["Mobile Phone"] = $row."MobilePhone".ToString()
$spItem["Categories"] = $row."Categories"
$spItem.Update()
}
"—————"
"Upload Complete"
$spWeb.Dispose()
$spWeb.Dispose()
I need to find a way to "concatenate" checks to the categories field, so that I am able to search the categories field for any of the checked boxes. So far everything I have done will add vendor,project manager to the column, but then I am not able to filter it by vendor and have the contact come up.

To update a multiple choice value field, you need to use a SPFieldMultiChoiceValue object. For example :
$choicevalues = New-Object Microsoft.SharePoint.SPFieldMultiChoiceValue
$choicevalues.Add("Choice 1")
$choicevalues.Add("Choice 2")
$list.Fields["Categories"].ParseAndSetValue($spItem,$choicevalues)
So you will first need to split your $row."Categories" variable into an array and then add each category to $choicevalues before updating your Categories field.
Hope this help!

Related

how to create dynamic link list from .lnk files in a folder with powershell

I would like to be able to create a dynamic link list from .lnk files in a folder with powershell. I want the link list to be presented to the user in a form that can be minimzed, but will stay active for the entire session, even if the user launch one of them the main form will remain active.
I'm having a hard time moving from VBS to powershell, so any help would be appreciated.
Finally I have managed to have a begining of solution here is my code.
$linklist = #(
("MyFisrtApp" , "\\Path\To\MyFisrtApp.exe"),
("MySecondApp" , "\\Path\To\MySecondApp.exe"),
("MyThirdApp" , "\\Path\To\MyThirdApp.exe"),
("MyFourthApp" , "\\Path\To\MyFourthApp.exe")
)
#Create Form Object
$mainform = New-Object System.Windows.Forms.Form
$mainform.Size = New-Object System.Drawing.Size(400,300)
$mainform.Text = " My Virtual Applications"
$mainform.StartPosition = "CenterScreen" #loads the window in the center of the screen
# convert the array of arrays into an ordered Hashtable
$linkHash = [ordered]#{}
$linklist | ForEach-Object { $linkHash[$_[0]] = $_[1] }
$calculatedPosition =40
$linkHash.GetEnumerator() | ForEach-Object {
$lnk = New-Object System.Windows.Forms.LinkLabel
$lnk.Text = $_.Name # set the name for the label
$lnk.Tag = $_.Value # store the link url inside the control's Tag property
$lnk.Location = New-Object System.Drawing.Point(60, $calculatedPosition)
# inside the scriptblock, $this refers to the LinkLabel control itself
$lnk.Add_Click({ Start-Process $this.Tag })
$mainform.Controls.Add($lnk)
$calculatedPosition += 40 # just a guess, you may want different vertical spacing
}
#Show Form
$mainform.ShowDialog()
My next move now is to display the icon of the .lnk file on the left of each link and also dynamically construct the linklist hashtable from the properties of a list of .lnk files in a specific folder. Also having the form to auto-size as needed if more links need to be shown, would be interesting. I'm trying different setting with mitigated results.

Updating a lookup column in SharePoint Online list using PoweShell PnP

I have worked on a PowerShell PnP script to update a column in a cloud-based SharePoint list.
I want to update the column “Parent ID” with the content from “FH ID”. My script works fine on a regular column, but “Parent ID” is a lookup-column and therefore, it has three values: LookupID, LookupValue and TypeId (see image). My script updates LookupId, but we need it to update LookupValue.
Can anyone help with a solution?
In the below script we are testing on one “FH ID” = 1020
enter code here
Connect-PnPOnline https://skat.sharepoint.com/sites/ICIfo
function UpdateColumn(){
$listName = "FH kontakt"
$list = Get-PnPList $listName
$items = Get-PnPListItem $listName
foreach ($item in $items){
if ($item["FH_x0020_ID"] -eq "1020"){
Write-Output $item["FH_x0020_ID"]
Write-Output $item["FH_x0020_Navn"]
Write-Output $item["Parent_x0020_ID"]
Write-Output ""
Set-PnPListItem -List $listName -Identity $item -Values #{"Parent_x0020_ID" = $item["FH_x0020_ID"]}
}
}
}
UpdateColumn
To update the lookup column in SharePoint online, you need to get the ID of the parent lookup item.
You could refer to this article for more:
https://www.sharepointdiary.com/2017/03/sharepoint-online-powershell-to-update-lookup-field.html

Layering multiple 'ForEach' statements to loop through a directory and perform actions on each file found

I've been working on this Powershell script for a good week now, and it almost works as expected.
Essentially, the script reaches into the specified directory which we have another script dropping .CSV files into, grabs the .CSV file(s) and pushes the information found into a Sharepoint list, well, that's the intention anyway. I've gotten the script to work perfectly if I manually specify the file, the issue I am having is actually getting all the .CSV files into a group, and then looping through each .CSV to pull the information out and push it into a Sharepoint list. Once done, it renames the file from .CSV to .ARCHIVED for another script to come in and re-locate after we're done with it.
I think I have, through selective (creative) troubleshooting, figured out what I am doing wrong, I just don't know how to proceed after identifying the issue.
I declare the string $Filecsv like so:
$Filecsv = get-childitem "Z:\" -recurse | where {$_.extension -eq ".csv"}
So, this reaches into my 'Z:\' directory, and pulls all the files with .CSV extension and combines them into a table...
ForEach ($items in $Filecsv) {
And this says for each item, perform logic...
foreach($row in $Filecsv)
The only problem is, when I call $Filecsv, it is returning the list of each .CSV file in the directory like such:
And as such, when I execute the bit of code that says 'put the information into my list', only the file name is added to my Sharepoint list....
Now, I can see what's going on here, it's pulling the 'Name' from the $Filecsv table, and pushing that up to Sharepoint, however, I am not sure how to re-construct my logic so that it operates as expected because as it exists now, it should (to me anyway) work as I think it does, but I am still new to Sharepoint and am certainly missing something here.
Below, is the full code, if it helps:
# Add SharePoint PowerShell Snapin which adds SharePoint specific cmdlets
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
#start the counter at 1 to track times script has looped
$iterations = 1
# set the location where the .CSV files will be pulled from and define the
# file extension we are concerned with
$filecsv = get-childitem "Z:\" -recurse | where {$_.extension -eq ".csv"}
# for each file found in the directory
ForEach ($items in $Filecsv) {
# check to see if files exist, if not exit cleanly
if ($Filecsv) {"File exists" + $Filecsv} else {exit}
# count the times we've looped through
"Iterations : $iterations"
# specify variables needed. The webURL should be the site URL, not including the list
# the listName should be the list name
$WebURL = "http://SHAREPOINTURL/"
$listName = "test"
# Get the SPWeb object and save it to a variable
$web = Get-SPWeb -identity $WebURL
# Get the SPList object to retrieve the list
$list = $web.Lists[$listName]
# START deletes all items. code shows the number of items in a list, then deletes all items
# If you don't want your script to delete items, then remove this
$site = new-object Microsoft.SharePoint.SPSite ( $WebURL )
$web = $site.OpenWeb()
"Web is : " + $web.Title
# Enter name of the List below instead of
$oList = $web.Lists["test"];
"List is :" + $oList.Title
"List Item Count: " + $oList.ItemCount
#delete existing contents and replace with new stuff
$collListItems = $oList.Items;
$count = $collListItems.Count - 1
for($intIndex = $count; $intIndex -gt -1; $intIndex--) {
"Deleting record: " + $intIndex
$collListItems.Delete($intIndex);
}
# END Deletes all items
# goes through the CSV file and performs action for each row
foreach($row in $Filecsv)
{
$newItem = $list.items.Add()
$item = $list.items.add()
# Check if cell value is not null in excel
if ($row."Name" -ne $null)
# Add item to sharepoint list. for this one, I had to use the internal column name.
#You don't always have to, but I had trouble with one SharePoint column, so I did
{$newItem["Name"] = $row."Name"}
else{$newItem["Name"] = $row."Not Provided"}
if ($row."Description" -ne $null)
{$newItem["Description"] = $row."Description"}
else{$newItem["Description"] = $row."No Description"}
if ($row."NetworkID" -ne $null)
{$newItem["Network ID"] = $row."NetworkID"}
else{$newItem["Network ID"] = $row."No NetworkID"}
if ($row."Nested" -ne $null)
{$newItem["Nested"] = $row."Nested"}
else{$newItem["Nested"] = $row."Not Nested"}
# Commit the update, then loop again until end of file
$newItem.Update()
}
# get the date and time from the system
$datetime = get-date -f MMddyy-hhmmtt
# rename the file
$NewName = $items.fullname -replace ".csv$","$datetime.csv.archived"
$Items.MoveTo($NewName)
# +1 the counter to count the number of files we've looped through
$iterations ++
}
a very cursory look would suggest that you need to use $items not $filecsv in your main loop.
essentially you are looping over the contents of the $filecsv collection, so you need to look at $items.
Your ForEach loops look redundant since they are both looping through a list of FileInfo objects. I think you want to find all the files, and for each file load it into memory and process it's contents. We'll go that route.
I have moved your SharePoint object creation out of the loop since I don't see any point to creating the object over and over for each file processed since it never references anything based on the file or it's contents. It simply makes the same object over, and over, and over.
# Add SharePoint PowerShell Snapin which adds SharePoint specific cmdlets
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
#start the counter at 1 to track times script has looped
$iterations = 1
# specify variables needed. The webURL should be the site URL, not including the list
# the listName should be the list name
#Setup SP object
$WebURL = "http://SHAREPOINTURL/"
$listName = "test"
# Get the SPWeb object and save it to a variable
$web = Get-SPWeb -identity $WebURL
# Get the SPList object to retrieve the list
$list = $web.Lists[$listName]
# START deletes all items. code shows the number of items in a list, then deletes all items
# If you don't want your script to delete items, then remove this
$site = new-object Microsoft.SharePoint.SPSite ( $WebURL )
$web = $site.OpenWeb()
"Web is : " + $web.Title
# Enter name of the List below instead of
$oList = $web.Lists["test"];
"List is : " + $oList.Title
"List Item Count: " + $oList.ItemCount
#delete existing contents and replace with new stuff
$collListItems = $oList.Items;
$count = $collListItems.Count - 1
for($intIndex = $count; $intIndex -gt -1; $intIndex--) {
"Deleting record: " + $intIndex
$collListItems.Delete($intIndex);
}
# END Deletes all items
Find all the CSV files, and start looping through the list of them. I removed the check to see if the file exists. You just pulled a directory listing to find these files, they really should exist.
# set the location where the .CSV files will be pulled from and define the
# file extension we are concerned with
$CSVList = get-childitem "Z:\" -recurse | where {$_.extension -eq ".csv"}
ForEach ($CSVFile in $CSVList) {
# count the times we've looped through
"Iterations : $iterations"
Now, this is different. It loads the CSV file, and processes each row in it as $row. I'm pretty sure this is what you intended to do from the start. I also changed it from If(Something -ne $null) to check for either null, or empty since either can actually exist and the later can cause you some issues. It's just a safer method in general.
foreach($row in (Import-CSV $CSVFile.FullName))
{
$newItem = $list.items.Add()
$item = $list.items.add()
# Check if cell value is not null in excel
if (![string]::IsNullOrEmpty($row."Name"))
# Add item to sharepoint list. for this one, I had to use the internal column name.
#You don't always have to, but I had trouble with one SharePoint column, so I did
{$newItem["Name"] = $row."Name"}
else{$newItem["Name"] = $row."Not Provided"}
if (![string]::IsNullOrEmpty($row."Description"))
{$newItem["Description"] = $row."Description"}
else{$newItem["Description"] = $row."No Description"}
if (![string]::IsNullOrEmpty($row."NetworkID"))
{$newItem["Network ID"] = $row."NetworkID"}
else{$newItem["Network ID"] = $row."No NetworkID"}
if (![string]::IsNullOrEmpty($row."Nested"))
{$newItem["Nested"] = $row."Nested"}
else{$newItem["Nested"] = $row."Not Nested"}
# Commit the update, then loop again until end of file
$newItem.Update()
}
I don't really understand why you are adding a new item twice, but if it works then more power to you. Then your bit to rename files when you're done with them (hey, this looks familiar):
# get the date and time from the system
$datetime = get-date -f MMddyy-hhmmtt
# rename the file
$NewName = $CSVFile.fullname -replace ".csv$","$datetime.csv.archived"
$CSVFile.MoveTo($NewName)
# +1 the counter to count the number of files we've looped through
$iterations ++
}
I did rename a few things to make them more indicative of what they represent ($Items to $CSVFile and what not). See if this works for you. If you have questions or concerns let me know.
Edit: Ok, to fix the loop trying to pull each item from the current folder we reference the FullName property of it. One line changed:
foreach($row in (Import-CSV $CSVFile.FullName))

Create Libraries and Permissions with SharePoint Powershell

I'm trying to make a script to help automate this process a bit but am fairly new to using PowerShell with SharePoint and don't really know the route to take.
I have a list of 40 items and I need to make a library for each one. Each library then needs to have unique permissions with 3 default groups(Owners, Members, Visitors). The groups should be named the same as the List.Title + Owners/Members/Visitors.
So far I create a site group as follows:
# Owner Group
$web.SiteGroups.Add(“$web Owners”, $web.Site.Owner, $web.Site.Owner, “Use this group to grant people full control permissions to the $web site”)
$ownerGroup = $web.SiteGroups["$web Owners"]
$ownerGroup.AllowMembersEditMembership = $true
$ownerGroup.Update()
The naming of my groups needs to be the list title and not the web name as I have above.
I create a new library like this:
PS > $spWeb = Get-SPWeb -Identity http://SPServer
PS > $listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
PS > $spWeb.Lists.Add("My Documents","My Doc Library",$listTemplate)
Clearly this is not automated at all and no faster than just using the GUI to make each new library and adding in the site groups. Can anyone help get me started on a script that would iterate through a list of names create a library and create 3 groups on the site for each new library?
Thanks!!
This should get you on the right track I believe.
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
$Lists = #("My Documents", "My Docs", "Testing")
$Groups = #("Owners", "Members", "Visitors")
$listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
$Web = Get-SPWeb "Your_URL"
$Lists | ForEach-Object {
$ListName = $_
$Description = "$_ Description"
$Web.Lists.Add($ListName, $Description, $listTemplate)
$Groups | % {
$GroupName = "$ListName $_"
$Web.SiteGroups.Add($GroupName, $Web.Site.Owner, $Web.Site.Owner, "Group $GroupName created by automatic process")
$group = $Web.SiteGroups["$GroupName"]
if ( !$GroupName -contains "Visitors")
{
$group.AllowMembersEditMembership = $true
} else
{
$group.AllowMembersEditMembership = $false
}
$group.Update()
}
}

Exchange Powershell: Contacts from Public Folder

Exchange Management Shell:
[PS] C:\Windows\system32>$AddressBook = Get-PublicFolderItemStatistics -Identity "\Shared Company Address Book"
[PS] C:\Windows\system32>$AddressBook [0] | format-list
RunspaceId : d8e95055-1f3e-4e7f-a1fc-d5b97ecbcb96
ServerName : MAILMAN
DatabaseName : Public Folder Database 0524088380
Subject : John Q User
PublicFolderName : \Company Address Book
LastModificationTime : 11/12/2012 2:57:49 PM
CreationTime : 11/12/2012 2:56:28 PM
HasAttachments : False
ItemType : IPM.Contact
MessageSize : 6.598 KB (6,756 bytes)
Identity : 000000001A447390AA6611CD9BC800AA002FC45A0900580787449ABF5E4891DD89178938242C0000000250AE00001BE1
A5309D57D5439914FD70BDC745C100000B8942FD0000
MapiIdentity : 000000001A447390AA6611CD9BC800AA002FC45A0900580787449ABF5E4891DD89178938242C0000000250AE00001BE1
A5309D57D5439914FD70BDC745C100000B8942FD0000
OriginatingServer : mailman.company.com
IsValid : True
[PS] C:\Windows\system32>
Okay... I'm trying to export the contacts in an Exchange Server 2010 Contact List. I can not, for the world of me, figure out how to get the "Data" out of this stupid thing.
If I do $AddressBook | Format-List, it lists all the contacts, so I'm know I'm in the right ballpark.
how can I get all of the information out of this list? Last Name, First Name, Email Address, Business Phone, etc.
This is an old post, but I have the same issue for a couple days and was unable to figure it out. So, I opted to use the road #WernerCD used and would like to add my PowerShell script that could help you in the case that you decide to go down this path.
Before starting, allow me to explain my issue. We have multiple folders with contacts in it. These contacts contain User Defined Fields that due to migration issues, were not added to their containing folder. While we use Outlook, we need to be able to see these fields on the current view. However, when we try to add the UDF columns, it only allows us to use the "User-defined fields in folder", which is empty.
Below is the PowerShell script that will check a public folder (and its subfolders) within Outlook, check every contact UserProperties (Equivalent for UDF), put them into an array, then it will check if each contact's UDF exist on its containing folder (UserDefinedProperties), and if it doesn't exist, it will added it as a Text field without a value.
Please keep in mind that all our contact folders are under a folder called Shared Contacts Folder.
Code
# Connection to Outlook
$Outlook = New-Object -com Outlook.Application
$Namespace = $outlook.GetNamespace("MAPI")
# "Location" of public folders (Change me#example.com)
$PublicFolder = $Namespace.Folders.Item("Public Folders - me#example.com")
$PublicFolders = $PublicFolder.Folders.Item("All Public Folders")
# Folder that you would like to check. We will check Shared Contacts under Shared Contacts Folder
$SharedContactsFolder = $PublicFolders.Folders.Item("Shared Contacts Folder")
$SharedContacts = $SharedContactsFolder.Folders.Item("Shared Contacts")
Write-Host ("<----------------------------------------------------------->") -foreground Yellow
function CheckContacts($MyFolder){
# Check if this folder has subfolder
If ( $MyFolder.Folders.Count -gt 0) {
Write-Host ("Folder '" + $MyFolder.Name + "' contains subfolders (" + $MyFolder.Folders.Count + ")") -BackgroundColor Yellow -foreground DarkBlue
Foreach ( $Subfolder in $MyFolder.Folders ) {
CheckContacts($Subfolder)
}
}
Write-Host ("Working on folder: " + $MyFolder.Name) -BackgroundColor White -foreground DarkBlue
$All_UDF = #()
# Check User Defined Fields (UDF) for each contact and add them to array
foreach ( $Contacts in $MyFolder.Items ) {
foreach ( $UDF in $Contacts.UserProperties ) {
# Check if field was previously added to array
If ($All_UDF -notcontains $UDF.Name) {
$All_UDF += $UDF.Name
}
}
}
# Add all UDF to Folder's UDF
Write-Host ("We will add the following UDF into '" + $MyFolder.Name + "': ") -foreground Green
Write-Host ($All_UDF -join "`n") -foreground Green
Foreach ( $UDF in $All_UDF ){
# Only add if UDF does not exist on folder's UDF
if( (CheckFolderUDF $MyFolder $UDF) -eq $false) {
# Add - Always add UDF as Text field (1)
Write-Host ("Adding '" + $UDF + "' to '" + $MyFolder.Name + "'")
$MyFolder.UserDefinedProperties.Add($UDF, 1)
}else{
Write-Host ("Already present: " + $UDF)
}
}
Write-Host ("<----------------------------------------------------------->") -foreground Yellow
}
Function CheckFolderUDF ( $MyFolder, $MyUDFName ) {
$Result = $false
Foreach ( $Folder_UDF in $MyFolder.UserDefinedProperties ){
If ( $Folder_UDF.Name -eq $MyUDFName ) {
$Result = $true
break
}
}
return $Result
}
# Start - Check Shared Contacts
CheckContacts($SharedContacts)
How do I run/test this code?
1) Open Windows PowerShell ISE (within Windows).
2) Copy the code above and paste it into the PowerShell ISE window.
3) Read and understand the code.
4) Modify as needed.
P.S.: I tried to add this a "comment", but I don't have enough points.
After much pain and suffering... and stumbling upon [this post]. This is in Powershell (not Exchange Powershell Console) and from my computer (not server MailMan):
$Outlook = New-Object -com Outlook.Application
$Namespace = $outlook.GetNamespace("MAPI")
$PublicFolder = $Namespace.Folders.Item("Public Folders - me#example.com")
$PublicFolders = $PublicFolder.Folders.Item("All Public Folders")
$AddressBook = $PublicFolders.Folders.Item("Company Address Book")
$Contacts = $AddressBook.Items
$Contacts | Select FullName
This actually pulls the contact information. I'm still looking at how to do it on the server side (Exchange Powershell Console), but this should be a good foundation to select the desired fields and push them into the database as I need.
I figure if I can figure out how to get the "Public Folders - dummy_user#example.com", I should be able to do the same thing on the server.
I also assume there is an easier way to do this (maybe by pull path, instead of one part at a time), but this does work.
Now to find out how to get UserDefinedFields....
Why don't you use Get-Contact instead of Get-PublicFolderItemStatistics?