Powershell Version 2 Load DLL for use on persistent connection's invoke command scriptblock - dll

Does anyone know how I can load a DLL without having it on each remote server I am using in a persistent connection and running the invoke-command cmdlet with?
I am using DotNetZip to backup folders on about 13 servers. Everything is working locally, but when it gets to a remote server (the first one in the array is the local server), it errors because it doesn't see the DLL on the remote server.
I execute this script on one server and it should zip up folders on each remote server:
foreach($i in $appServers) {
$sessionForI = New-PSSession -computername $i
Invoke-Command -Session $sessionForI -ScriptBlock {
if (!(Test-Path -path C:\\newDeploy)) {
New-Item C:\\newDeploy -type directory
}
[System.Reflection.Assembly]::LoadFrom("C:\\newDeploy\\Ionic.Zip.dll");
$directoryToZip = "C:\\Program Files (x86)\\SubDir\\$folder"
$zipfile = new-object Ionic.Zip.ZipFile
$e = $zipfile.AddSelectedFiles("name != '*.e2e'",$directoryToZip, "",1)
if (!(Test-Path -path C:\\newDeploy\\backup)) {
New-Item C:\\newDeploy\\backup -type directory
}
$zipfile.Save("C:\\newDeploy\\backup\\" + $folder+ ".zip")
$zipfile.Dispose()
}
remove-PSSession -session $sessionForI
}
Thank you .
-Jim

I'm pretty sure you are going to need to copy Ionic.Zip.dll to the remote machines to do this. You could try sharing it out from your lead system and using a UNC path to load it from the remote machines (i've never tried that... going to now...) :-)
Update - yep just confirmed you can pass a UNC path to [System.Reflection.Assembly]::LoadFrom.
Update 2 - While the assembly loaded, using it didn't work so well:
Exception calling "AddFile" with "1" argument(s): "Request for the permission of type 'System.Security.Permissions.File
IOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed."
At line:1 char:11
+ $z.AddFile <<<< ("C:\AMCleanUp.log")
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
When I loaded a local copy of the the DLL the AddFile method worked fine. You're only option might be to copy this DLL to all your servers...

You can use a UNC path in the LoadFrom for the remote boxes, but I see that someone has had problem doing the same with DotNetZip:
http://social.technet.microsoft.com/Forums/en-US/winserverpowershell/thread/dd5dcae2-1ccc-4be2-b986-61c069102ffb/

I think your problems with accessing remote resources in an already remote session has to do with double-hop authentication. Check this link http://www.ravichaganti.com/blog/?p=1230

Related

Microsoft.Azure.IpSecurityRestriction not found in Azure PowerShell script

I am trying to run some PowerShell commands and my script is failing on the following line:
$ipsr = New-Object Microsoft.Azure.IpSecurityRestriction
The error is:
Cannot find type [Microsoft.Azure.IpSecurityRestriction]: verify that the assembly containing this type is loaded
I am trying to run this “inline” in an Azure PowerShell task as part of my deployment pipeline. Is this supported or do I need to first import an assembly?
I can reproduce your issue. First, it should be Microsoft.Azure.Management.WebSites.Models.IpSecurityRestriction, not Microsoft.Azure.IpSecurityRestriction, then make sure you have installed the Az.Websites powershell module, just use the command below.
Import-Module -Name Az.Websites
New-Object Microsoft.Azure.Management.WebSites.Models.IpSecurityRestriction
Besides, actually we import the module just for the Microsoft.Azure.Management.Websites.dll, so you can also use the command as below, check the path of your .dll file.
Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\Az.Websites\1.1.0\Microsoft.Azure.Management.Websites.dll'
New-Object Microsoft.Azure.Management.WebSites.Models.IpSecurityRestriction

Cannot install Azure/AzureRM PowerShell modules on Worker roles

