Running Powershell script with if condition from a .NET app - vb.net

I have a .net application that successfully runs Powershell commands that it pulls from text files- until I tried doing one that is more complicated and contains an if condition. The script works correctly from a PS console but in .NET I only know how to pass in a string for the script, which after reading the file, it adds extra stuff like vblf and even if I take it out, it won't work. Is this even possible?
.NET Runtime Error: Server was unable to process request. ---> The
term 'False' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or
if a path was included, verify that the path is correct and try again.
.NET code:
'Grab Powershell script from text (.ps1) file
strScript = File.ReadAllText(ScriptFileName)
'inject the arguments into the script
strScript = InsertArguments(strScript, Arguments)
'Open the runspace and create a pipeline if it's not already open
If psRunspace.RunspaceStateInfo.State = RunspaceState.BeforeOpen Then
psRunspace.Open()
End If
Dim MyPipeline As Pipeline = psRunspace.CreatePipeline()
MyPipeline.Commands.AddScript(strScript)
Dim psResults As Collection(Of PSObject) = MyPipeline.Invoke()
Powershell Script, stored in ps1 file:
new-mailbox -name $argument1 -DisplayName $argument1 -UserPrincipalName $argument2 Room -DomainController $argument5
if ($argument4 -eq "False") {
Set-CalendarProcessing $argument1 -BookingWindowInDays 400 -DeleteSubject $false -AutomateProcessing autoaccept -AllBookInPolicy $false -BookInPolicy $argument3 -DomainController $argument5
} else {
Set-CalendarProcessing $argument1 -BookingWindowInDays 400 -DeleteSubject $false -AutomateProcessing autoaccept -AllBookInPolicy $true -DomainController $argument5
}
When this script is read in, here is a substring of what gets pulled into strScript:
-Room -DomainController mcexdct1" & vbLf & "if (False -eq "False") {"

Fixed it by changing the PowerShell script from this:
if ($argument4 -eq "False") {
to this:
if ("$argument4" -eq "False") {
It works with quotation marks around it. I guess the "junk" I was seeing in the script string (like vblf) is normal- I don't work with reading from text files that much.

Related

DSC Script Resource - executes .exe, but doesn't wait until completion

Question)
How do I get a DSC script resource to wait until the code has completed before moving on?
(The code is invoke-expression "path\file.exe")
Details)
I am using powershell version 5
and am trying to get DSC setup to handle our sql server installations.
My manager has asked me to use the out of the box DSC components.
i.e. no downloading of custom modules which may help.
I have built up the config file that handles the base server build - everything is good.
The script resource that installs sql server is good.
It executes, and waits until it has installed completely, before moving on.
When I get up to the script resource that installs the sql server cumulative update, I have issues.
The executable gets called and it starts installing (it should take 10-15 minutes), but the dsc configuration doesn't wait until it has installed, and moves on after a second.
This means that the DependsOn for future steps, gets called, before the installation is complete.
How can I make the script resource wait until it has finished?
Have you tried the keyword "DependsOn" like that ?
Script MyNewSvc
{
GetScript = {
$SvcName = 'MyNewSvc'
$Results = #{}
$Results['svc'] = Get-Service $SvcName
$Results
}
SetScript = {
$SvcName = 'MyNewSvc'
setup.exe /param
while((Get-Service $SvcName).Status -ne "Running"){ Start-Sleep 10 }
}
TestScript = {
$SvcName = 'MyNewSvc'
$SvcLog = 'c:\svc.log'
If (condition) { #like a a running svc or a log file
$True
}
Else {
$False
}
}
}
WindowsFeature Feature
{
Name = "Web-Server"
Ensure = "Present"
DependsOn = "[Script]MyNewSvc"
}
Invoke-Expression doesn't seem to wait until the process has finished - try this in a generic PowerShell console and you'll see the command returns before you close notepad:
Invoke-Expression -Command "notepad.exe";
You can use Start-Process instead:
Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow;
And if you want to check the exit code you can do this:
$process = Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow -PassThru;
$exitcode = $process.ExitCode;
if( $exitcode -ne 0 )
{
# handle errors here
}
Finally, to use command line arguments:
$process = Start-Process -FilePath "setup.exe" -ArgumentList #("/param1", "/param2") -Wait -PassThru;
$exitcode = $process.ExitCode;

StreamWriter cannot call a method on a null-valued expression

First time user, looking for help with a script that's been driving me crazy.
Basically, I need to create a set number of files of an exact size (512KB, 2MB, 1GB) to test a SAN. These files need to be filled with random text so that the SAN doesn't catch the nuls and does actually allocate the blocks - that's also the reason I couldn't just use fsutils.
Now, I've been messing with the new-bigrandomfile by Verboon and tweaking it to my needs.
However I'm getting the error:
You cannot call a method on a null-valued expression.
At L:\random5.ps1:34 char:9
+ $stream.Write($longstring)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
This is the bit of code I've come up with so far; I'll add a loop at the end to copy the file I just created N times so to fill up the lun.
Set-Strictmode -Version 2.0
#temp file
$file = "c:\temp\temp.rnd"
#charset size
$charset = 64
#Block Size
$blocksize = 512
#page size
$Pagesize = 512KB
#Number of blocks in a page
$blocknum = $Pagesize / $blocksize
#Resulting/desired test file size
$filesize = 1GB
#number of pages in a file
$pagenum = $filesize / $Pagesize
# create the stream writer
$stream = System.IO.StreamWriter $file
# get a 64 element Char[]; I added the - and _ to have 64 chars
[char[]]$chars = 'azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQSDFGHJKLMWXCVBN0123456789-_'
1..$Pagenum | ForEach-Object {
# get a page's worth of blocks
1..$blocknum| ForEach-Object {
# randomize all chars and...
$rndChars = $chars | Get-Random -Count $chars.Count
# ...join them in a string
$string = -join $rndChars
# repeat random string N times to get a full block string length
$longstring = $string * ($blocksize / $charset)
# write 1 block to file
$stream.Write($longstring)
# release resources by clearing string variables
Clear-Variable string, longstring
}
}
$stream.Close()
$stream.Dispose()
# release resources through garbage collection
[GC]::Collect()
$file.Close()
I've tried a gazillion variants like:
$stream = [System.IO.StreamWriter] $file
$stream = System.IO.StreamWriter $file
$stream = NewObject System.IO.StreamWriter $file
Of course, being a total noob at powershell, I've tried using quotes, brackets, provided the full path instead of the variable, etc. All (or most) seem to be valid syntax variants, according to a ton of examples I found online, but the output is still the same.
In case you have any improvement to suggest or alternative way to perform this task I'm all ears.
Edited the script above: just a couple of " for $file made the error disappear, - thanks LinuxDisciple; however, the file gets created but stays at 0 bytes and the script stuck in a loop.
Fix your instantiation of StreamWriter to any of these correct variants:
$stream = [System.IO.StreamWriter]::new($file)
$stream = [IO.StreamWriter]::new($file) # the default namespace may be omitted
$stream = New-Object System.IO.StreamWriter $file
You can specify encoding:
$stream = [IO.StreamWriter]::new(
$file,
$false, # don't append
[Text.Encoding]::ASCII
)
See StreamWriter on MSDN for available constructors and parameters.
PowerShell ISE offers autocomplete with tooltips:
type [streamw and press Ctrl-Space to autocomplete the full .NET class name
type ]:: to see the available methods and properties
type new and press Ctrl-Space to see the constructor overrides
whenever needed, put the caret at the method name and press Ctrl-Space for the tooltip
I know nothing about powershell but a few things:
Are you sure $longstring has a value before you call stream.Write()? It sounds like it's null and that's why the error. If you can somehow output the value of $longstring to the console, it would help you make sure that it has a value.
Also, troubleshoot the code with a simplified version of your code, so that you can pinpoint what's going on, for example
$file = c:\temp\temp.rnd
$stream = System.IO.StreamWriter $file
$longstring = 'whatever'
$stream.Write($longstring)

Powershell error during making copy of outlook folder

I try to make copy of all folders from user account into pst file. Script works fine for all folders except Inbox. When I try to copy data from inbox at end of copying I have error
Exception calling "CopyTo" with "1" argument(s): "Cannot move or copy folders. Cannot copy folder. A top-level folder cannot be copied to one of its subfolders. Or, you may not have appropriate permissions for the folder. To check your permissions for the folder, right-click the folder, and then click Properties on the shortcut menu."
Script below
$start = Get-Date
#checking if outlook is running
$isRunning = (#(Get-Process -ea silentlycontinue OUTLOOK).count -gt 0)
#showing dialog to choose direcotry where pst should be saved
$app = new-object -com Shell.Application
$folder = $app.BrowseForFolder(0, "Select Folder", 0, "C:\")
if ($folder.Self.Path -ne "") {
$path = $folder.Self.Path+"\"+$env:USERNAME+".pst"
} else {
Write-Host "directory not selected. Exiting.."
[Environment]::Exit(1)
}
#reference to outlook
Add-Type -Assembly "Microsoft.Office.Interop.Outlook" | out-null
$outlook = New-Object -ComObject outlook.application
$ns = $outlook.GetNamespace("MAPI")
#adding personal archive file
$ns.AddStoreEx($path, 1)
Write-Host created $path
$dst = $ns.Folders.GetLast()
Write-Host folder name: $dst.Name
#list of directory types to copy. more details https://msdn.microsoft.com/en-us/library/office/ff861868.aspx
$folderTypes = 9,10,3,16,6,12,4,5,30,13,28
#iterating trough list of directories and for each dir make copy in pst file. next counting of objects in archive and pst
foreach ($id in $folderTypes) {
$src = $ns.GetDefaultFolder($id)
$tmp = $src.copyTo($dst)
Write-Host copied to $tmp.name
}
#deattaching personal store from outlook
$ns.GetType().InvokeMember('RemoveStore',[System.Reflection.BindingFlags]::InvokeMethod, $null, $ns, ($dst))
Write-Host $dst.name closed
#if outlook wasn't running on start we must close it
if (-not $isRunning) {
Write-Host closing outlook
$outlook.Quit()
}
$end = Get-Date
$diff = New-TimeSpan -start $start -end $end
Write-Host done
"time elapsed {0:g}" -f $diff
Any ideas why this error occurs?
Thanks in advace
Make sure that you don't have the DisableCrossAccountCopy key in the windows registry set. Here is what the CopyTo method description states:
Setting the REG_MULTI_SZ value, DisableCrossAccountCopy, in HKCU\Software\Microsoft\Office\14.0\Outlook in the Windows registry has the side effect of disabling this method.
Also I'd suggest releasing underlying COM objects in the code instantly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. This is particularly important if your add-in attempts to enumerate more than 256 Outlook items in a collection that is stored on a Microsoft Exchange Server. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. Read more about that in the Systematically Releasing Objects article.

Upload a file to Sharepoint 2010 with powershell 2.0

I'm struggling since a couple of days to upload files to Sharepoint 2010 with powershell.
I'm on a win7 machine with powershell v2 trying to upload to a SP 2010 site.
I'm having 2 major issues
$Context.web value is always empty even after Executequery() and no
error is shown. My $Context variable gets the server version (14.x.x.x.x) but nothing more
$Context.Load($variable) which always returns the error Cannot find an overload for "Load" and the argument count: "1".
I copied Sharepoint DLLs to my Win7 machine and I import the reference to my script.
The below script is a mix of many parts I took from the net.
I'v already tried unsuccessfully to add an overload on the clientcontext defining Load method without Type parameter suggested in the following post
http://soerennielsen.wordpress.com/2013/08/25/use-csom-from-powershell/
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "https://Root-of-my-site"
$listname = "My-folder"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$Folder = "C:\temp\Certificates"
$List = $Context.Web.Lists.GetByTitle($listname)
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = get-content -encoding byte -path $File.Fullname
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$Context.Load($Upload)
$Context.ExecuteQuery()
}
The error is
Cannot find an overload for "Load" and the argument count: "1".
At C:\temp\uploadCertToSharepoint.ps1:48 char:14
+ $Context.Load <<<< ($Upload)
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Can someone please help me sorting this issue?
I'll need to upload around 400 files with ad-hoc fields to a sharepoint site in a couple of weeks and at the moment I'm completely stuck. Running the script server side is unfortunately not possible.
Thanks,
Marco
This error occurs since ClientRuntimeContext.Load is a Generics Method:
public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject
and Generics methods are not supported natively in PowerShell (V1, V2) AFAIK.
The workaround is to invoke a generic methods using MethodInfo.MakeGenericMethod method as described in article Invoking Generic Methods on Non-Generic Classes in PowerShell
In case of ClientRuntimeContext.Load method, the following PS function could be used:
Function Invoke-LoadMethod() {
param(
$clientObjectInstance = $(throw “Please provide an Client Object instance on which to invoke the generic method”)
)
$ctx = $clientObjectInstance.Context
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$type = $clientObjectInstance.GetType()
$clientObjectLoad = $load.MakeGenericMethod($type)
$clientObjectLoad.Invoke($ctx,#($clientObjectInstance,$null))
}
Then, in your example the line:
$Context.Load($Upload)
could be replaced with this one:
Invoke-LoadMethod -clientObjectInstance $Upload
References
Invoking Generic Methods on Non-Generic Classes in PowerShell
Some tips and tricks of using SharePoint Client Object Model in
PowerShell. Part 1
It throws the error because in powershell 2.0 you cannot call generic method directly.
You need to create closed method using MakeGenericMethod. Try to use code below.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "http://server"
$listname = "listName"
$Folder = "C:\PS\Test"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$method = $Context.GetType().GetMethod("Load")
$closedMethod = $method.MakeGenericMethod([Microsoft.SharePoint.Client.File])
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = (get-content -encoding byte -path $File.Fullname)
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$closedMethod.Invoke($Context, #($Upload, $null) )
$Context.ExecuteQuery()
}

System.DirectoryServices.DirectorySearcher works if called from PowerShell but not if called from cmd.exe

I wrote an script for PowerShell 1.0 (now using 2.0) that executes a search on my Active Directory. The code is the following:
$filter = "some filter"
$rootEntry = New-Object System.DirectoryServices.DirectoryEntry
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $rootEntry
$searcher.Filter = $filter
$searcher.SearchScope = "Subtree"
$colResults = $searcher.FindAll()
After calling FindAll() method of the DirectorySearcher instance, I print the results to see what I got.
The thing is, if I start PowerShell.exe and call the script on the prompt I'm able to see results. But if I try to call it using cmd.exe using the same filter I don't see any results. FindAll() returns an empty result set.
I'm running this on a Windows 2003 Server. It did not came with PowerShell 1.0 so I downloaded it and installed it on the server. It does have .Net Framework 2.0.
Any suggestions?
Thanks a lot.
By defaul your $rootEntry point on the root of you local AD i you are running on a server, and this with the credetial of the current process. you don't show what is your filter and how you use your result.
Here is a small sample of an ADSI search from PowerShell
Clear-Host
# ADSI Bind with current process credentials
#$dn = [adsi] "LDAP://192.168.30.200:389/dc=dom,dc=fr"
# ADSI Bind with specific credentials
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.183.138:389/dc=societe,dc=fr","administrateur#societe.fr","test.2011")
# Look for users
$Rech = new-object System.DirectoryServices.DirectorySearcher($dn)
$rc = $Rech.filter = "((objectCategory=person))"
$rc = $Rech.SearchScope = "subtree"
$rc = $Rech.PropertiesToLoad.Add("distinguishedName");
$rc = $Rech.PropertiesToLoad.Add("sAMAccountName");
$rc = $Rech.PropertiesToLoad.Add("ipphone");
$rc = $Rech.PropertiesToLoad.Add("telephoneNumber");
$rc = $Rech.PropertiesToLoad.Add("memberOf");
$rc = $Rech.PropertiesToLoad.Add("distinguishedname");
$rc = $Rech.PropertiesToLoad.Add("physicalDeliveryOfficeName"); # Your attribute
$liste = $Rech.findall()
Finally got it working by doing two things:
Upgrade to PowerShell 2.0.
Run with -File option.
So the command was run like this:
>>powershell -file ./script.ps1 "dn" "uid"
I'm not sure what the difference between the -File and -Command options are (does anyone?) but it worked.
Thanks.