Powershell + EWS script to move all text messages to Inbox/SMS subfolder - c#

I'm hoping someone more experienced than me can post a Powershell script example (using the simplest possible method) that will...
Identify all messages that use "Text Message" OR "Multimedia Message" forms... then, move them to the Inbox/SMS subfolder.
Basically, I'm just trying to reproduce what my Outlook 2010 Rule does, except via command line.
If there's a pre-made cmdlet that does this already; with an example of what Im trying to do, that would be ideal.

You should be able to do that with something like this
$MailboxName = $args[0]
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
function FolderIdFromPath{
param (
$FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"
)
process{
## Find and Bind to Folder based on Path
#Define the path to search should be seperated with \
#Bind to the MSGFolder Root
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#Split the Search path into an array
$fldArray = $FolderPath.Split("\")
#Loop through the Split Array and do a Search for each level of folder
for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {
#Perform search based on the displayname of each folder level
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])
$findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)
if ($findFolderResults.TotalCount -gt 0){
foreach($folder in $findFolderResults.Folders){
$tfTargetFolder = $folder
}
}
else{
"Error Folder Not Found"
$tfTargetFolder = $null
break
}
}
if($tfTargetFolder -ne $null){
return $tfTargetFolder.Id.UniqueId.ToString()
}
}
}
$TextMessageClass = "IPM.Note.Mobile.SMS"
$MMSMessageClass = "IPM.Note.Mobile.MMS"
$sfItemSearchFilter1 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$TextMessageClass)
$sfItemSearchFilter2 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$MMSMessageClass)
#SearchFilter collection Or
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or);
$sfCollection.Add($sfItemSearchFilter1)
$sfCollection.Add($sfItemSearchFilter2)
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
# Get Folder to Move Item to
$fldId = FolderIdFromPath -FolderPath "\Inbox\SMS"
$SubFolderId = new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId)
$SubFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId)
$Itemids = #()
if($SubFolder -ne $null){
#Define ItemView to retrive just 1000 Items
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$fiItems = $null
do{
$fiItems = $service.FindItems($Inbox.Id,$sfCollection,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
Write-Host ("Moving " + $Item.Subject)
$Itemids += $Item
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
#Total Items Processed Varible
$nmbProcessed = 0
if($Itemids.Count -gt 0){
write-host ("Move " + $Itemids.Count + " Items")
#Create Collection for Move Batch
$type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
$type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.ItemId" -as "Type")
$BatchItemids = [Activator]::CreateInstance($type)
#Varible to Track BatchSize
$batchSize = 0
foreach($iiID in $Itemids){
$nmbProcessed++
$BatchItemids.Add($iiID.Id)
if($iiID.Size -ne $null){
$batchSize += $iiID.Size
}
#if BatchCount greator then 50 or larger the 10 MB Move Batch
if($BatchItemids.Count -eq 50 -bor $batchSize -gt (10*1MB)){
$Result = $null
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$collectionCount = 0
[INT]$Rcount = 0
[INT]$Errcount = 0
if($Result -ne $null){
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
$collectionCount++
}
}
else{
Write-Host -foregroundcolor red ("Move Result Null Exception")
}
Write-host ($Rcount.ToString() + " Items Moved Successfully " + "Total Processed " + $nmbProcessed + " Total Folder Items " + $Itemids.Count)
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
$BatchItemids.Clear()
$batchSize = 0
}
}
if($BatchItemids.Count -gt 0){
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$Rcount = 0
[INT]$Errcount = 0
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
}
Write-host ($Rcount.ToString() + " Items Moved Successfully")
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
}
}
}
Cheers
Glen

Related

How to retrieve all AD object using the USNChanged property and HighestCommittedUsn C#