I have a Worker roles on Azure Cloud service (classic) and I want them to install Azure and AzureRM PowerShell modules on startup. I've added a startup task in my ServiceDefinition.csdef file:
<Startup>
<Task commandLine="InstallAzureModules.cmd" executionContext="elevated" taskType="simple" />
</Startup>
InstallAzureModules.cmd file looks like this:
PowerShell.exe -ExecutionPolicy Unrestricted .\InstallAzureModules.ps1 >> "D:\InstallAzureModulesLogs.txt" 2>&1
And InstallAzureModules.ps1 looks like this:
Install-PackageProvider NuGet -Force
Install-Module Azure -AllowClobber -Force
Install-Module AzureRM -AllowClobber -Force
In result I have an error There is not enough space on the disk, however when I connect to any Worker instance using Remote Desktop and run InstallAzureModules.cmd manually all modules are installed without any errors.
Please help to have these modules installed.
Thanks.
Finally after contacting Microsoft Support the issue was resolved!
According to Support Professional who was working with my request this error is due to a redirection of application temporary folder. I was recommended to go through this link for a possible fix, but it didn't work.
Also updates to my PS script were provided and what actually helped was setting TMP and TEMP environment variables to a folder on drive C. So here is my final script:
$env_TMP = $env:TMP
$env_TEMP = $env:TEMP
$env:TMP = "C:\_trashable\Modules"
$env:TEMP = "C:\_trashable\Modules"
Install-PackageProvider NuGet -Force
Install-Module Azure -AllowClobber -Force
$env:TMP = $env_TMP
$env:TEMP = $env_TEMP
ECHO "Finished"

PowerShell version differences with variables

I am running a script in Windows Server 2008 that has PowerShell V2 that collects the AppPool names from IIS and then deletes all of them and then deletes all of the websites and contents of those websites. No issue. Works beautifully. Now, run that same script on Windows Server 2012 with PowerShell V4 with the same pools and websites and doesn't delete everything because of spaces in the names of AppPools. Why is this only an issue in PS V4.
This is what it looks like
Also it doesn't matter in v4 whether I double quote the $site variable because it still throws the same error. Again this is just fine in v2 and it runs WAY faster. The exact same pools, sites, content on 2008 servers can get deleted in about 20 seconds, and on 2012 it takes several minutes and then throws errors like this and skips some stuff.
....
$msdeploy=Get-Command 'C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe'
foreach ($site in $list.WebAppName) {
write-host $site
& $msdeploy -verb:delete -dest:appPoolConfig=$site –skip:objectname=rootwebconfig32 –skip:objectname=httpCert –skip:objectname=machineconfig32
}
The output is this as an example
2.0 DefaultAppPool
msdeploy.exe : Error: Unrecognized argument '"-dest:"appPoolConfig=2.0'. All arguments must begin with "-".
At line:19 char:5
+ & $msdeploy -verb:delete -dest:appPoolConfig=$site –skip:objectname=rootwe ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Error: Unrecogn...begin with "-".:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Error count: 1.
You are doing it wrongly. From MSDN documentation of Web Deploy Command Line Syntax an excerpt
With a minor modification to its usual syntax, Web Deploy commands can
be run from a Windows PowerShell prompt. To do this, change the colon
character (:) after the verb, source, and dest arguments of the Web
Deploy command to an equal sign (=). In the following example, compare
the Web Deploy command with its PowerShell version.
Web Deploy command: msdeploy -verb:sync -source:metakey=/lm/w3svc/1
-dest:metakey=/lm/w3svc/2 -verbose
PowerShell command: .\msdeploy.exe -verb=sync
-source=metakey=/lm/w3svc/1 -dest=metakey=/lm/w3svc/2 -verbose
Your command syntax should be
$msdeploy -verb=delete -dest=appPoolConfig=$site.trim() –skip=objectname=rootwebconfig32
–skip=objectname=httpCert –skip=objectname=machineconfig32
I would try encapsulating your param value in quotes and the statement in braces, like below:
& { $msdeploy -verb=delete -dest=appPoolConfig="$site" –skip=objectname=rootwebconfig32 –skip=objectname=httpCert –skip=objectname=machineconfig32 }

Using CurrentDomain.SetData("APP_CONFIG_FILE") doesn't work in PowerShell ISE

