I have the following powershell scripts that iterates through all list items and workflows, trying to find old running workflows
The logic is OK, but I need to be able to export to to csv with the columns
ListeItemUrl, ListItemName, Nr of Days Opened.
$web = get-spweb https://mysite.com/sites/billing
$list = $web.Lists["Bill Cycles"]
$count = 0
foreach($wf in $list.WorkflowAssociations)
{
if ($wf.Name -like "*Previous Version*")
{
Write-Host 'Bill Cycles with old workflow: ' $wf.Name
foreach($listitem in $list.Items)
{
if($listitem.ContentType.Name -eq "Bill Cycle")
{
$workflows = $listitem.Workflows
foreach($Workflow in $listitem.Workflows)
{
if($Workflow.AssociationId -eq $wf.Id)
{
$count = $count+1
Write-Host $listitem.Name
Write-Host 'https://mysite.com/sites/billing/'$listitem.Url.TrimStart();
Write-Host 'Workflow opened for: ' ((Get-Date) - $Workflow.Created).Days
}
}
}
}
}
}
Write-host 'Count: ' $count
Then with the exported file I can easily sort by nr of days and deliver the report I need.
Format your output as a csv - ListeItemUrl, ListItemName, NrofDaysOpened - and pipe it to Export-Csv cmdlet( you can find out more by running get-help Export-Csv). You will have to change Write-Host to Write-Output
Related
My PowerShell script is as follows, I have got all the sites in farm saved as CSV file using Export-Csv command.
$farmList = Import-Csv "TestFarm.csv"
$farmList1 = Import-Csv "OtherFarm1.csv"
foreach ($site in $farmList)
{
Write-Host "db - ", $site
foreach ($farmsite in $farmList1)
{
if ($site -eq $farmsite) {
Write-Host "matching site found for ", $farmsite
break
}
Write-Host "farm - ", $farmsite
}
}
My Excel files in CSV looks like
Site
/sites/TestSite
/sites/testsite1234
...
The second Excel file in CSV looks like
Site
/sites/TestSite
/sites/testsite1234
...
When I debug the program, I am getting a value of $site and $farmSite as
#{Site=/sites/TestSite} , but when I compare the values using -eq, the values do not match.
I have also tried using Compare-Object without success.
You need to compare the objects' Site properties instead of the objects themselves.
Change this:
if ($site -eq $farmsite) {
into this:
if ($site.Site -eq $farmsite.Site) {
If the files contain just this one property you could also expand it on import:
$farmList = Import-Csv "TestFarm.csv" | Select-Object -Expand Site
$farmList1 = Import-Csv "OtherFarm1.csv" | Select-Object -Expand Site
The latter would also allow you to simplify your code by using a -contains check:
foreach ($farmsite in $farmList1) {
if ($farmList -contains $farmsite) {
Write-Host "matching site found for $farmsite"
}
}
I have a list of files in a folder each are in this format: custID_invID_prodID or custID_invID_prodID_Boolvalue. For every file I need to break it into sections based on '_'. Currently I have this code:
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #()
$file += ([String]$f).Split("_")
$total = ([String]$f).Split("_") | Measure-Object | select count
Write-Host "${total}"
if($total -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
The problem is that Write-Host "${total}" equals #{Count=#} where # is real number of times "_" is found in file. How can I use $total inside my if statement to do different operations based upon the number of "_" found?
Would it not be simpler just to assign the parts you want directly to named variables rather than working with an array?
foreach($f in (Get-ChildItem test *.txt)) {
$custId, $invID, $prodID, $Boolvalue = $f.BaseName -split "_"
Write-Host $custId, $invID, $prodID, $Boolvalue
}
If the name only has 3 parts this will simply set $Boolvalue to an empty string.
Also note that you don't have to trim the extension off the last element after splitting, just use the BaseName property to get the name without extension.
You need to get the count-property value, like $total.count in your if test. You could also clean it up like this.
$files = Get-ChildItem test *.txt
foreach($f in $files){
$file = #(([String]$f).Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
for($i = 2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
Write-Host "${file}"
}
}
}
If you had included more information about what you were trying to do, we could clean it up alot more. Ex. It's seems like you want to do something like this:
Get-ChildItem test *.txt | ForEach-Object {
$file = #($_.BaseName.Split("_"))
Write-Host "$($file.Count)"
if($file.Count -eq 2) {
Write-Host $file
}
}
Seems to me that you're doing it the hard way. Why not:
$x = "aaa_bbb_ccc"
$cnt = $x.Split("_").count
I don't have much knowledge of Sharepoint Powershell but I'm attempting to piece something together to automate the checking of expiry date fields inside an infopath form library vs today's date.
I wish to compare every date in the following column,
Construction_x0020_Card_x0020_Expiry
To today's date. If the value is less or is null than today's date (meaning expired licence or no licence) modify the text in the following field:
Construction_x0020_Card_x0020_Type
This is what I have so far;
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
$webUrl = "https://####"
$listDisplayName = "HR Employee Record"
$ExpiryDate = #($Construction_x0020_Card_x0020_Expiry)
?? Not sure if this works,$Construction_x0020_Card_x0020_Expiry is an array?
$TodayDate = (Get-Date).ToString("dd-MM-yyyy")
$convertedExpiryDate = $ExpiryDate.ToString("dd-MM-yyyy")
?? Not sure if this works, $Construction_x0020_Card_x0020_Expiry is System.Object[] trying to convert to System.DateTime
foreach ($item in $convertedExpiryDate.items | where {$convertedExpiryDate -lt $TodayDate}) {
if ($convertedExpiryDate -eq $null) {
Write-Host #($Construction_x0020_Card_x0020_Type)["$null"]
} elseif ($convertedExpiryDate -lt $TodayDate) {
Write-Host #($Construction_x0020_Card_x0020_Type)["Expired"]
} elseif ($convertedExpiryDate -ge $TodayDate) {
Write-Host #($Construction_x0020_Card_x0020_Type)["Valid"]
}
}
Thanks,
Lance
I'm having a strange issue here using the NewWebPage xml command in SP 2010 Foundation. The below code works does not work
cls
Write-Host "Production Manager v8.0 Deployment Utility" -ForegroundColor red
Write-Host ""
#Starting the script. Lets see if we can find the configuration and map files first.
Write-Host -NoNewline "Checking for the Configuration.xml, PageMap.xml and PageTemplate.xml files: " -ForegroundColor white
if((Test-Path "Configuration.xml") -and (Test-Path "PageMap.xml") -and (Test-Path "PageTemplate.xml"))
{
Write-Host "FOUND" -ForegroundColor green
}
else
{
Write-Host "NOT FOUND" -ForegroundColor red
Write-Host "Check for the necessary files and try again."
Write-Host ""
Write-Host ""
exit
}
Write-Host "Reading Configuration.xml"
[xml]$Configuration = Get-Content Configuration.xml
Write-Host "Reading PageMap.xml"
[xml]$PageMap = Get-Content PageMap.xml
Write-Host "Reading from Production Manager Site: "$Configuration.Configuration.SiteConfiguration.SiteURL
#Some variables necessary for the loop
$pageCreationLoopIterations = 0
$pageLayout = ""
$pageTitle = ""
$createPageCommand = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $productionManagerLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
#Beginning the loop
Write-Host "Running through the PageMap file"
foreach($Page in $PageMap.Pages.Page)
{
$web = Get-SPWeb $Configuration.Configuration.SiteConfiguration.SiteURL
$productionManagerLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageName = if($Page.SelectSingleNode("PageName")) { $Page.PageName } else { $Configuration.Configuration.PageConfiguration.DefaultPageName }
$pageLayout = if($Page.SelectSingleNode("PageLayout")) { $Page.PageLayout } else { $Configuration.Configuration.PageConfiguration.DefaultPageLayout }
Write-Host 'Creating Page ' $pageName
$web.ProcessBatchData($createPageCommand)
}
But this one works just fine every time I run it:
$url = "http://mpm8/mpm";
$listname = "Production Manager"
$web = Get-SPWeb $url
$pagesLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageLayout = 8
$cmd = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $pagesLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
$web.ProcessBatchData($cmd)
I really cannot see anything different between the two scripts. The error I get running the first one is:
<Result ID="0,NewWebPage" Code="-2130575350">
<ErrorText>Invalid URL Parameter.
The URL provided contains an invalid Command or Value. Please check the URL again. </ErrorText></Result>
Can you help me on this one? Maybe I cannot run this thing out a foreach loop? :(
Thanks!
You have set the $createPageCommand first (before the loop), then set the variables that it needs second (in the loop).
If you run this in the ISE and step through (ie debug it), you can see the variables and their values, otherwise simply emit the values of the variables to screen as you run through the loop to ensure it is setting them correctly.
So, in your example, place the $createPageCommand within the loop and after $productionManagerLibrary is set. Then, immediately before $web.ProcessBatchData($createPageCommand) you should emit the value of $createPageCommand to ensure it is OK.
I've only eye balled this without running it, but please us know if this is the reason!
How it is posible to fix this script?
Yes, I´m changing the collection in the foreach loop and this is the reason for this error.
An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
At C:\Users\user\Documents\PowerShell\ChangeAllListsV2.ps1:47 char:20
+ foreach <<<< ($list in $webLists)
+ CategoryInfo : InvalidOperation: (Microsoft.Share...on+SPEnumerator:SPEnumerator) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
#Script change in all lists the required field property "testfield" to false
#Part 0 - Configuration
$urlWebApp = "http://dev.sharepoint.com"
$countFound = 0
$countList = 0
$countFoundAndChange = 0
#Part 1 - PreScript
$snapin = Get-PSSnapin | Where-Object {$_.Name -eq "Microsoft.SharePoint.Powershell"}
if ($snapin -eq $null)
{
Write-Host “Loading SharePoint Powershell”
Add-PSSnapin Microsoft.SharePoint.Powershell
}
#Part 2 - Script
$webApp = Get-SPWebApplication $urlWebApp
#$webApp | fl
$webAppSites = $webApp.sites
foreach($site in $webAppSites)
{
Write-Host "***********************************************************************"
Write-Host "Found site: " $site -foreground blue
$siteAllWebs = $site.AllWebs
foreach($web in $siteAllWebs)
{
Write-Host "Found web: " $web -foreground blue
#$web | fl
$webLists = $web.Lists
foreach($list in $webLists)
{
$countList ++
Write-Host "Found list: " $list -foreground blue
#Change list property
$field = $Null
$field = $list.Fields["testfield"]
if($field){
Write-Host "Field found: " $list -foreground green
#Write-Host "in web: " $web -foreground green
$countFound ++
try{
if($field.Required)
{
#######################################################
$field.Required = $False
$field.Update()
#######################################################
$field = $Null
Write-Host "Done!: Change list: " $list -foreground green
$countFoundAndChange ++
}else{
Write-Host "Already!: Change list: " $list -foreground green
}
}
catch{
$field = $Null
Write-Host "Error!: Change list: " $list -foreground red
Write-Host "in web: " $web -foreground red
$_
}
}
}
}
}
Write-Host "Found lists: " $countList
Write-Host "Found lists with column [testfield]: " $countFound
Write-Host "Change lists with column [testfield]: " $countFoundAndChange
The SPListCollection tends to modify the collection when updating its properties (fields, event receivers, etc.). You can use a for-loop instead:
for ($i = 0; $i -lt $webLists.Count; $i++)
{
$list = $web.Lists[$i];
# ...
}
I know this is a pretty old thread. This is for anybody ending up to this page looking for an answer.
The idea is, like other answers suggest, to copy the collection (using the clone() method) to another and iterate "another" and modify the original variable inside the loop without having to use for in place of foreach:
A collection of type ArrayList:
[System.Collections.ArrayList]$collection1 = "Foo","bar","baz"
$($collection1.Clone()) | foreach {
$collection1.Remove("bar")
}
Output:
PS H:\> $collection1
Foo
baz
A collection of type Hashtable:
[System.Collections.Hashtable]$collection2 = #{
"Forum" = "Stackoverflow"
"Topic" = "PowerShell"
}
$($collection2.Clone())| foreach {
$collection2.Remove("Forum")
}
Output:
PS H:> $collection2
Name Value
---- -----
Topic PowerShell
And, a basic array:
[System.Array]$collection3 = 1, 2, 3, 4
$($collection3.Clone()) | foreach {
$collection3[$collection3.IndexOf($_)] = 10
}
Output:
PS H:\> $collection3
10
10
10
10
As long as your collection is not of fixed size.
You can try copying the collection you're currently iterating on to another collection (an array or a list) and then iterate on that new collection.
Something like this:
$collection = #(1, 2, 3, 4)
$copy = #($collection)
$collection[0] = 10
$collection -join " "
$copy -join " "
The code above gives the following output:
10 2 3 4
1 2 3 4
Note that the $copy variable refers to a different collection.
Check: http://soreddymanjunath.blogspot.in/2014/07/collection-was-modified-enumeration.html
Here is anonther example for same issue
if($web.IsMultilingual -eq $true )
{
foreach($cul in $web.SupportedUICultures)
{
if($cul.LCID -ne $webCul.LCID -and $cul.LCID -ne "1033")
{
$web.RemoveSupportedUICulture($cul)
}
}
$web.Update()
}
for the first time it will go through the loop foreach will remove supported culture for frist time, when it comes to loop for the second iteration then it will throw you the exception “Collection was modified; enumeration operation may not execute”,
Solution to Above problem is to Store to values to modified in a Arraylist and try to modify which will fix the problem, Here i am storing Arraylist called enumcul and inserting values into it and modifying it...
$enumcul=New-Object Collections.ArrayList
$i=0
if($web.IsMultilingual -eq $true )
{
foreach($cul in $web.SupportedUICultures)
{
if($cul.LCID -ne $webCul.LCID -and $cul.LCID -ne "1033")
{
$enumcul.Insert($i, $cul)
$i=$i+1
}
}
foreach( $k in $enumcul)
{
$web.RemoveSupportedUICulture($k)
$web.Update()
}