I'm trying to do some kind of AD sync using USNChanged and retrieve all of the objects using the USNChanged property, but the entire process sounds a bit cryptic to me.
I have a PowerShell code that basically does what I need, but I was wondering you if anyone can help me convert it into a C# code. I would appreciate the help.
PS Code:
$highestCommittedUSN = (Get-ADRootDSE -server $preferredDC -properties * | select-object -property highestCommittedUSN).highestCommittedUSN
$lowUSN = 0
$highUSN = 0
for ($i=1000; $i -le $highestCommittedUSN ; $i=$i+10000 ) {
[system.gc]::Collect()
$error.clear()
$highUSN=$i
write-host "(&(usnChanged>=$lowUSN)(usnChanged<$highUSN))"
$adObjects = (Get-ADObject -LDAPFilter "(&(usnChanged>=$lowUSN)(usnChanged<=$highUSN))" -Server $preferredDC -Properties * -IncludeDeletedObjects)
($adObjects).count
if ($error.count -lt 0) {
for ($j=$lowUSN; $j -le 490767138; $j=$j+1 ) {
">>> " + $j
$adObjects = Get-ADObject -LDAPFilter "(usnChanged=$j)" -Server "DCName" -Properties * -IncludeDeletedObjects
$adObjects | ConvertTo-Json | Out-File -FilePath ff.json -Append
if ($error.count -ge 1) {
"There is problem with this USN " + $j + " on " + $preferredDC | Out-File -FilePath badUSN.txt -Append
$adObjects.distinguishedName
}
$adObjects.distinguishedName
}
}
$lowUSN=$highUSN
$adObjects.distinguishedName
$adObjects = $null
$error.clear()
}
I did found a similar question here : How to read "uSNChanged" property using C# , but I'm not sure if I can use that answer.

Powershell Balloon Notification Vanishes before showing

Have another head scratcher here, simply put my balloon notification does not work when fired from inside of a function that is inside of a job, the icon shows up for a fraction of a second and then disappears, tried several compinations of -wait -sleep -sleep-start to see if i could delay its "completion" since i feel the function is not running it's full course before ending, it works outside of the job/function, but inside it does not, here is the snippet I am calling
function MakeToolTip
{
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory=$true)]
$Text,
[Parameter(Mandatory=$true)]
$Title,
[ValidateSet(‘None’, ‘Info’, ‘Warning’, ‘Error’)]
$Icon = ‘Info’, $Timeout = 10000
)
Add-Type -AssemblyName System.Windows.Forms
if ($script:balloon -eq $null)
{
$script:balloon = New-Object System.Windows.Forms.NotifyIcon
}
$path = Get-Process -id $pid | Select-Object -ExpandProperty Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = $Icon
$balloon.BalloonTipText = $Text
$balloon.BalloonTipTitle = $Title
$balloon.Visible = $true
$balloon.ShowBalloonTip($Timeout)
}
This is called with: MakeToolTip "Testing" "VNC Connections" Info
The full code of the script is here, lines 239-269: https://pastebin.com/7Q4gwLJe
I'm considering running the balloon in a separate job? Maybe the pipeline is completing it too quickly because there too much other stuff going on?
EDIT
Finding that when wrapped in a job a balloon notification does not work, very strange behavior
$MakeToolTip =
{
function Makeit {
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory=$true)]
$Text,
[Parameter(Mandatory=$true)]
$Title,
[ValidateSet(‘None’, ‘Info’, ‘Warning’, ‘Error’)]
$Icon = ‘Info’, $Timeout = 10000
)
Add-Type -AssemblyName System.Windows.Forms
if ($balloon -eq $null)
{
$balloon = New-Object System.Windows.Forms.NotifyIcon
}
$path = Get-Process -id $pid | Select-Object -ExpandProperty Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = $Icon
$balloon.BalloonTipText = $Text
$balloon.BalloonTipTitle = $Title
$balloon.Visible = $true
$balloon.ShowBalloonTip($Timeout)
write-host $Text, $Title
}
}
$balloonjob = Start-Job -ScriptBlock {MakeIt 'Hi' 'There'} -InitializationScript $MakeToolTip -Name "BalloonJob"
Register-ObjectEvent $balloonjob -EventName StateChanged -Action {
if ($eventArgs.JobStateInfo.State -eq [System.Management.Automation.JobState]::Failed)
{
Write-Host "Closing the Balloon"
# This command removes the original job
$sender | Remove-Job -Force
# These commands remove the event registration
$eventSubscriber | Unregister-Event -Force
$eventSubscriber.Action | Remove-Job -Force
}
}

Why is Get-ChildItem command not reading C Drive from C# Program?

