Not able to load log4net.dll on Windows Server 2012 using Powershell - powershell

I have created my own framework for Powershell scripts which I am using on a lot of 2008-servers without issues. Now was the time to try it out on the first 2012-server, but for some reason it fails.
I initially do some checking to ensure that paths are valid, then I try to load the logger
try {
$log = New-Logger -Configuration $log4NetFile -Dll $dll40Path
Write-Verbose "[Enable-Logger] Using 4.0 DLL"
} catch [System.Management.Automation.MethodInvocationException] {
Write-Verbose "[Enable-Logger] Using 3.5 instead of 4.0 DLL"
$log = New-Logger -Configuration $log4NetFile -Dll $dll35Path
} finally {
Write-Verbose "[Enable-Logger] Log is $log"
}
On all our 2008-servers it loads 4.0 without any issues, but on 2012 the $log-variable is always empty, meaning it cannot load the dll.
To ensure that it has nothing to do with the config file I did try the exact same code on 2008, and it worked.

Initial workaround
Did check Powershell-version
Powershell -Command "Write-Host $psversiontable.psversion"
which is 3.0 on the 2012-server. Starting the script with the 2.0 engine like this
Powershell -Version 2 -File alertXymon1-region3queues.ps1 -verbose
and it worked.
Final solution
Did some checking today and figured out that the problem was not caused by the DLL, but a code line that is loading the DLL
[void][Reflection.Assembly]::LoadFrom($log4netDllPath) | Out-Null
Found the post "Powershell 2.0 script not working in PS 3.0" which helped me out.
I have now tested the code without [void] on both Powershell 2.0 and 3.0 and it works like a charm

Related

Selenium ChromeDriver displays SessionNotCreated after launching Chrome and then exiting

I'm attempting to create a PowerShell script to automate some testing tasks but falling at the first hurdle.
I have a folder with ChromeDriver 105.0.5195.52 (chromedriver.exe) and the Selenium Web Driver 4.4.0 for .NET (WebDriver.dll).
On the test server, .NET version 4.8.03761 and Google Chrome 105.0.5195.102 (64-Bit) are installed.
After executing the script, Chrome is briefly launched and then exits. I see the following message in the PowerShell window:
New-Object : Exception calling ".ctor" with "1" argument(s): "session not created
from tab crashed
(Session info: chrome=105.0.5195.102) (SessionNotCreated)"
In Task Manager, the ChromeDriver.exe continues to run. Subsequent runs yield the same
behaviour and message.
The script is:
$workingPath = 'C:\Users\Me\Desktop\LaunchTest'
if (($env:Path -split ';') -notcontains $workingPath) {
$env:Path += ";$workingPath"
}
$env:Path -split ';'
Add-Type -Path "$($workingPath)\WebDriver.dll"
$ChromeOptions = [OpenQA.Selenium.Chrome.ChromeOptions]::new()
$ChromeOptions.AddArguments('start-maximized')
$ChromeDriver = New-Object -TypeName "OpenQA.Selenium.Chrome.ChromeDriver" -ArgumentList #($ChromeOptions)
$ChromeDriver.Navigate().GoToURL('<https://google.co.uk>')
Any thoughts will be appreciated.
I faced quite similar problem.
My solution:
uninstall chrome 64-bit and remove its reminder
install chrome 32-bit instead

Using MSBuild from PowerShell returns error VCBuild not loaded