I'm attempting to use a .NET 4.0 assembly in PowerShell ISE, and trying to change the config file which is used via:
[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $PathToConfig);
[Configuration.ConfigurationManager]::ConnectionStrings.Count always returns "1",and "[Configuration.ConfigurationManager]::ConnectionStrings[0].Name" always returns "LocalSqlServer", and that ConnectionString name is not in my ".config" file.
Note that executing the PowerShell script from a PowerShell command prompt functions as expected. It's just when I execute it from within PowerShell ISE, it doesn't work as expected.
It's because the path to app.config for PowerShell ISE has already been loaded and cached so changing the app.config path afterwards won't make a difference:
stackoverflow.com/q/6150644/222748
Here is an example script that will clear the cached path so it will work under PowerShell ISE:
[System.AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", $PathToConfig)
Add-Type -AssemblyName System.Configuration
[Configuration.ConfigurationManager].GetField("s_initState", "NonPublic, Static").SetValue($null, 0)
[Configuration.ConfigurationManager].GetField("s_configSystem", "NonPublic, Static").SetValue($null, $null)
([Configuration.ConfigurationManager].Assembly.GetTypes() | where {$_.FullName -eq "System.Configuration.ClientConfigPaths"})[0].GetField("s_current", "NonPublic, Static").SetValue($null, $null)
[Configuration.ConfigurationManager]::ConnectionStrings[0].Name
Taking off [0] works for me.
([Configuration.ConfigurationManager].Assembly.GetTypes() | where {$_.FullName -eq "System.Configuration.ClientConfigPaths"}).GetField("s_current", "NonPublic, Static").SetValue($null, $null)

Can you remove an Add-ed Type in PowerShell again?

I'm currently writing a library in C# and was using PowerShell to quickly test it on some occasions. However, this prevents me from re-building the project as PowerShell obviously still has the DLL open.
Is there a way of unloading the DLL again after adding it with Add-Type? The documentation doesn't seem to have clues on that and the obvious candidate would be Remove-Type (which doesn't exist – there is only one command anyway with Type as its noun). It gets cumbersome to close PowerShell and do all the stuff of navigating to the build directory and adding the type again each time I want to rebuild.
Like the others say, this is a .NET behavior. Assemblies loaded into an AppDomain cannot be unloaded. Only the AppDomain can be unloaded, and powershell uses a single appdomain. I blogged a bit about this some years ago:
https://web.archive.org/web/20170707034334/http://www.nivot.org/blog/post/2007/12/07/WhyAppDomainsAreNotAMagicBullet
When I test like this, I usually keep a shell open and use a nested shell to do tests. start powershell, cd to bin location then run "powershell" to start nested shell (new process.) "exit" to start over, and run "powershell" again.
I find the simplest way to get around this problem is to wrap the Add-Type and the test code inside of a Start-Job. Start-Job will create a background process, and the type will be loaded there. Once you are done, the process goes away and you're free to retry.
Here's an example of how it looks:
$job = Start-Job -ScriptBlock {
Add-Type -path 'my.dll'
$myObj = new-object My.MyTestClassName
$result = $myObj.TestMethod
$result
}
Wait-Job $job
Receive-Job $job
The output from the test method will be echoed to the console.
If your assembly doesn't require a binding context you can do this:
$bytes = [System.IO.File]::ReadAllBytes("Path_To_Your_Dll.dll")
[System.Reflection.Assembly]::Load($bytes)
Here is a complete example that allows to run the Add-Type command as a background job so that the assembly is unloaded once it finishes:
# Start-Job will not preserve the working directory, so do it manually
# Other arguments can also be passed to the job this way
$cd = Split-Path $MyInvocation.MyCommand.Path
$jobParams = #{
'cd' = $cd
}
Start-Job -InputObject $jobParams -ScriptBlock {
cd $Input.cd
Add-Type -Path assembly.dll
} | Receive-Job -Wait -AutoRemoveJob
Receive-Job -Wait will make sure that the output of the job is received since otherwise it will be lost.
I have been facing to similar problem. It is not possible to unload a type/assembly (that's because it applies to .NET framework).
In .NET you can solve it if you crate a new application domain (System.AppDomain) and load the assembly into that domain. It is possible to unload the app domain and that unloads all the dlls as well.
I haven't tried it yet, because for me it is much simpler to close a tab in Console and open new one.
Visual Studio Code:
Settings -> Extensions -> PowerShell Configuration -> Debugging: Create Temporary Integrated Console
Check checkbox: "Determines whether a temporary PowerShell Integrated Console is created for each debugging sessions, usefull for debugging PowerShell classes and binary modules."