$archive = ("*www*","*Backup*","*inetpub*","*wwwroot*","*archive*","*Archive*","*ARCHIVE*","*WINDOWS*","*Program Files*","*JioMediaShare*","*thumbnails*");
function Log-Message {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true, Position=0)]
[string]$LogMessage
)
Write-Output ("{0} - {1}" -f (Get-Date), $LogMessage)
}
function Exclude-Directories {
Process {
$allowThrough = $true
foreach ($directoryToExclude in $archive) {
$directoryText = "*\" + $directoryToExclude
$childText = "*\" + $directoryToExclude + "\*"
if (($_.FullName -like $directoryText -and $_.PsIsContainer) -or $_.FullName -like $childText) {
$allowThrough = $false
break
}
}
if ($allowThrough) {
return $_
}
}
}
Log-Message "Starting Search"
Get-WMIObject Win32_LogicalDisk | ForEach-Object {
Log-Message $_.DeviceID
Get-ChildItem -Path $_.DeviceID -Include *.jpg, *.png, *.bmp, *.jpeg, *.gif, *.webp -Recurse |
Exclude-Directories |
where {!$_.PSIsContainer} |
Select-Object FullName, Name, BaseName, CreationTime, LastWriteTime, Length |
Export-Csv -NoTypeInformation -Path Images.csv -Encoding UTF8 -Append |
% {$_.Replace('"','')}
}
Log-Message "Search completed."
If I run the above script independently, that script is running without errors and search Images in C:, : and E: drive.
string text = System.IO.File.ReadAllText(#"New-Item.ps1");
using (PowerShell PowerShellInstance = PowerShell.Create()) {
// use "AddScript" to add the contents of a script file to the end of the execution pipeline.
// use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
PowerShellInstance.AddScript(text);
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
foreach (PSObject outputItem in PSOutput) {
// if null object was dumped to the pipeline during the script then a null
if (outputItem != null) {
Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
}
}
if (PowerShellInstance.Streams.Error.Count > 0) {
Console.Write("Error");
}
Console.ReadKey();
}
When running the PowerShell script in a C# program, it is skipping search in the C: drive.
I think the issue is with -Path $_.DeviceID. As $_.DeviceID is not a full path, but just something like 'C:', PowerShell is trying to interpret what you mean and I think it is defaulting to the working directory for that drive (e.g. the value of $pwd).
One option to get around this is to run Set-Location "$($_.DeviceID)\" before the enumeration and drop the -Path parameter from the call to Get-ChildItem. That is:
Set-Location "$($_.DeviceID)\"
Get-ChildItem -Include *.jpg, *.png, *.bmp, *.jpeg, *.gif, *.webp -Recurse ...

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

“Cannot Invoke this function because the current host does not implement it” by C# code

I'm developing a Console application that must read from a PS1 file and execute the command from a Form.
My runs very well when I have to call simple function PS1
I have this scrip1 that :
=======================================================================
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
#Function to copy user permissions
Function Copy-UserPermissions($SourceUserID, $TargetUserID, [Microsoft.SharePoint.SPSecurableObject]$Object)
{
#Determine the given Object type and Get URL of it
Switch($Object.GetType().FullName)
{
"Microsoft.SharePoint.SPWeb" { $ObjectType = "Site" ; $ObjectURL = $Object.URL; $web = $Object }
"Microsoft.SharePoint.SPListItem"
{
if($Object.Folder -ne $null)
{
$ObjectType = "Folder" ; $ObjectURL = "$($Object.Web.Url)/$($Object.Url)"; $web = $Object.Web
}
else
{
$ObjectType = "List Item"; $ObjectURL = "$($Object.Web.Url)/$($Object.Url)" ; $web = $Object.Web
}
}
#Microsoft.SharePoint.SPList, Microsoft.SharePoint.SPDocumentLibrary, Microsoft.SharePoint.SPPictureLibrary,etc
default { $ObjectType = "List/Library"; $ObjectURL = "$($Object.ParentWeb.Url)/$($Object.RootFolder.URL)"; $web = $Object.ParentWeb }
}
#Get Source and Target Users
$SourceUser = $Web.EnsureUser($SourceUserID)
$TargetUser = $Web.EnsureUser($TargetUserID)
#Get Permissions of the Source user on given object - Such as: Web, List, Folder, ListItem
$SourcePermissions = $Object.GetUserEffectivePermissionInfo($SourceUser)
#Iterate through each permission and get the details
foreach($SourceRoleAssignment in $SourcePermissions.RoleAssignments)
{
#Get all permission levels assigned to User account directly or via SharePOint Group
$SourceUserPermissions=#()
foreach ($SourceRoleDefinition in $SourceRoleAssignment.RoleDefinitionBindings)
{
#Exclude "Limited Accesses"
if($SourceRoleDefinition.Name -ne "Limited Access")
{
$SourceUserPermissions += $SourceRoleDefinition.Name
}
}
#Check Source Permissions granted directly or through SharePoint Group
if($SourceUserPermissions)
{
if($SourceRoleAssignment.Member -is [Microsoft.SharePoint.SPGroup])
{
$SourcePermissionType = "'Member of SharePoint Group - " + $SourceRoleAssignment.Member.Name +"'"
#Add Target User to the Source User's Group
#Get the Group
$Group = [Microsoft.SharePoint.SPGroup]$SourceRoleAssignment.Member
#Check if user is already member of the group - If not, Add to group
if( ($Group.Users | where {$_.UserLogin -eq $TargetUserID}) -eq $null )
{
#Add User to Group
$Group.AddUser($TargetUser)
#Write-Host Added to Group: $Group.Name
}
}
else
{
$SourcePermissionType = "Direct Permission"
#Add Each Direct permission (such as "Full Control", "Contribute") to Target User
foreach($NewRoleDefinition in $SourceUserPermissions)
{
#Role assignment is a linkage between User object and Role Definition
$NewRoleAssignment = New-Object Microsoft.SharePoint.SPRoleAssignment($TargetUser)
$NewRoleAssignment.RoleDefinitionBindings.Add($web.RoleDefinitions[$NewRoleDefinition])
$object.RoleAssignments.Add($NewRoleAssignment)
$object.Update()
}
}
$SourceUserPermissions = $SourceUserPermissions -join ";"
Write-Host "***$($ObjectType) Permissions Copied: $($SourceUserPermissions) at $($ObjectURL) via $($SourcePermissionType)***"
}
}
}
Function Clone-SPUser($SourceUserID, $TargetUserID, $WebAppURL)
{
###Check Whether the Source Users is a Farm Administrator ###
Write-host "Scanning Farm Administrators Group..."
#Get the SharePoint Central Administration site
$AdminWebApp = Get-SPwebapplication -includecentraladministration | where {$_.IsAdministrationWebApplication}
$AdminSite = Get-SPWeb $AdminWebApp.Url
$AdminGroupName = $AdminSite.AssociatedOwnerGroup
$FarmAdminGroup = $AdminSite.SiteGroups[$AdminGroupName]
#enumerate in farm adminidtrators groups
foreach ($user in $FarmAdminGroup.users)
{
if($User.LoginName.Endswith($SourceUserID,1)) #1 to Ignore Case
{
#Add the target user to Farm Administrator Group
$FarmAdminGroup.AddUser($TargetUserID,"",$TargetUserID , "")
Write-Host "***Added to Farm Administrators Group!***"
}
}
### Check Web Application User Policies ###
Write-host "Scanning Web Application Policies..."
$WebApp = Get-SPWebApplication $WebAppURL
foreach ($Policy in $WebApp.Policies)
{
#Check if the search users is member of the group
if($Policy.UserName.EndsWith($SourceUserID,1))
{
#Write-Host $Policy.UserName
$PolicyRoles=#()
foreach($Role in $Policy.PolicyRoleBindings)
{
$PolicyRoles+= $Role
}
}
}
#Add Each Policy found
if($PolicyRoles)
{
$WebAppPolicy = $WebApp.Policies.Add($TargetUserID, $TargetUserID)
foreach($Policy in $PolicyRoles)
{
$WebAppPolicy.PolicyRoleBindings.Add($Policy)
}
$WebApp.Update()
Write-host "***Added to Web application Policies!***"
}
### Drill down to Site Collections, Webs, Lists & Libraries, Folders and List items ###
#Get all Site collections of given web app
$SiteCollections = Get-SPSite -WebApplication $WebAppURL -Limit All
#Convert UserID Into Claims format - If WebApp is claims based! Domain\User to i:0#.w|Domain\User
if( (Get-SPWebApplication $WebAppURL).UseClaimsAuthentication)
{
$SourceUserID = (New-SPClaimsPrincipal -identity $SourceUserID -identitytype 1).ToEncodedString()
$TargetUserID = (New-SPClaimsPrincipal -identity $TargetUserID -identitytype 1).ToEncodedString()
}
#Loop through all site collections
foreach($Site in $SiteCollections)
{
#Prepare the Target user
$TargetUser = $Site.RootWeb.EnsureUser($TargetUserID)
Write-host "Scanning Site Collection Administrators Group for:" $site.Url
###Check Whether the User is a Site Collection Administrator
foreach($SiteCollAdmin in $Site.RootWeb.SiteAdministrators)
{
if($SiteCollAdmin.LoginName.EndsWith($SourceUserID,1))
{
#Make the user as Site collection Admin
$TargetUser.IsSiteAdmin = $true
$TargetUser.Update()
Write-host "***Added to Site Collection Admin Group***"
}
}
#Get all webs
$WebsCollection = $Site.AllWebs
#Loop throuh each Site (web)
foreach($Web in $WebsCollection)
{
if($Web.HasUniqueRoleAssignments -eq $True)
{
Write-host "Scanning Site:" $Web.Url
#Call the function to Copy Permissions to TargetUser
Copy-UserPermissions $SourceUserID $TargetUserID $Web
}
#Check Lists with Unique Permissions
Write-host "Scanning Lists on $($web.url)..."
foreach($List in $web.Lists)
{
if($List.HasUniqueRoleAssignments -eq $True -and ($List.Hidden -eq $false))
{
#Call the function to Copy Permissions to TargetUser
Copy-UserPermissions $SourceUserID $TargetUserID $List
}
#Check Folders with Unique Permissions
$UniqueFolders = $List.Folders | where { $_.HasUniqueRoleAssignments -eq $True }
#Get Folder permissions
foreach($folder in $UniqueFolders)
{
#Call the function to Copy Permissions to TargetUser
Copy-UserPermissions $SourceUserID $TargetUserID $folder
}
#Check List Items with Unique Permissions
$UniqueItems = $List.Items | where { $_.HasUniqueRoleAssignments -eq $True }
#Get Item level permissions
foreach($item in $UniqueItems)
{
#Call the function to Copy Permissions to TargetUser
Copy-UserPermissions $SourceUserID $TargetUserID $Item
}
}
}
}
Write-Host "Permission are copied successfully!"
}
#Define variables for processing
$WebAppURL = "http://sp2010devid/sites/EsercioWeekend/"
#Provide input for source and Target user Ids
$SourceUser ="virtualsp\admnistrator"
$TargetUser ="virtualsp\b.ferreirarocha"
#Call the function to clone user access rights
Clone-SPUser $SourceUser $TargetUser $WebAppURL
==========================================================================
And the most important part of my C# Code that runs the script:
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace HowToRunPowerShell
{
public partial class FormPowerShellSample : Form
{
// Represents the kind of drag drop formats we want to receive
private const string dragDropFormat = "FileDrop";
public FormPowerShellSample()
{
InitializeComponent();
}
private void buttonRunScript_Click(object sender, EventArgs e)
{
try
{
textBoxOutput.Clear();
textBoxOutput.Text = RunScript(textBoxScript.Text);
}
catch (Exception error)
{
textBoxOutput.Text += String.Format("\r\nError in script : {0}\r\n", error.Message);
}
}
/// <summary>
/// Runs the given powershell script and returns the script output.
/// </summary>
/// <param name="scriptText">the powershell script text to run</param>
/// <returns>output of the script</returns>
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
PSSnapInException snapInError;
runspace.RunspaceConfiguration.AddPSSnapIn("Microsoft.SharePoint.PowerShell", out snapInError);
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script output objects into nicely formatted strings
// remove this line to get the actual objects that the script returns. For example, the script
// "Get-Process" returns a collection of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<PSObject> results = pipeline.Invoke();
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
#region Drag-drop handling events
private void FormPowerShellSample_DragDrop(object sender, DragEventArgs e)
{
// is it the correct type of data?
if (e.Data.GetDataPresent(dragDropFormat))
{
// dragging files onto the window yields an array of pathnames
string[] files = (string[])e.Data.GetData(dragDropFormat);
if (files.Length > 0)
{
// just read the first file
using (StreamReader sr = new StreamReader(files[0]))
{
// and plunk the contents in the textbox
textBoxScript.Text = sr.ReadToEnd();
}
}
}
}
private void FormPowerShellSample_DragEnter(object sender, DragEventArgs e)
{
// only accept the dropped data if it has the correct format
e.Effect = e.Data.GetDataPresent(dragDropFormat) ? DragDropEffects.Link : DragDropEffects.None;
}
#endregion
}
}
but when I try to run the script it return me the the following exception :
“Cannot Invoke this function because the current host does not
implement it”
I found this post link and it Seems it Someone has found the same isseus and solve it with this procedure but i'm completely ignorant on PowerSehll.
To run arbitrary PowerShell scripts from .net code, you need to implement a host.
Here are my blog posts on the subject: http://powershellstation.com/category/writing-a-host/
Since you have the script in question, you can simply eliminate the parts of it which require a host. You have most of the write-host calls commented out. Try commenting out the rest.
Other things that cause problems are prompting for input and using -confirm.
private static void Main(string[] args)
{
// Display the welcome message.
Console.Title = "PowerShell Console Host Sample Application";
ConsoleColor oldFg = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" Windows PowerShell Console Host Application Sample");
Console.WriteLine(" ==================================================");
Console.WriteLine(string.Empty);
Console.WriteLine("This is an example of a simple interactive console host uses ");
Console.WriteLine("the Windows PowerShell engine to interpret commands.");
Console.WriteLine("Type 'exit' to exit.");
Console.WriteLine(string.Empty);
Console.ForegroundColor = oldFg;
// Create the listener and run it. This method never returns.
PSListenerConsoleSample listener = new PSListenerConsoleSample();
listener.Run();
}
/// <summary>
/// Initializes a new instance of the PSListenerConsoleSample class.
/// </summary>
public PSListenerConsoleSample()
{
// Create the host and runspace instances for this interpreter.
// Note that this application does not support console files so
// only the default snap-ins will be available.
this.myHost = new MyHost(this);
this.myRunSpace = RunspaceFactory.CreateRunspace(this.myHost);
this.myRunSpace.Open();
// Create a PowerShell object to run the commands used to create
// $profile and load the profiles.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
try
{
this.currentPowerShell.AddScript("Add-PSSnapin Microsoft.Sharepoint.Powershell");
this.currentPowerShell.AddScript(#"C:\Users\Administrator\Desktop\Untitled1.ps1");
this.currentPowerShell.Runspace = this.myRunSpace;
PSCommand[] profileCommands = Microsoft.Samples.PowerShell.Host.HostUtilities.GetProfileCommands("SampleHost06");
foreach (PSCommand command in profileCommands)
{
this.currentPowerShell.Commands = command;
this.currentPowerShell.Invoke();
}
}
finally
{
// Dispose the PowerShell object and set currentPowerShell
// to null. It is locked because currentPowerShell may be
// accessed by the ctrl-C handler.
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
/// <summary>
POWERSHELL PS1
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
function Get-SPUserEffectivePermissions(
[object[]]$users,
[Microsoft.SharePoint.SPSecurableObject]$InputObject) {
begin { }
process {
$so = $InputObject
if ($so -eq $null) { $so = $_ }
if ($so -isnot [Microsoft.SharePoint.SPSecurableObject]) {
throw "A valid SPWeb, SPList, or SPListItem must be provided."
}
foreach ($user in $users) {
# Set the users login name
$loginName = $user
if ($user -is [Microsoft.SharePoint.SPUser] -or $user -is [PSCustomObject]) {
$loginName = $user.LoginName
}
if ($loginName -eq $null) {
throw "The provided user is null or empty. Specify a valid SPUser object or login name."
}
# Get the users permission details.
$permInfo = $so.GetUserEffectivePermissionInfo($loginName)
# Determine the URL to the securable object being evaluated
$resource = $null
if ($so -is [Microsoft.SharePoint.SPWeb]) {
$resource = $so.Url
} elseif ($so -is [Microsoft.SharePoint.SPList]) {
$resource = $so.ParentWeb.Site.MakeFullUrl($so.RootFolder.ServerRelativeUrl)
} elseif ($so -is [Microsoft.SharePoint.SPListItem]) {
$resource = $so.ParentList.ParentWeb.Site.MakeFullUrl($so.Url)
}
# Get the role assignments and iterate through them
$roleAssignments = $permInfo.RoleAssignments
if ($roleAssignments.Count -gt 0) {
foreach ($roleAssignment in $roleAssignments) {
$member = $roleAssignment.Member
# Build a string array of all the permission level names
$permName = #()
foreach ($definition in $roleAssignment.RoleDefinitionBindings) {
$permName += $definition.Name
}
# Determine how the users permissions were assigned
$assignment = "Direct Assignment"
if ($member -is [Microsoft.SharePoint.SPGroup]) {
$assignment = $member.Name
} else {
if ($member.IsDomainGroup -and ($member.LoginName -ne $loginName)) {
$assignment = $member.LoginName
}
}
# Create a hash table with all the data
$hash = #{
Resource = $resource
"Resource Type" = $so.GetType().Name
User = $loginName
Permission = $permName -join ", "
"Granted By" = $assignment
}
# Convert the hash to an object and output to the pipeline
New-Object PSObject -Property $hash
}
}
}
}
end {}
}
Get-SPSite -Limit All | Get-SPWeb | Get-SPUserEffectivePermissions "virtualsp\administrator" | Export-Csv -NoTypeInformation -Path c:\perms.csv

Categories