3

Suppose I already configured IIS to allow the remote runspace of "full", how do I resolve the exception that I'm getting where Powershell says "%" is not found.

Then when I comment out the offending for..each statement, it says New-Object can't be found.

Am I missing an import? Based on the comments, it's possible that I'm missing some configuration in WinRM, or an Exchange 2010 role permission.

public static void testExchangeMBScript()
{
  PSCredential credential = new PSCredential(@"domain\me", createPassword("pw"));

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, "exchangehost.company.com", 80, "/Powershell", "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);

connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;

Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);

try
{
    runspace.Open();
    Pipeline pipeline = runspace.CreatePipeline();

    string ps1 = "Get-MailboxDatabase -Status";
    string PSFull = @" $Databases = Get-MailboxDatabase -Status
    foreach($Database in $Databases) {
        $DBSize = $Database.DatabaseSize
        $MBCount = @(Get-MailboxStatistics -Database $Database.Name).Count

        $AllMBStats = Get-MailboxStatistics -Database $Database.Name    
     $MBItemAssocCount = $AllMBStats   |   %{$_.AssociatedItemCount.value} |  Measure-Object -Average   -Sum
     $MBDeletedCount =   $AllMBStats   |   %{$_.DeletedItemCount.value} |  Measure-Object -Average   -Sum
     $MBItemCount =      $AllMBStats   |   %{$_.ItemCount.value} |  Measure-Object -Average  -Sum
     $MBDeletedItemSize= $AllMBStats   |   %{$_.TotalDeletedItemSize.value.ToMb() } |  Measure-Object -Average  -Sum
     $MBItemSize   =     $AllMBStats   |   %{$_.TotalItemSize.value.ToMb()} |  Measure-Object -Average    -Sum      

        New-Object PSObject -Property @{
Server = $Database.Server.Name
DatabaseName = $Database.Name
UserCount = $MBCount
""DatabaseSize (GB)"" = $DBSize.ToGB()
""AverageMailboxSize (MB)"" =  $MBItemSize.Average
""WhiteSpace (MB)"" = $Database.AvailableNewMailboxSpace.ToMb()
ItemCount = $MBItemCount.Sum
""LogicalSize (MB)"" =   $MBItemSize.Sum
        }
    }
";
    pipeline.Commands.AddScript(PSFull);



    // This method cannot be called multiple times on a given pipeline. The state of the 
    // pipeline must be NotStarted when Invoke is called. When this method is called, it
    // changes the state of the pipeline to Running. 
    // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms569128(v=vs.85).aspx
    if (pipeline.PipelineStateInfo.State == PipelineState.NotStarted)
    {
        Collection<PSObject> results = pipeline.Invoke();
    }
}
catch (RemoteException re)
{

    // if: Assignment statements are not allowed in restricted language mode or a Data section.
    // then: configure IIS application settings PSLanguageMode = FullLanguage
}
catch (Exception e)
{

}
        }