I'm trying to launch a build on a few Visual Studio 2005 solutions from a PowerShell Script and it returns this error:
MSBUILD : error MSB3428: Could not load the Visual C++ component "VCBuild.exe". To fix this, 1) install the .NET Framework 2.0 SDK, 2) install Microsoft Visual Studio 2005 or 3) add the location
of the component to the system path if it is installed elsewhere. [C:\path\to\solution\solutionToBuild.sln]
Done Building Project "C:\path\to\solution\solutionToBuild.sln" (default targets) -- FAILED.
Build FAILED.
"C:\path\to\solution\solutionToBuild.sln" (default target) (1) ->
(solutionToBuild target) ->
MSBUILD : error MSB3428: Could not load the Visual C++ component "VCBuild.exe". To fix this, 1) install the .NET Framework 2.0 SDK, 2) install Microsoft Visual Studio 2005 or 3) add the locati
on of the component to the system path if it is installed elsewhere. [C:\path\to\solution\solutionToBuild.sln]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.84
I really have no idea why this message is returned since I have MSVS 2005 installed and I have .NET V2 installed:
PSChildName Version Release Product
----------- ------- ------- -------
v2.0.50727 2.0.50727.5420
v3.0 3.0.30729.5420
Windows Communication Foundation 3.0.4506.5420
Windows Presentation Foundation 3.0.6920.5011
v3.5 3.5.30729.5420
Client 4.6.01590 394806 4.6.2
Full 4.6.01590 394806 4.6.2
Client 4.0.0.0
It might be that I need to add something to my system environment path but the error message doesn't say what to add, so I'm a little bit at a loss here.
So I ended up changing the function I wrote completely and here's what I came up with (which worked for me):
function Build-MSVS2K5Solution {
param (
[parameter(mandatory=$true)][validateNotNullOrEmpty()][String] $solutionToBuild,
[parameter(mandatory=$true)][validateNotNullOrEmpty()][Array] $solutionEnv,
[parameter(mandatory=$false)][validateNotNullOrEmpty()][Switch] $autoOpenBuildLog = $false
)
process {
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Please wait while solutions are being rebuilt.",0,"Building Solution...",0x1)
cd "C:\"
if (Test-Path "C:\Windows\Microsoft.NET\Framework\v2.0.50727")
{
$msBuild = "C:\Windows\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe"
}
$buildArgs = $solutionToBuild + (Get-ChildItem $SolutionToBuild | Where { $_.Name -like "*.sln" }) +" /p:Configuration=Debug /p:Platform=Win32"
$buildLog = [Environment]::GetFolderPath("Desktop") + "\buildlog.log"
foreach ($solEnv in $solutionEnv ) {
$itemPath = "env:" + $solEnv.Substring(0, ($solEnv.ToString().LastIndexOf("=") ) )
$itemValue = $solEnv.Substring( ($solEnv.ToString().LastIndexOf("=")+1) )
Set-Item -Path $itemPath -Value $itemValue
}
Write-Output "Building $buildArgs..."
Start-Process -FilePath $msBuild -ArgumentList $buildArgs -RedirectStandardOutput $buildLog
if ($autoOpenBuildLog)
{
Write-Output "Getting content of : $buildLog"
Get-Content $buildLog -Tail 1 -Wait
}
}
}
$solutionEnv = "envName=envValue", "envName2=envValue2", "etc.=etc."
Build-MSVS2K5Solution -SolutionToBuild "C:\Path\to\solution\" -solutionEnv $solutionEnv -autoOpenBuildLog
It's not perfect, but it works. Basically, I send the path to the solution, the function finds the solution "*.sln" (I know the solution direrctory only contains one). It sets the environment variables received in a string, then starts the build and if $autoOpenBuildLog is called, the buildLog is printed in the powershell prompt.
Feel free to let me know if you have suggestions to improve the code!
Thank you #David Daugherty and #stijn !

Remove Class from Memory in PowerShell

