none
Powershell to get- every subsite in farm

    Question

  • (Powershell noob here)  I'm attempting to write a script to delete every /SitePages/Results.aspx file in the farm, but only for SubSites.  So far i've got this, which gives me each site in the farm, but i only want to do this on subsites.

    $sites= Get-SPSite -WebApplication $url|Where-Object{$_.Url -like"$url/sites/*"}
    foreach($site in $sites){
    # Delete fuctions here.
    }
    I'd really like to build this script on my own but i wont argue with anyone that just happens to have this already built.  :) 
    • Edited by Matt Petrie Wednesday, July 10, 2013 10:37 PM file not folder!
    Wednesday, July 10, 2013 10:36 PM

Answers

  • You can't delete items from a collection while you are enumerating it, because it dirties the collection.

    To make your script work, make a copy of the files collection, and enumerate through the copy. Then use the SPFolder.Files collection to call Delete to delete any files.

    All you need to do is change the foreach section of you code to look like this:

    #Create a copy of the files collection
    $files = $spfolder.Files;

    #Loop through the copy of the files collection foreach($file in $files){ #Delete the file named $pageName if($file -match $pageName) { #Call the delete method on the spFolder.Files collection $spfolder.Files.Delete($file.Url); Write-Host "Deleted:" $file; } }



    Regards, Matthew
    MCPD | MCITP
    My Blog
    Please remember to click "Mark As Answer" if a post solves your problem or "Vote As Helpful" if it was useful.

    I just added a webpart to the TechNet Gallery that allows administrative users to upload, crop and format user profile photos. Check it out here: Upload and Crop User Profile Photos


    Wednesday, July 17, 2013 4:13 PM
  • Hi Matt,

    Get sub site under site collection:

    $sites= Get-SPSite -WebApplication $url|Where-Object{$_.Url -like"$url/sites/*"}
    foreach($site in $sites)
    {   
        foreach($subSite in $site.Webs)                                                   {                                  
          # Delete functions here.                     
                            
       }
    }

    Best Regards.


    Kelly Chen
    TechNet Community Support

    Thursday, July 11, 2013 8:56 AM
  • Hi Matt,

    Try this (I just tested it).

    (Get-SPWebApplication "http://yourwebapplication").Sites;
    foreach($s in $sites){
    $webs = $s.RootWeb.Webs;
    foreach($web in $webs)
    {
    	$s.RootWeb.Webs.Delete($web.ServerRelativeUrl.ToString().Trim('/'));	
    }
    }


    Regards, Matthew
    MCPD | MCITP
    My Blog
    Please remember to click "Mark As Answer" if a post solves your problem or "Vote As Helpful" if it was useful.

    I just added a webpart to the TechNet Gallery that allows administrative users to upload, crop and format user profile photos. Check it out here: Upload and Crop User Profile Photos


    Thursday, July 11, 2013 9:10 AM

