(Powershell & C#) ExchangeOnline Cmdlets partially working - c#

The issue is a little awkward to explain but essentially I have a piece of C# code that connects & logs into ExchangeOnline via powershell (using System.Management.Automation.Powershell) from that I wish to run some more commands specific to the ExchangeOnline instance.
I've found that certain commands do not work such as "Get-Mailbox" or "Get-DistributionList". The error stream I have captured simply says the cmdlet does not exist. However if I try these commands in a powershell window it works.
The apparently problematic code I have running is pasted below (mix of both powershell and C#):
string FullTenantScript = #"
[string]$SizeOfAllMailboxes = ((get-exomailbox -ResultSize Unlimited | get-exomailboxstatistics).TotalItemSize.Value.ToMB() | measure-object -sum).sum
$TenantProperties = [pscustomobject]#{
'Primary Domain' = Get-AzureADDomain | Where-Object IsDefault -Match 'True' | Select -ExpandProperty Name
'Tenant ID' = Get-AzureADTenantDetail | select -ExpandProperty ObjectId
'Number of users' = #(Get-AzureADUser -all $true).count
'Number of Mailboxes' = #(get-exomailbox -ResultSize unlimited).Count
'Number of Devices' = #(Get-AzureADDevice -all $true).Count
'Number of Subscriptions' = #(Get-AzureADSubscribedSku).Count
'Number of Distribution Lists' = #(Get-DistributionGroup).Count
'Number of Dynamic DLs' = #(Get-DynamicDistributionGroup).Count
'Number of 365 Groups' = #(Get-UnifiedGroup).Count
'Size of all mailboxes' = $SizeOfAllMailboxes + ' MB'
}
$TenantProperties
";
PowerShell getTenantinfo = PowerShell.Create().AddScript(FullTenantScript);
Collection<PSObject> TenantInfoResult = getTenantinfo.Invoke();
I've read online that some of the older cmlets are being phased out (for example Get-Mailbox), so instead I use (get-exomailbox). I just don't get why I can run them in a powershell window but not in my program (as they should do essentially the same thing).
Please also note my program connects into Azure also (using Connect-AzureAD).
Any help or advice as to why this is happening would be greatly appreciated & any further info required I will provide.
Thanks,
Mouse

Related

How to get output of PowerShell's Get-WmiObject in C#

I need to get which network interface is connected to which network. I found that this information is accessible in MSFT_NetConnectionProfile. Unfortunatelly I cannot access it directly from C# (I get ManagementException: Provider load failure on computer where it should run) but when I access it from PowerShell, it works. Then my idea is to run PowerShell command from C# but I cannot get the result.
using System.Management.Automation;
string command = "Get-WmiObject -Namespace root/StandardCimv2 -Class MSFT_NetConnectionProfile | Select-Object -Property InterfaceAlias, Name";
PowerShell psinstance = PowerShell.Create();
psinstance.Commands.AddScript(command);
var results = psinstance.Invoke();
foreach (var psObject in results)
{
/* Get name and interfaceAlias */
}
The code runs without errors but results are empty. I tried even adding Out-File -FilePath <path-to-file> with relative and absolute file path but no file was created. I even tried old >> <path-to-file> but without luck. When I added Out-String then there was one result but it was empty string.
When I tested the commands directly in PowerShell then it worked. Is there a way how to get it in C#?
The PS commands must be constructed in a builder-pattern fashion.
Additionally, in PS Core the Get-WmiObject has been replaced by the Get-CimInstance CmdLet.
The following snippet is working on my env:
var result = PowerShell.Create()
.AddCommand("Get-CimInstance")
.AddParameter("Namespace", "root/StandardCimv2")
.AddParameter("Class", "MSFT_NetConnectionProfile")
.Invoke();

Get Memory (Private Working Set) of Process

My problem is what i am getting for WorkingSet is very different than Task manager Memory (Private Working Set). I have tried various solutions written on NET but the values are far too away from matching. Kindly help me get Memory (Private Working Set) from task manager.
script += string.Format(#"$Processes = Get-Process -ComputerName {0} | Sort-Object WorkingSet -desc | Select-Object;", remoteMachineName);
script += #"$ProcessArray= #();";
script += #"foreach ($process in $Processes) {";
script += #"$ProcessName = $process.ProcessName;";
script += #"$ProcessSize = $process.WorkingSet/1KB;";
script += #"$objAverage = New-Object System.Object;";
script += #"$objAverage | Add-Member -type NoteProperty -name Name -value $ProcessName;";
script += #"$objAverage | Add-Member -type NoteProperty -name Memory -value $ProcessSize;";
script += #"$ProcessArray +=$objAverage; }; ";
What is shown in Process Manager as Memory (Private Working Set) is the value of the performance counter \Process\working Set - Private.
You can retrieve this value with:
$ProcessPrivateSet = Get-Counter "\Process(instancename)\Working Set - Private"
$WSPrivateKiloBytes = $ProcessPrivateSet.CounterSamples[0].CookedValue / 1KB
$WSPrivateKiloBytes is now the same value as what you see in Process Manager.
The problem with retrieving this value for a distinct process is that performance counters name process instances by process name + instance count, rather than by process ID.
So, if you launch 1 instance of a Java application, you may retrieve the counter for the java.exe process, like so:
Get-Counter "\Process(java)\Working Set - Private"
Now, if you launch another one, you'll need to reference that one like this:
Get-Counter "\Process(java#1)\Working Set - Private"
and so on and on.
You can change this behavior by setting the ProcessNameFormat for performance counter objects on the local system like this:
$RegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\PerfProc\Performance\"
Set-ItemProperty $RegPath -Name ProcessNameFormat -Value 2 -Type DWord
A value of 2 means "include process ID in instance names", a value of 1 (default) would mean "use an instance counter" (like shown above).
The new format will be processname_id
After changing the ProcessNameFormat, you can now retrieve performance counters for a specific Process ID, like so:
$javap = Get-Process -Name java | Select -First 1
Get-Counter "\Process(java_$($javap.Id))\"
Since you now have a distinct correlation between Get-Process output and the performance counters, you can now retrieve the "Private Working Set" value for each process, with a single Select-Object statement using a calculated property:
Get-Process java | Select Name,Id,#{Name="WSPrivate(KB)";Expression = {(Get-Counter "\Process($($_.Name)_$($_.Id))\Working Set - Private").CounterSamples[0].CookedValue / 1KB}}
It will take some time to retrieve each individual counter sample, so if you plan on doing this often or for a large set of processes, you might want to use a wildcard (*) and retrieve \Process(*)\Working Set - Private and then look at the InstanceName in each entry in CounterSamples

Exchange Powershell in c#

I have a bunch of powershell commands within c# however one is returning 0 results and I just cant figure it out, so with the power of the internet I am hoping you guys have an answer.
My c# code running the power is as follows
internal static List<ExchangeMailboxes> ExchangeMailboxList(string snapIn)
{
List<ExchangeMailboxes> data = new List<ExchangeMailboxes>();
StringBuilder stringBuild = new StringBuilder();
stringBuild.AppendLine("$script:WarningPreference = 'SilentlyContinue'");
stringBuild.AppendLine(
"Get-Mailbox -ResultSize Unlimited | Get-MailboxStatistics | Select DisplayName,#{name='TotalItemSize';expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split('(')[1].Split(' ')[0].Replace(',','')/1GB),2)}},#{name='TotalDeletedItemSize';expression={[math]::Round((($_.TotalDeletedItemSize.Value.ToString()).Split('(')[1].Split(' ')[0].Replace(',','')/1GB),2)}}");
using (PowerShell inst = PowerShell.Create())
{
inst.AddScript("Add-PSSnapin " + snapIn)
.AddScript(stringBuild.ToString());
Collection<PSObject> results = inst.Invoke();
foreach (PSObject obj in results)
{
data.Add(new ExchangeMailboxes()
{
Name = obj.Members["DisplayName"].Value.ToString(),
InboxSize = obj.Members["TotalItemSize"].Value.ToString(),
DeletedSize = obj.Members["TotalDeletedItemSize"].Value.ToString()
});
}
}
return data;
}
I can confirm that the snapin is loading correctly and if I run the powershell command manually it is all fine and I con confirm there are no rights issues
here is the powershell command in its raw format
Get-Mailbox -ResultSize Unlimited | Get-MailboxStatistics | Select DisplayName,#{name='TotalItemSize';expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split('(')[1].Split(' ')[0].Replace(',','')/1GB),2)}},#{name='TotalDeletedItemSize';expression={[math]::Round((($_.TotalDeletedItemSize.Value.ToString()).Split('(')[1].Split(' ')[0].Replace(',','')/1GB),2)}}
The recommended way to interact with Exchange 2010 and newer is to open a session to http://servername/powershell using the microsoft.exchange configuration:
$ExSession = New-PSSession –ConfigurationName Microsoft.Exchange –ConnectionUri ‘http://ExServer1.contoso.com/PowerShell/?SerializationLevel=Full’ -Credential $Credentials –Authentication Kerberos
I've never tried remoting from c# code, but I guess it shouldn't be any different than what your doing now (except for the powershell code itself, of course). Since you're interacting with various "size" attributes in Exchange, it is still recommended to have the management tools installed locally, otherwise those values don't serialize/deserialize properly (you'll find other posts on serverfault on that topic).

Powershell to get list of users who last checked-in files into TFS2010

I'm trying to hack a quick script that runs stylecop to check C# style violations in a project, and then print out the name of the user who last checked it in into Team Foundation Server 2010.
I've gone as far as producing the list of files containing violations:
# using Stylecop from http://sourceforge.net/projects/stylecopcli/
StylecopCli\StyleCopCLI.exe -sln mysoluton.sln -set mysettings.StyleCop -out report.xml
[xml]$violations= Get-Content report.xml
$count = $violacoes.StyleCopViolations.Violation.Count
$filenames = $violacoes.StyleCopViolations.Violation | group Source -NoElement | sort Count -descending | % {$_.Name}
Any idea on how to do the last part? get the user who last checkedin into TFS? I've been looking at the TFS PowerTools and "tf.exe history" (https://msdn.microsoft.com/en-us/library/yxtbh4yh.aspx) but can't get the hang of it.
I believe this is what you're looking for:
$filenames | %{ tf history $_ /noprompt /recursive /stopafter:1 }

C# execute Powershell

So a while back I wrote a Powershell script that would Parse an IIS Log and then do some stuff (email, create a report, and other nifty stuff). Well I am working on excuting that from a C# console app. Before I post my code I just wanted to make one thing clear, i would like to try and stay away from log parser to parse this log cause of many reasons but one specifically, why use something when you can write something else to your liking ;). So here is my code
PS Script:
$t1 =(get-date).AddMinutes(-10)
$t2 =$t1.ToUniversalTime().ToString("HH:mm:ss")
$IISLogPath = "C:\inetpub\logs\LogFiles\W3SVC1\"+"u_ex"+(get-date).AddDays(-3).ToString("yyMMdd")+".log"
$IISLogFileRaw = [System.IO.File]::ReadAllLines($IISLogPath)
$headers = $IISLogFileRaw[3].split(" ")
$headers = $headers | where {$_ -ne "#Fields:"}
$IISLogFileCSV = Import-Csv -Delimiter " " -Header $headers -Path $IISLogPath
$IISLogFileCSV = $IISLogFileCSV | where {$_.date -notlike "#*"}
$tape = $IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem | Out-Host
C# app thus far:
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
Collection<PSObject> results = pipeline.Invoke();
foreach (PSObject obj in results)
{
Console.WriteLine(results);
}
Console.ReadLine();
Now when I run my App it just sits and doesnt display anything and i Set my breakpoints and it says that there is nothing to display within my foreach which i am completely hung up on cause running my ps1 script it works perfectly and shows many lines. Any insight anyone can give would be great.
Try changing in .ps1 file:
$global:tape =$IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem
and in c#
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
pipeline.Commands.AddScript("$tape");
pipeline.Commands.AddScript("out-string");
or in .ps1:
$tape =$IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem
$tape
and in c#
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
pipeline.Commands.AddScript("out-string");

Categories