I've created a class called "Application" and loaded it in my main script with:
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
However if I ONLY make changes to the class file and run the code in PowerShell ISE none of the changes are applied. It's almost as if the class is still in memory even though I've used -FORCE.
I've also tried to remove the module before loading it and the same issue happens:
Remove-Module "Application" -ErrorAction Ignore -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
If I make a single character change in my main script then it reloads the class! But I shouldn't have to modify the main script to force PowerShell to reload the class, that just seems silly.
Is there a way to remove the Application class from memory if it exists?
NOTE: Files with just functions in them work file. This only applies to Class imports.
Addition: In the console, if I run the Remove-Module command it runs successfully but I can STILL create new objects with:
$appDetails = [Application]::new($applicationID);
Doesn't make sense to me...
MAIN SCRIPT:
# Application Details
# -----------------
#ID
$applicationID = 1;
############################################
#
# Load Supporting Scripts
#
############################################
try
{
Remove-Module "Application" -ErrorAction Ignore -FORCE;
Remove-Module "Common" -ErrorAction Ignore -FORCE;
Remove-Module "ServerData" -ErrorAction Ignore -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Common.ps1" -GLOBAL -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\ServerData.ps1" -GLOBAL -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
}
catch
{
Write-Host "`nError: Cannot load required PowerShell scripts. Ensure C:\PowerShell_Scripts\ exists and has the required files." -ForegroundColor Red;
EXIT;
}
############################################
#
# Load the SharePoint Snapin Module.
#
############################################
LoadSharePointModule;
############################################
#
# Display component details to user.
#
############################################
#Create object of "Application" to get app details based on the ID.
$appDetails = [Application]::new($applicationID);
Write-Host "Ending ......";
APPLICATION CLASS FILE
Class Application
{
#Class Properties
[STRING] $appName;
[INT32] $appID;
[INT32] $versionMajor;
[INT32] $versionOS;
[INT32] $versionCentraAdmin;
[INT32] $versionMain;
[INT32] $versionGUI;
[INT32] $versionWorkflow;
[INT32] $versionForm;
[INT32] $versionVS;
[INT32] $versionOther;
[INT32] $versionFull;
[OBJECT] $spDevSite;
[OBJECT] $versionList;
#Constructor: Setup class properties.
Application ([INT32] $appID)
{
Write-Host "`nGathering application details ..." -ForegroundColor Yellow;
try
{
#Get the SharePoint Developer site Object.
$this.spDevSite = Get-SPWeb -ErrorAction Stop $GLOBAL:spDevURL;
}
catch
{
Write-Host "`nUnable to connect to SharePoint Developer site!: $($GLOBAL:spDevURL)";
#EXIT;
}
#Assign class property.
$this.appID = $appID;
}
}
I have deliberately set the URL for $GLOBAL:spDevURL; so that the Constructor fails for this test. It fails normally and displays
Write-Host "`nUnable to connect to SharePoint Developer site!: $($GLOBAL:spDevURL)";
But if I make a change to this line and run the script, the change is not applied.
The Known Issue
There is a known issue in PowerShell 5.0 and 5.1 that explains this behavior. The issue was acknowledged by DongBo Wang on the PowerShell 6 team in November 2016. He wrote the following:
"The module analysis result is stored in a cache with the module file path as the key and the PSModuleInfo object as the value. The cache entries are not properly invalidated based on the LastWriteTime of the module file, and thus same cached value got reused."
In other words, PowerShell 5.0, 5.1, and 6.0 keeps (and uses) old copies of classes in memory when it shouldn't.
Implications
This issue causes considerable problems for development using PowerShell classes if you do not compensate for it. I wrote a test that covers about 100 of the scenarios where class reloading is important. Vaguely speaking, in about 17 of those scenarios PowerShell 5.0 and 5.1 doesn't reload the class when it should. This means using the same session across edits creates a real likelihood the interpreter will have cached duplicate copies of the same or similar classes. That makes behavior unpredictable and causes strange results that cannot be troubleshot.
Workaround
I have found that you can still be productive developing using PowerShell classes. You just need to perform each test run in a fresh PowerShell session when a project involves PowerShell classes whose source the PowerShell interpreter may consider to have changed. The customary way to do this is to invoke your test command from your PowerShell console by invoking powershell.exe:
powershell.exe -Command { Invoke-Pester }
That's not a terribly inefficient test-edit-test cycle if you've got tight unit tests. If you need to step through code, you'll need to launch a fresh copy of ISE each time you make an edit.
With this workaround, I have found the productivity impact of this bug to be manageable. I developed this and this entirely using this workaround. Each of those projects involve a significant amount of code involving PowerShell classes.