All replies

  • Hi Matt,

    Get sub site under site collection:

    $sites= Get-SPSite -WebApplication $url|Where-Object{$_.Url -like"$url/sites/*"}
    foreach($site in $sites)
    {   
        foreach($subSite in $site.Webs)                                                   {                                  
          # Delete functions here.                     
                            
       }
    }

    Best Regards.


    Kelly Chen
    TechNet Community Support

    Thursday, July 11, 2013 8:56 AM
  • Hi Matt,

    Try this (I just tested it).

    (Get-SPWebApplication "http://yourwebapplication").Sites;
    foreach($s in $sites){
    $webs = $s.RootWeb.Webs;
    foreach($web in $webs)
    {
    	$s.RootWeb.Webs.Delete($web.ServerRelativeUrl.ToString().Trim('/'));	
    }
    }


    Regards, Matthew
    MCPD | MCITP
    My Blog
    Please remember to click "Mark As Answer" if a post solves your problem or "Vote As Helpful" if it was useful.

    I just added a webpart to the TechNet Gallery that allows administrative users to upload, crop and format user profile photos. Check it out here: Upload and Crop User Profile Photos


    Thursday, July 11, 2013 9:10 AM
  • I have tested this and i think it works for the most part.  I get error messages but it looks like it deletes the files that i want it to delete.  I'm not sure if i'm disposing everything correctly, and i'm sure its rather messy.  Anyone got tips?

    $webappUrl = "http://sharepoint/"; $libraryName = "SitePages"; $pageName = "Results.aspx"; foreach($webapp in get-spwebapplication $webAppUrl) { Write-Host "Web Application:" $webapp -foregroundcolor Blue; Write-Host "-------------------------------------" -foregroundcolor Blue; foreach($site in get-spsite -limit all -webapplication $webapp) { foreach($web in get-spweb -limit all -site $site) { foreach($web in $web.webs){ Write-Host "SubSite Name:" $web -foregroundcolor DarkGreen; #Lookup the folder named $LibraryName $spFolder = $web.GetFolder($libraryName); #List all files within folder foreach($file in $spfolder.Files){ #Delete the file named $pageName if($file -match $pageName) { $file.Delete() Write-Host "Deleted:" $file; } } $web.Dispose() Write-Host "-----------------"; } #I can put stuff in here if i want to do this to every site, rather than every subsite in a site collection. } $site.Dispose() } }


    Tuesday, July 16, 2013 10:29 PM
  • Hi Matt,

    What’s the error message do you get? I have a test on my server with your code, it works well to delete files from sub site.

    Best Regards.


    Kelly Chen
    TechNet Community Support

    Wednesday, July 17, 2013 3:03 AM
  • After it finds and deletes a file, i get the following message in Powershell.  The file deletes itself just fine and it moves on to find more files.  I have no doubt that this code is not the most efficient.

    An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
    At Z:\SharePoint\Test Scripts\DeleteResultsAspxFromAllSubsites.ps1:20 char:20
    +             foreach <<<< ($file in $spfolder.Files){
        + CategoryInfo          : InvalidOperation: (Microsoft.Share...on+SPEnumerator:SPEnumerator) [], RuntimeException
        + FullyQualifiedErrorId : BadEnumeration

    Wednesday, July 17, 2013 3:42 PM
  • You can't delete items from a collection while you are enumerating it, because it dirties the collection.

    To make your script work, make a copy of the files collection, and enumerate through the copy. Then use the SPFolder.Files collection to call Delete to delete any files.

    All you need to do is change the foreach section of you code to look like this:

    #Create a copy of the files collection
    $files = $spfolder.Files;

    #Loop through the copy of the files collection foreach($file in $files){ #Delete the file named $pageName if($file -match $pageName) { #Call the delete method on the spFolder.Files collection $spfolder.Files.Delete($file.Url); Write-Host "Deleted:" $file; } }



    Regards, Matthew
    MCPD | MCITP
    My Blog
    Please remember to click "Mark As Answer" if a post solves your problem or "Vote As Helpful" if it was useful.

    I just added a webpart to the TechNet Gallery that allows administrative users to upload, crop and format user profile photos. Check it out here: Upload and Crop User Profile Photos


    Wednesday, July 17, 2013 4:13 PM
  • Even if i add that code, i still get an error [An error occurred while enumerating through a collection: Collection was modified; en....]  Perhaps i still have the code wrong?  Do i have too many foreach's?


    $webappUrl = "http://popspsdevweb3:500/";
    $libraryName = "SitePages";
    $pageName = "Results.aspx";
    foreach($webapp in get-spwebapplication $webAppUrl)
    {
       Write-Host "Web Application:" $webapp -foregroundcolor Blue;
       Write-Host "-------------------------------------" -foregroundcolor Blue;
       foreach($site in get-spsite -limit all -webapplication $webapp)
       {
          foreach($web in get-spweb -limit all -site $site)
          {
             foreach($web in $web.webs){
                Write-Host "SubSite Name:" $web -foregroundcolor DarkGreen;
                $spFolder = $web.GetFolder($libraryName);
                #Create a copy of the files collection
                $files = $spfolder.Files;
                #Loop through the copy of the files collection
                foreach($file in $files){               
                   #Delete the file named $pageName
                   if($file -match $pageName)
                   {
    	          #Call the delete method on the spFolder.Files collection
    	          $spfolder.Files.Delete($file.Url);
    	          Write-Host "Deleted:" $file;
                   }
                }
                $web.Dispose()
                Write-Host "-----------------";
             }
    #I can put stuff in here if i want to do this to every site, rather than every subsite in a site collection.
          }
          $site.Dispose()
       }
    }

    Thursday, July 25, 2013 11:08 PM
  • Yeah, too many loops and not getting the right bit.

    $webappUrl = "http://popspsdevweb3:500/"; $libraryName = "SitePages"; $pageName = "Results.aspx"; foreach($webapp in get-spwebapplication $webAppUrl) { Write-Host "Web Application:" $webapp -foregroundcolor Blue; Write-Host "-------------------------------------" -foregroundcolor Blue; foreach($site in get-spsite -limit all -webapplication $webapp) { foreach($web in get-spweb -limit all -site $site) { Write-Host "SubSite Name:" $web -foregroundcolor DarkGreen; $spFolder = $web.GetFolder($libraryName); #Get the file which has the correct name $file = $spfolder.Files | where {$_.name -eq $pageName)

    #if there are no files with the name specified this will be a null object and so the following loop will not execute

    if ($file)

    { #Delete the file straight out of the folder object, quicker and doesn't touch the enumerator $spFolder.GetItemByUniqueID($file.UniqueID).Delete() Write-Host "Deleted:" $file; } $web.Dispose() Write-Host "-----------------"; } $site.Dispose() } }


    The

     foreach($web in $web.webs)

    Bit was wasted, you were already doing that for every web.

    The bit below will at best be slow as each time you try to delete a file you re-fetch the entire files collection again. I've replaced it with a 'GetItemByUniqueID' which should work, although i normally use that on the list object rather than the folder and haven't tested.

    	          #Call the delete method on the spFolder.Files collection
    	          $spfolder.Files.Delete($file.Url);
    	          Write-Host "Deleted:" $file;

    You're also deleting files in all the Site Collections as well as all the sub sites, as you know the root of a site collection contains a 'rootWeb' which is, obviously a web. That would be picked up by the '

       foreach($web in get-spweb -limit all -site $site)

    command, although it's possible that your $web.Webs might ommit it, but since i stripped that out for performance reasons I'm not sure.

    The way you were fetching files was a bit over the top, a nice where clause strips out a lot of the code and makes it a lot quicker.

    I haven't tested it but with a little work that should be a lot faster.

    Also as a warning this will not work on Large lists.

    I've stripped out your $web.dispose() cmd as


    Thursday, July 25, 2013 11:24 PM
  • Method invocation failed because [Microsoft.SharePoint.SPFolder] doesn't contain a method named 'GetItemByUniqueID'.
    At line:23 char:44
    +                 $spFolder.GetItemByUniqueID <<<< ($file.UniqueID).Delete()
        + CategoryInfo          : InvalidOperation: (GetItemByUniqueID:String) [], RuntimeException
        + FullyQualifiedErrorId : MethodNotFound
    Friday, July 26, 2013 6:50 PM