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

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))

Related

Powershell hashtable.containskey is always returning true

The following code is trying to do one thing. Its trying to find IDs that have been added to an Active Directory group since the last time the job was run.
It does this by reading the user IDs from an Active Directory group and compares them against the IDs it saved in a file the day before.
I first read the AD group into a hashtable ($ADUsersHashtable)
I then read the file into a similar hashtable ($YesterdaysADUsersFile)
Both hashtables use the UserID as the key.
I then check to see if each ID in $ADUsersHashtable is in $YesterdaysADUsersFile. An ID that is in $ADUsersHashtable but not in $YesterdaysADUsersFile is an ID that was added to AD since the last time this job ran.
The problem is, if $YesterdaysADUsersFile has more than one entry in it, the containskey method always returns true (see output below)
If I delete all but one entry in the file, the code works as expected.
If i have more than one entry in the file the code doesn't work as expected.
The following is the code that reads AD and the file into the hashtables and then compares the keys.
$scriptName = $MyInvocation.MyCommand.Name
$LogFile = "D:\Polarion\data\logs\User Access Dates\$scriptName.log"
$Today = Get-Date
$outDate = get-date -format "yyyy-MM-dd"
Add-content $LogFile "$Today Running $scriptName"
$MyServer = $env:computername
Add-content $LogFile "`t$Today Running on $MyServer"
#Will be populated with IDs from AD that i didn't find yesterday
$NewUsersFile = "D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt"
clear-content $NewUsersFile #I only want the new users from todays AD group.
$ADUsersHashtable = #{} #Contains IDs of the members in the AD Group.
Get-ADGroupMember -Identity PEALM_ALL_USERS -Recursive `
| Get-ADObject -Properties SamAccountName, mail `
| select SamAccountName, mail `
| foreach {$ADUsersHashtable.Add($_.SamAccountName, $_.mail)}
#$ADUsersHashtable
$YesterdaysADUsersFile = "D:\Polarion\data\logs\User Access Dates\UserIdFromPEALM_ALL_USERS.txt" #Contains the IDs that I knew about the last time this ran.
$YesterdaysADUsersHashTable = #{}
$YesterdaysADUsersHashTable = Get-Content($YesterdaysADUsersFile) |
foreach {$_.ToString().Replace(":", "=")} |
ConvertFrom-StringData
$YesterdaysADUsersHashTable
$NoNewUsersFound = $true
foreach ($UserIDFromAD in $ADUsersHashtable.keys){ #For each user ID in Todays AD group
if ($YesterdaysADUsersHashTable.containsKey($UserIDFromAD)){ #If the UserID is in Yesterdays list ignore it.
write-host YesterdaysADUsersHashTable contains key $UserIDFromAD
} else {
$NoNewUsersFound = $false
write-host YesterdaysADUsersHashTable Doesnt contains key $UserIDFromAD
write-host "`tadding $UserIDFromAD to the $NewUsersFile file."
Add-content $LogFile "`t$Today Adding $UserIDFromAD to $NewUsersFile file"
Add-Content $NewUsersFile "$UserIDFromAD : $outDate" #if its not in yesterdays list write it to the new users file.
}
}
if ($NoNewUsersFound){
Add-content $LogFile "`t$Today No new users IDs found in Active Directory."
}
#Clear-Content $YesterdaysADUsersFile #we want to overwrite the file, not append to it.
#$ADUsersHashtable.keys `
# | %{ Add-Content $YesterdaysADUsersFile "$_ : $($ADUsersHashtable.$_)" } #writes the content of the hashtable to a file.
The following is the output when the file (and hence $YesterdaysADUsersHashTable) has two entries in it.
The first four lines are the dump of $YesterdaysADUsersHashTable.
The next five lines are from the output from the write-host commands in the if-containskey block. They show that hashtable.containskey is returning true for every key in $ADUsersHashtable. But those keys are not in $YesterdaysADUsersHashTable, this is what i don't understand.
Name Value
---- -----
QZMRW2 xxxx.xxxx#xxxx.com
dzrbcn xxxx.xxxx#xxxx.com
YesterdaysADUsersHashTable contains key QZMRW2
YesterdaysADUsersHashTable contains key dzrbcn
YesterdaysADUsersHashTable contains key MZDP2G
YesterdaysADUsersHashTable contains key BZ5LBQ
YesterdaysADUsersHashTable contains key FZ080Y
$YesterdaysADUsersHashTable clearly doesn't contain "MZDP2G", "BZ5LBQ", or "FZ080Y"
And, if I remove everything from the file except one user ID, the code seems to work.
$YesterdaysADUsersHashTable now has only one entry "QZMRW2" and the code seems to work.
Name Value
---- -----
QZMRW2 xxxx.xxxx#xxxx.com
YesterdaysADUsersHashTable contains key QZMRW2
YesterdaysADUsersHashTable Doesnt contains key dzrbcn
adding dzrbcn to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key MZDP2G
adding MZDP2G to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key BZ5LBQ
adding BZ5LBQ to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key FZ080Y
I am clearly not understanding somehting.
Any suggestions would be greatly appreciated.
Just because a key exists doesn't mean that the corresponding value is not $null.
See below for an example:
$a = #{ keyName = "Value" }
$b = #{ keyName = $null }
foreach($key in $a.Keys){
if($b.ContainsKey($key)){
$bValue = $b[$key] # or `$b.$key` if the key is a string
if($null -eq $bValue){
Write-Host "Key '$key' exists in '`$b', but its value is `$null :("
}
}
}
Without details about how you populate your hashtables it's hard to say why :)
I got the code to work by changing from hashtable.containskey to -contains. In the following code snippit, i commented our the line containing .containsKey($UserIDFromAD) and added the line containing $UserIDsFromFile.keys -contains $UserIDFromAD. The code now works as expected.
This has to be something to do with the difference between checking by reference and checkig by value. I know how this works in Java but not in Powershell.
#if ($UserIDsFromFile.containsKey($UserIDFromAD)){ #If the UserID is in Yesterdays list ignore it.
if ($UserIDsFromFile.keys -contains $UserIDFromAD){ #If the UserID is in Yesterdays list ignore it.

Issue with moving multiple items from one outlook folder to another - Powershell

I am trying to select multiple emails from on outlook inbox folder via mapi addressing and want to move a copy of these emails to another folder in the same inbox.
Unfortunately my script seems to do whatever it wants, sometimes copying 6 emails before stopping with following failure, sometimes stopping right with the first email.
Failure:
... "veeam")} | ForEach-Object {$_.Copy().Move($Namespace.Folders.Item("$ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [ForEach-Object], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.PowerShell.Commands.ForEachObjectCommand
I could not find any solution for this and I am sitting here confused since in another mailbox the code works just fine.
Of course I am setting the variables $Mailbox and $TempWorkPath beforehand.
Thanks in advance for your help.
Trying to run the code in a foreach-loop is less performant and ends with the same issue.
About 3 hours of google search did not help me at all.
Just moving the object causes the code to break, probably because of indexiation?
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$OutlookSession = New-Object -ComObject Outlook.Application
$Namespace = $OutlookSession.GetNameSpace("MAPI")
$Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Items.Restrict('[UnRead] = True') | Where-Object {($_.Subject -match "ackup") -or ($_.SenderEmailAddress -match "veeam")} | ForEach-Object {$_.Copy().Move($Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Folders.Item("$TempWorkPath"))} | Out-Null
<# Do things with the selected/coppied emails #>
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($OutlookSession) | Out-Null
$OutlookSession = $null | Out-Null
In Theory an based on my tests in another folder this should work perfectly fine, create a copy of the email, move it to my folder and afterwards I can do things with it.
Well, I think I found my way around the issue. Running the command in a while loop instead of an foreach loop seems to work better.
$Inbox = $Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Items.Restrict('[UnRead] = True') | Where-Object {($_.Subject -match "ackup") -or ($_.SenderEmailAddress -match "veeam")}
$MailCounter = $Inbox.Count
$HelperForCounting = 0
while ($MailCounter -gt $HelperForCounting)
{
$Inbox[$MailCounter].Copy().Move($Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Folders.Item("$TempWorkPath"))
$MailCounter = $MailCounter - 1
}
Greetings
I also had this issue with processing emails on Outlook. My overall scheme is to process emails folder by folder. I traced the issue to the Emails.getNext() function. My completely uneducated guess is it has something to do with parallel processing of Emails and how it grabs them in ForEach() and getNext(). The problem went away by using the getLast().
Note in the following code it will just move all read emails to archive folder and then some unread emails to corporate dump folder and most unread emails to the unread folder. This is itself just a mutation on the .p0r email script. There is a > $null at the end of the function block is where I originally had it on the ForEach loop and it worked as one would expect, but it does not work on the While loop blocking function. Instead that had to be moved to the location in the move unread section. Still a lot of room for improvement, getting some strange com errors but it will process through an inbox so long as GetLast() email is moved out of the folder.
As for my rationale on the root cause, I noticed that the failure to read a whole inbox is dependent on the size of the inbox. So each run my go through 2/3 of the remaining emails in the inbox.
# OUTLOOK RULES #
#################
# OUTLOOK RULES #
#################
#Import Object Library?
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
# VARIABLES
$index=0;
$pstPath = "C:\YOURPATHHERE"
# DISPLAY INFO
function display( [string]$subject, [string]$color , [string]$out) {
# REQUIRED LENGTH OF STRING
$len = 20
# STRINGS THAT ARE LONGER WILL BE CUT DOWN,
# STRINGS THAT ARE TO SHORT WILL BE MADE LONGER
if ( $subject.length -lt 20 ){
$toadd=20-$subject.length;
for ( $i=0; $i -lt $toadd; $i++ ){
$subject=$subject+" ";
}
$len = $subject.length
}
else { $len = 20 }
$index=$index+1
Write-host -ForegroundColor $color -nonewline " |" ((($subject).ToString()).Substring(0,$len)).ToUpper()
}
# CREATING OUTLOOK OBJECT
$outlook = New-Object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
# GETTING PST FILE THAT WAS SPECIFIED BY THE PSTPATH VARIABLE
$pst = $namespace.Stores | ?{$_.FilePath -eq $pstPath}
# ROOT FOLDER
$pstRoot = $pst.GetRootFolder()
# SUBFOLDERS
$pstFolders = $pstRoot.Folders
$fArchive = $pstFolders.Item("Archive")
# PERSONAL SUBFOLDER
$personal = $pstFolders.Item("Personal")
# INBOX FOLDER
$DefaultFolder = $namespace.GetDefaultFolder(6)
# INBOX SUBFOLDERS
$InboxFolders = $DefaultFolder.Folders
# DELETED ITEMS
$DeletedItems = $namespace.GetDefaultFolder(3)
# EMAIL ITEMS
$Emails = $DefaultFolder.Items
$workingFile = [IO.Path]::GetTempFileName()
# PROCESSING EMAILS
$currentWriteFolder = $pstFolders.Item("Archive")
While ($Emails.count -gt 0) {
$Email = $Emails.GetLast()
#Move all reads into Archive
if (!$Email.Unread) {
$email.move($fArchive) > $null
continue
}
#Filter unread items by sender
$WriteString = $Email.SenderEmailAddress.ToString()
[IO.File]::WriteAllLines($workingFile, $WriteString)
if (Select-String -Path $workingFile -Pattern "company") {
$email.move($currentWriteFolder.Folders.Item("globalcorp"))
continue
}
$email.move($pstFolders.Item("Unread"))
} # > $null
[IO.File]::Delete($workingFile)
Write-host ""

String matching in PowerShell

I am new to scripting, and I would like to ask you help in the following:
This script should be scheduled task, which is working with Veritas NetBackup, and it creates a backup register in CSV format.
I am generating two source files (.csv comma delimited):
One file contains: JobID, FinishDate, Policy, etc...
The second file contains: JobID, TapeID
It is possible that in the second file there are multiple same JobIDs with different TapeID-s.
I would like to reach that, the script for each line in source file 1 should check all of the source file 2 and if there is a JobID match, if yes, it should have the following output:
JobID,FinishDate,Policy,etc...,TapeID,TapeID....
I have tried it with the following logic, but sometimes I have no TapeID, or I have two same TapeID-s:
Contents of sourcefile 1 is in $BackupStatus
Contents of sourcefile 2 is in $TapesUsed
$FinalReport =
foreach ($FinalPart1 in $BackupStatus) {
write-output $FinalPart1
$MediaID =
foreach ($line in $TapesUsed){
write-output $line.split(",")[1] | where-object{$line.split(",")[0] -like $FinalPart1.split(",")[0]}
}
write-output $MediaID
}
If the CSV files are not huge, it is easier to use Import-Csv instead of splitting the files by hand:
$BackupStatus = Import-Csv "Sourcefile1.csv"
$TapesUsed = Import-Csv "Sourcefile2.csv"
This will generate a list of objects for each file. You can then compare these lists quite easily:
Foreach ($Entry in $BackupStatus) {
$Match = $TapesUsed | Where {$_.JobID -eq $Entry.JobID}
if ($Match) {
$Output = New-Object -TypeName PSCustomObject -Property #{"JobID" = $Entry.JobID ; [...] ; "TapeID" = $Match.TapeID # replace [...] with the properties you want to use
Export-Csv -InputObject $Output -Path <OUTPUTFILE.CSV> -Append -NoTypeInformation }
}
This is a relatively verbose variant, but I prefer it like this.
I am checking for each entry in the first file whether there is a matching entry in the second. If there is one I combine the required fields from the entry of the first list with the ones from the entry in the second list into one object that I can then export very comfortably using Export-Csv.

Uploading CSV to Sharepoint, choice column not uploading correctly

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!

PowerShell - Sorry, we couldn't find Microsoft.PowerShell.Core\FileSystem::

I'm trying to modify the script created by Boe Prox that combines multiple CSV files to one Excel workbook to run on a network share.
When I run it locally, the script executes great and combines multiple .csv files into one Excel workbook.
Clear-Host
$OutputFile = "ePortalMonthlyReport.xlsx"
$ChildDir = "C:\MonthlyReport\*.csv"
cd "C:\MonthlyReport\"
echo "Combining .csv files into Excel workbook"
. C:\PowerShell\ConvertCSVtoExcel.ps1
Get-ChildItem $ChildDir | ConvertCSVtoExcel -output $OutputFile
echo " "
But when I modify it to run from a network share with the following changes:
Clear-Host
# Variables
$OutputFile = "ePortalMonthlyReport.xlsx"
$NetworkDir = "\\sqltest2\dev_ePortal\Monthly_Report"
$ChildDir = "\\sqltest2\dev_ePortal\Monthly_Report\*.csv"
cd "\\sqltest2\dev_ePortal\Monthly_Report"
echo "Combining .csv files into Excel workbook"
. $NetworkDir\ConvertCSVtoExcel.ps1
Get-ChildItem $ChildDir | ConvertCSVtoExcel -output $OutputFile
echo " "
I am getting an error where it looks like it using the network path twice and I am not sure why:
Combining .csv files into Excel workbook
Converting \sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv
naming worksheet 001_StatsByCounty
--done
opening csv Microsoft.PowerShell.Core\FileSystem::\sqltest2\dev_ePortal\Monthly_Report\\sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv) in excel in temp workbook
Sorry, we couldn't find Microsoft.PowerShell.Core\FileSystem::\sqltest2\dev_ePortal\Monthly_Report\\sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv. Is it possible it was moved, renamed or deleted?
Anyone have any thoughts on resolving this issue?
Thanks,
Because in the script it uses the following regex:
[regex]$regex = "^\w\:\\"
which matches a path beginning with a driveletter, e.g. c:\data\file.csv will match and data\file.csv will not. It uses this because (apparently) Excel needs a complete path, so if the file path does not match, it will add the current directory to the front of it:
#Open the CSV file in Excel, must be converted into complete path if no already done
If ($regex.ismatch($input)) {
$tempcsv = $excel.Workbooks.Open($input)
}
ElseIf ($regex.ismatch("$($input.fullname)")) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
}
Else {
$tempcsv = $excel.Workbooks.Open("$($pwd)\$input")
}
Your file paths will be \\server\share\data\file.csv and it doesn't see a drive letter, so it hits the last option and jams $pwd - an automatic variable of the current working directory - onto the beginning of the file path.
You might get away if you edit his script and change the regex to:
[regex]$regex = "^\w\:\\|^\\\\"
which will match a path beginning with \\ as OK to use without changing it, as well.
Or maybe edit the last option (~ line 111) to say ...Open("$($input.fullname)") as well, like the second option does.
Much of the issues are caused in almost every instance where the script calls $pwd rather than $PSScriptRoot. Replace all instances with a quick find and replace.
$pwd looks like:
PS Microsoft.PowerShell.Core\FileSystem::\\foo\bar
$PSScriptRoot looks like:
\\foo\bar
The second part i fixed for myself is what #TessellatingHeckler pointed out. I took a longer approach.
It's not the most efficient way...but to me it is clear.
[regex]$regex = "^\w\:\\"
[regex]$regex2 = "^\\\\"
$test = 0
If ($regex.ismatch($input) -and $test -eq 0 ) {
$tempcsv = $excel.Workbooks.Open($input)
$test = 1 }
If ($regex.ismatch("$($input.fullname)") -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
$test = 1}
If ($regex2.ismatch($input) -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open($input)
$test = 1 }
If ($regex2.ismatch("$($input.fullname)") -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
$test = 1}
If ($test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($PSScriptRoot)\$input")
$test = 0 }