Task Scheduler with Powershell Sharepoint ps1

I am facing an issue using the Task Scheduler to run a Sharepoint Powershell script.
I'm using the following in the Task Scheduler :
-Command "& 'C:\Users\crpmcr\Desktop\Upload\Script.ps1'"
This is the resume of my script :
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
New-Item "[PATH]" -type file
$stream = [System.IO.StreamWriter] "[PATH]"
$stream.WriteLine("Message Example")
Try{
$web = Get-SPWeb "[WebApplicationUrl]"
}
Catch{
$stream.WriteLine("Error")
}
$stream.close()
If i remove the line in the try, i get the Message Example line in my new file. But it seems that the line in the try does make everything break. My file is created but it's empty. Even if some text has been added before. Also the rest of my script using the web is not working obviously.
Any idea about what my problem could be ?
Thanks!
If you are running PowerShell from
C:\Windows\SysWOW64\WindowsPowerShell\v1.0
Try changing it to
C:\Windows\System32\WindowsPowerShell\v1.0
And see if that makes any difference.
Solution found. My script was creating a file for logs and when i clicked in it it was empty. So i thought there was an issue but in fact it's because the line GetSP-web take severals seconds on my server. so it blocks the writing while it's looking for the web. 10 seconds later my file had the lines added. Obviously i was too fast and had to wait longer to see the result.

Why won't my .NET 4.0 assembly register to GAC using Powershell?

I'm using a .ps1 script to register a set of .NET 4.0 assemblies to the GAC on a production Win 2008 server r2. The assemblies have strong names and the script returns no errors:
[Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices");
[System.EnterpriseServices.Internal.Publish] $publish = New-Object System.EnterpriseServices.Internal.Publish;
$publish.GacInstall("D:\MyComponents\EZTrac.Domain.Dealer.dll")
After I run this I look in GAC_MSIL (where it should be), and then GAC_32 and GAC_64 for good measure, but it is not in any of these.
I used this post as a guide. Any idea what I'm forgetting here?
Here is a small util function I wrote for gaccing assemblies. It does a few check before doing so:
function Gac-Util
{
param (
[parameter(Mandatory = $true)][string] $assembly
)
try
{
$Error.Clear()
[Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices") | Out-Null
[System.EnterpriseServices.Internal.Publish] $publish = New-Object System.EnterpriseServices.Internal.Publish
if (!(Test-Path $assembly -type Leaf) )
{ throw "The assembly $assembly does not exist" }
if ([System.Reflection.Assembly]::LoadFile($assembly).GetName().GetPublicKey().Length -eq 0 )
{ throw "The assembly $assembly must be strongly signed" }
$publish.GacInstall($assembly)
Write-Host "`t`t$($MyInvocation.InvocationName): Assembly $assembly gacced"
}
catch
{
Write-Host "`t`t$($MyInvocation.InvocationName): $_"
}
}
After testing this out, my comments under the original question are correct. The problem was that v2 of PowerShell cannot register .NET 4.0 components. In order to do that you have to install either WMF 3 or WMF 4 so that you get Powershell 3 or 4.
As you noted in the comments: Using System.EnterpriseServices.Internal.Publish in .Net 2.0 does not allow you to register .Net 4.0 assemblies and it returns no errors if anything went wrong. Updating to PowerShell v3 or higher or running older PowerShell versions using .Net 4.0 is a solution. Read more about it here.
Other alternatives are using .Net 4.0 gacutil or have a look at my PowerShell GAC module which is able to install .Net 4.0 assemblies in PowerShell v2 and does give errors if anything went wrong.