makerofthings7
  • 60,103
  • 53
  • 215
  • 448
  • I would imagine alias' are supported... `%` is an alias for the `ForEach-Object` cmdlet. Does it work if you put the full cmdlet name? – Andy Arismendi Feb 11 '12 at 16:34
  • When I make that change, I get the following exception: `The term 'ForEach-Object' is not recognized as the name of a cmdlet, function, script file, or operable program` – makerofthings7 Feb 11 '12 at 16:44
  • Those remote sessions are crippled for using foreach-object. Change those to foreach loops, and I bet it will work better. – mjolinor Feb 11 '12 at 16:59
  • @mjolinor Are you aware of other limitations? I have several scripts that I'd like to load in this manner – makerofthings7 Feb 11 '12 at 17:12
  • Also, @mjolinor do you have any tips on how to edit the code? I don't know how to use for..each with Measure-Object – makerofthings7 Feb 11 '12 at 17:19
  • 1
    I know if you use the -warningvariable or -errorvariable common parameters it doesn't work. If you open up a generic ps session and load the management snapin and do a get-command on any of the Exchange management cmdlets, it will tell you they're cmdlets. Do the same thing in that remote session and you'll find that you aren't actually using the cmdlets, you're using proxy functions, so you're limited to whatever functionality they enabled in the proxy. – mjolinor Feb 11 '12 at 17:22
  • You could create an array and pipe it to Measure-Object. `$a = @(); foreach ($stat in $AllMBStats) {$a += $stat.ItemCount.value} ; $a | Measure-Object ...`. – Andy Arismendi Feb 11 '12 at 17:22
  • I'd be tempted to create a pair of hash tables for the counts and sum, foreach the properties you want to count and accumulate through those and the calcuale the averages. Measure-object is a blocking cmdlet, that makes it something of a resource hog. Shameless blog pimping follows. http://mjolinor.wordpress.com/2012/01/29/powershell-hash-tables-as-accumulators/ – mjolinor Feb 11 '12 at 17:29
  • @mjolinor I just deleted all references to `for..each` and `Measure-Object`. Now I get `The term 'New-Object' is not recognized as the name of a cmdlet, function, script file, or operable program. ` – makerofthings7 Feb 11 '12 at 17:35
  • Something else to be aware of is that when you start that remote session and import the proxy functions, the functions you get will be determined by the RBAC roles that the account you're using belongs to in Exchange. If it doesn't belong to a role that's required to perform a certain action, you won't get the proxy function to do that. – mjolinor Feb 11 '12 at 17:37
  • @mjolinor Good point.. though I'm the org admin with all rights. Perhaps some of these rights aren't granted to the org admin – makerofthings7 Feb 11 '12 at 17:45
  • I think what you're tyring to do in that remote session is beyond the restrictions they put on that runspace. I'm not at work where I can test any kind of workaround, but I expect it's going to involve using a local runspace, and making calls to the remote runspace to get discrete objects that you'll have to assemble into a custom object in the local runspace. – mjolinor Feb 11 '12 at 17:50

2 Answers2

1

The remote session is a constrained runspace. It doesn't allow you to use cmdlets you're wanting for your script. I believe what you'll need to do is use an unconstrained local runspace, and then from there run the Exchange management cmdlets in the remote runspace using invoke-command:

$AllMBStats = invoke-command {Get-MailboxStatistics -Database $databasename} -argumentlist $database.name -connectionuri "http://exchangehost.company.com/powershell"

Then work with the return from that in the local runspace.

mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • Can you help me understand the difference in how I connect to a constrained vs unconstrained remote runspace using C#/Powershell? – makerofthings7 Feb 12 '12 at 17:52
  • You can only connect to a remote unconstrained runspace if there's a machine configured to provide you with one. I don't use C#, so I can tell you how to create one, but just using powershell, I'd open a powershell session on the local machine, then run the invoke-command from there using a connectionuri pointing to the Exchange server, just using the remote session to execute the Exchange cmdlets and doing things like calculating averages based on the returned values, and creating custom objects in the local session. – mjolinor Feb 12 '12 at 18:40
  • Hmm does [this IIS Powershell setting relate](http://security.stackexchange.com/q/11601/396) to what you're talking about? ... click the IIS link for more info – makerofthings7 Feb 12 '12 at 19:54
  • Probably. Basically operating in a constained runspace means that there are some elements and capabilities of the language you normally have that will not be available to you. The errors your seeing are because that runspace provided by Exchange does not have the foreach-object or new-object cmdlets, so you get errors trying to use % and new-object. I don't know exactly what the security implications are, but I suspect it may also be to keep you from imposing a substantial resource demand on the host. – mjolinor Feb 12 '12 at 20:30
  • 1
    I think maybe this might be useful: http://blogs.msdn.com/b/dvespa/archive/2011/07/20/exchange-powershell-get-help-where-object.aspx – mjolinor Feb 12 '12 at 23:08
0

Try creating a local runspace, and importing the Exchange 2010 commands from within that PS1

http://blogs.technet.com/b/exchange/archive/2009/11/02/3408653.aspx

makerofthings7
  • 60,103
  • 53
  • 215
  • 448