Accessing Sharepoint document library list using Windows PowerShell script - c#

Task: I need to loop thru all files on Sharepoint site and download them to local folder.
Script:
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$s = Get-SPSite “https://abc.abctools.consumer.abc.net/sites/rtc/report/SitePages/Forms/AllPages.aspx”
$files = $s.RootWeb.GetFolder("Shared Documents").Files
foreach ($file in $files) {
Write-host $file.Name
$b = $file.OpenBinary()
$fs = New-Object System.IO.FileStream(("C:\SP Document Library files\"+$file.Name), [System.IO.FileMode]::Create)
$bw = New-Object System.IO.BinaryWriter($fs)
$bw.Write($b)
$bw.Close()
}
Errors: I get when i try to run/execute above script.
1. "You cannot call a method on a null-valued expression."
New-Object: Exception calling ".ctor" with "2" agrument(s): "Could not find a part of the path 'C:\SP Document Library files\'
New-Object: Constructor not found. Cannot find an appropriate constructor for the type system.IO.BinaryWrite.
The term 'Get-SPSite' is not recognized as a cmdlet, function, operable program or script file. verify the term and try again.
Response on Error #2: I have created the folder & named "SP Document Library files" so that path is correct C:\SP Document Library files not sure why i see that msg.
Library files (.csv,.xls) exists in a folder.
Folder name : 2014-01-31.
1. What to do to in order resolve above error message(s).
2. I'm not sure if i need to use whole sharepoint url or part of it.Educate me on that.
Thanks!!

Try by giving ReadWrite FileAccess.
And you can get the root web directly if you know the Url instead of using SPSite.
Here's my script I use and has always worked
$siteUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc”'
$listUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc/Shared Documents”'
$folderPath = 'C:\\....'
$web = Get-SPWeb -Identity $siteUrl
$list = $web.GetList($listUrl)
$items = $list.Items
ForEach ($item in $items)
{
$binary = $item.File.OpenBinary();
$folderPathToSave = $folderPath + "\\" + $item.Name;
if ($binary -ne $null)
{
$stream = New-Object System.IO.FileStream($folderPathToSave,[System.IO.FileMode]::Create,[System.IO.FileAccess]::ReadWrite);
$writer = New-Object System.IO.BinaryWriter($stream);
$writer.Write($binary);
$writer.Close();
}
}
$web.Dispose()
The original post:
http://naimmurati.wordpress.com/2012/06/07/backup-documents-from-document-library-with-powershell-script/

Related

Run a File, located in Program Files (x86) from PowerShell

I run a Script from C# : Code Looks like this :
r.Open();
using(PowerShell ps = PowerShell.Create().AddScript(_scriptPath)) {
ps.Runspace = r;
ps.Invoke();
if (ps.HadErrors) {
List<string> _items = new List<string>();
var errorMessage = new StringBuilder();
foreach (ErrorRecord err in ps.Streams.Error) {
errorMessage.AppendLine(err.ToString());
}
_items.Add(errorMessage.ToString());
foreach (var i in _items) {
MessageBox.Show(i);
}
}
}
In the script i try to run copy-item, for that i need a path :
Script Path looks like this:
$ScriptPath = Split-Path $Script:MyInvocation.MyCommand.Path
With a Write-Host i got the Path, looks like this :
C:\Program Files (x86)\some\random\Folder\File.ps1
With the Ps.Streams.Error (in my C# Code) i'm getting following Error Message.
The term "x86" was not recognized as the name of a cmdlet, function, script file, or executable program.
program. Check the spelling of the name, or that the path is correct (if included), and then
repeat the procedure.
How do I need to write my Code, so Powershell understands my Path?
So i found a solution, or better the Solution :)
My Powershell Script is fine, no error there.
But when running a file from a Path, that has spaces in it, it need special Formation.
Like #iRon wrote.
But not in the script, it is needed it in the C# Code, when calling the PS Script.
My Path looked like this :
C:\Program Files (x86)\some\random\Folder\File.ps1
But should've looked like this to execute :
& 'C:\Program Files (x86)\some\random\Folder\File.ps1'
Added the "& ' Script '" to the Code :
private void executeScripts()
{
string _dirPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string _sPath = Path.GetDirectoryName(_dirPath) + #"\ExecuteScripts\DistributeFiles.ps1";
string _scriptPath = "& '" + _sPath + "'"; // <- This is where i added the Formation
Trace.TraceWarning(_scriptPath);
using (PowerShellProcessInstance pspi = new PowerShellProcessInstance())
{
//rest of the Code in my Post
}
}
Silly mistake took me quite some hours -.-
But I'm glad I found it. Thanks

How to get Build Id from tfs during the build in C#

To get a log link i need the id of current build, I tried to use this in my C# code, but it didn't return Build Id:
var envVars;
envVars = Environment.GetEnvironmentVariables();
To get the Build.Id during the build with C# you can sue this line:
string buildId = Environment.GetEnvironmentVariable("Build_BuildId",Environment.VariableTarget.Process);
You can get the build Id from this variable
$(Build.BuildId)
Pass it as parameter(exactly as it written here) to the tool(console app?) that you are building.
You can check other variables from this link
https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml
The solution is to put this var in txt file via PowerShell script, and than get it by C#
PS:
if(!(Test-Path -Path C:\BuildVariables)){
New-Item -ItemType directory -Path C:\BuildVariables
}
Out-File -FilePath C:\BuildVariables\buildId.txt -Force -InputObject $(Build.Buildid)
C#:
public static string ShowEnvironmentVariables()
{
string var = File.ReadAllText("C:\\BuildVariables\\buildId.txt");
return var;
}

Deleting file from FTP server using PowerShell

I wrote a PowerShell script to download files using FTPto my local machine.
After the file is downloaded, I want to delete it from the FTP server. I wrote this code too. But unfortunately it's not working.
Can anyone help me to point out what is wrong with my code? Any clues will be helpful ...
Here is my code
function Delete-File($Source,$Target,$UserName,$Password)
{
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile ($Source,$Target,$UserName,$Password)
{
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($username,$password)
if(Test-Path $targetpath)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $sourceuri $targetpath $user $pass
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
Delete-File $sourceuri $targetpath $user $pass
Every time I execute this script, the only statement I get
ABCDEF file downloaded
or
ABCDEF file exists
I guess Delete-File is not executing at all... any type of clue will be helpful.
You cannot use Test-Path with an FTP URL. So your code for deleting the file will never execute.
Just remove the Test-Path condition and try to delete the file unconditionally. Then check for error and treat "file not exist" error as you like.
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName, $Password)
try
{
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse() | Out-Null
Write-Host ("File {0} deleted." -f $Source)
}
catch
{
if ($_.Exception.InnerException.Response.StatusCode -eq 550)
{
Write-Host ("File {0} does not exist." -f $Source)
}
else
{
Write-Host $_.Exception.Message
}
}
Though as you try to delete the file only after you successfully download it, it's actually unlikely that the file won't exist.
So you may consider to give up on any specific error handling.
I ran your script locally to try it out and found a few issues. I refactored also a few things just to make it a bit more readable (at least in my opinion :) ).
Issues
Line 13. $Source parameter there is a ftp://... path. Test-Path will always return $false here and the delete request will never be executed.
In Get-FTPFile you were not referencing the input parameter of the function, instead the variables defined outside of it. I don't know if this was just a copy & paste bug or on purpose. In my opinion you should use the parameters you sent to the function. Lines 38, 39 and 50 at least in my code below.
Code
function Delete-File
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Target)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $Source $Target $UserName $Password
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
#Delete-File $sourceuri $targetpath $user $pass
There are also ready made PowerShell cmdlets for talking to FTP/SFTP, no need to create everything from scratch, unless you are required to.
Anyway, for reference, check out e.g.
https://www.powershellgallery.com/packages/PSFTP
https://www.powershellgallery.com/packages/WinSCP

search file in specific child folder in powershell

As per requirement, search file in specific child folder and then copy to destination path. I had implemented in c#.NET earlier but now want to convert in PowerShell.
foreach (var directory in Directory.EnumerateDirectories(sourcePath, specificChildFolder, SearchOption.AllDirectories))
{
var pathSrc = Path.Combine(directory, "xyz.config");
if (File.Exists(pathSrc))
{
File.Copy(pathSrc, pathDst, true);
break;
}
}
Here is code which you can use:
$path = "C:\Users\user1\Desktop\Config Rework\"
$destination = "c:\destination\"
foreach ($filepath in [System.IO.Directory]::EnumerateFiles($path,"xyz.config","AllDirectories"))
{
$file = New-Object System.IO.FileInfo($filepath)
if(Test-Path $file)
{
write-host $file.FullName
copy $file $destination
Write-Host 'Copied' $file ' to ' $destination
}
}
Output:
C:\Users\user1\Desktop\Config Rework\xyz\xyz.config
Copied C:\Users\user1\Desktop\Config Rework\xyz\xyz.config to c:\destination\
This has looped through the child folders under C:\Users\user1\Desktop\Config Rework, checked if xyz.config exists and then copied it to the destination folder.
Note that if your directory looks as follows:
xyz.config under xyz folder will take precedence as it will loop through the child folder.

Using powershell with .NET returning null

I am using .NET with powershell trying to retrieve result of Get-Acl command of specific AD object. Unfortunately when I run the code from C# code I get 0 result. Also the ThrowIfError is not throwing any error.
Command test01 = new Command("import-module");
test01.Parameters.Add("name", "activedirectory");
session.Commands.AddCommand(test01);
Command test0 = new Command("Set-Location");
test0.Parameters.Add("Path", "AD:");
session.Commands.AddCommand(test0);
Command test1 = new Command("Get-Acl");
test1.Parameters.Add("Path", identity);
session.Commands.AddCommand(test1);
session.AddCommand("select-object");
session.AddParameter("Property", "Access");
var tempResults1 = session.Invoke();
ThrowIfError();
private void ThrowIfError()
{
var errors = session.Streams.Error;
if (errors.Count > 0)
{
var ex = errors[0].Exception;
session.Streams.ClearStreams();
// Never close session to dispose already running scripts.
throw ex;
}
}
This code running on server in powershell is working correctly:
PS AD:\> Import-Module -Name activedirectory
PS AD:\> set-location ad:
PS AD:\> get-acl -path <distinguishedNameOfADObject>
Question
How to get the same result like I get from Powershell? I should get atleast something not a zero result.
Little background:
I am trying to get Send-As rights not using Get-ADPermission cmdlet because its taking too long time when I need to search for rights within thousands of mailboxes. Using this article link I am trying another approach to get the rights. I have already the slower version working using C# code:
Command command = new Command("Get-ADPermission");
command.Parameters.Add("Identity", identity);
session.Commands.AddCommand(command);
session.AddCommand("where-object");
ScriptBlock filter = ScriptBlock.Create("$_.ExtendedRights -eq 'send-as'");
session.AddParameter("FilterScript", filter);
session.AddCommand("select-object");
session.AddParameter("Property", "User");
tempResults = session.Invoke();
The better way is to define a powershell-script instead of multiple commands to get the values you need. Example with your powershell-code:
using System.Collections.ObjectModel;
using System.DirectoryServices;
using System.Management.Automation;
namespace GetAclPowershellTest
{
class Program
{
static void Main(string[] args)
{
/****Create Powershell-Environment****/
PowerShell PSI = PowerShell.Create();
/****Insert PowershellScript****/
string Content = "param($object); Import-Module ActiveDirectory; Set-Location AD:; Get-ACL -Path $object"; //Add Scrip
PSI.AddScript(Content);
PSI.AddParameter("object", "<distinguishedNameOfADObject>");
/****Run your Script with PSI.Invoke()***/
Collection<PSObject> PSIResults = PSI.Invoke();
/****All Errors****/
Collection<ErrorRecord> Errors = PSI.Streams.Error.ReadAll();
/****needed, because garbagecollector ignores PSI otherwise****/
PSI.Dispose();
/**** Your ACL-Object ****/
ActiveDirectorySecurity MyACL = (ActiveDirectorySecurity)PSIResults[0].BaseObject;
/*insert your code here*/
}
}
}
This example works for me.
You have to set a reference to the Powershell-Assembly (Usually you can find it at "C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll")
Benefit of this solution is, you could read a .ps1-File you got from someone, fill the parameters with the objects you have and the script runs like in a standard powershell-session. The only requirement to set parameters is the param-part in the Script.
More Infos about param: https://technet.microsoft.com/en-us/library/jj554301.aspx
Hope, this helps...
Greetings, Ronny
Update:
string Content = "param($object); Import-Module ActiveDirectory; Set-Location AD:; (Get-ACL -Path $object).Access | Where-Object{($_.ActiveDirectoryRights -eq 'ExtendedRight') -and ($_.objectType -eq 'ab721a54-1e2f-11d0-9819-00aa0040529b')}";
And the loop at the end looks like this now:
foreach (PSObject o in PSIResults)
{
ActiveDirectoryAccessRule AccessRule = (ActiveDirectoryAccessRule)o.BaseObject;
/**do something with the AccessRule here**/
}

Categories