I need to execute Get-MailboxStatistics in a Runspace.
I can connect to Exchange online. If I do a 'Get-Pssession' I can see the Exchange session. But how do I pass this ExchangeOnline session to the Runspace to execute Get-MailboxStatistics.
Currently it does not recognize the Get-MailboxStatistics command in the Runspace.
Here is my code (this is part of a larger script):
# Connecting to Exchange Online
$AdminName = "hil119"
$Pass = "password"
$cred_cloud = new-object -typename System.Management.Automation.PSCredential -argumentlist $AdminName, $Pass
Connect-ExchangeOnline -Credential $cred_cloud -Prefix Cloud
# Executing Get-MailboxStatistics in a Runspace
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({Get-MailboxStatistics 'd94589'})
$PowerShell.BeginInvoke()
After days of research I found that you can run a thread on a local system or on a remote Exchange server.
If you run it on the local system, then each thread needs to call the Exchange session on their own, but if you run it a remote exchange system (onprem or cloud) you can get the exchange session just once and pass that session to the thread. You can get a remote session with an Invoke command.
Also I ended up writing the script in Poshjobs instead or runspaces. Ultimately from what I have read Poshjobs is a combination of Start-job and runspaces.
So here is the snippet of code that be used to run a Thread on a remote server. With this script you can pass the same exchange session to all threads.
Function Func_ConnectCloud
{
$AdminName = "r43667"
$AdminPassSecure = "pass"
$Cred_Cloud = new-object -typename System.Management.Automation.PSCredential -argumentlist $AdminName, $AdminPassSecure
Connect-ExchangeOnline -Credential $Cred_Cloud
$CloudSession = Get-PSSession | Where { $_.ComputerName -like "outlook.office365*"}
Return $CloudSession
}
$script_Remote =
{
param(
$Alias,
$CloudSession
)
Invoke-Command -session $CloudSession -ArgumentList $Alias -ScriptBlock {param($Alias); Get-MailboxStatistics $Alias}
}
$CloudSession = Func_ConnectCloud
$Alias = 'h672892'
$Job1 = Start-RsJob -Name "job_$Alias" -ScriptBlock $ScriptRemote -ArgumentList $Alias, $CloudSession
Receive-RsJob $Job1
Remove-RsJob $Job1
You can use this script to run threads onprem as well as cloud, although when run on a cloud server, Microsoft will only allow TWO threads. If you run more that TWO threads, your Exchange session is killed (This is different from Throttling) . So if you have a cloud environment, then its best to run your threads locally.
Special reference to #postanote for his script at https://powershell.org/forums/topic/connecting-to-office-365-in-psjobs/
Related
Currently we get an access token and then pass this token to PowerShell script to loop across all ODFB personal sites.
$url = "https://XXXXX-admin.sharepoint.com"
$conn = Connect-PnPOnline -Url $url -AccessToken $access_token -ReturnConnection
$sitecollections = Get-PnPTenantSite -IncludeOneDriveSites:$true -Filter "Url -like '-my.sharepoint.com/personal/'" -Connection $conn | Select-Object -ExpandProperty Url
foreach ($site in $sitecollections)
{
....
}
It worked successfully for years until it was broken a while ago.
I tried different versions of PnP PowerShell:
PnP version
Error
SharePointPnPPowerShellOnline 3.21.2005.2 (currently used)
Get-PnPTenantSite : Attempted to perform an unauthorized operation.
SharePointPnPPowerShellOnline 3.29.2101.0
Get-PnPTenantSite : The current connection holds no SharePoint context.
PnP.PowerShell 1.10.28
Get-PnPTenantSite : Attempted to perform an unauthorized operation.
If I change script to use an user/password instead the access token, the script works without problems:
$url = "https://XXXXX-admin.sharepoint.com"
$User = "admin#mydomain.com"
$PWord = ConvertTo-SecureString -String "Password" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$conn = Connect-PnPOnline -Url $url -Credentials $Credential -ReturnConnection
$sitecollections = Get-PnPTenantSite -IncludeOneDriveSites:$true -Filter "Url -like '-my.sharepoint.com/personal/'" -Connection $conn | Select-Object -ExpandProperty Url
foreach ($site in $sitecollections)
{
....
}
So the error happens when the script connects to SP Online using an access token.
Perhaps the some things were changed. But what exactly? Have some scope to be added when an access token is requested?
Or have some new permissions to be added for the application in Azure AD?
Update:
Modified the script (added Write-Output "Connection is:" $conn | fl) to provide more details about connection and got the difference in ConnectionType property when SharePointPnPPowerShellOnline 3.21.2005.2 is used:
When an access token is used (and the script doesn't work properly), ConnectionType : O365
When an access token is used (and the script works fine), ConnectionType : TenantAdmin
I'm trying to make some graph API calls from AZure CloudShell. To make the API call I have to acquire a token. I have a 100% working code in Azure Desktop version (PSVersion 5.1)
But same code not working in CloudShell, which runs s with (Core - 6.2)
Cloudshell libraries have couple of mismatches with documentations
Im trying to use this version of AcuireTokenAsync.
For which I have to initial PlatmforParameter
but when Im getting an error
$platformParameters = New-Object
"Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters"
"Auto" New-Object : Cannot find an overload for "PlatformParameters"
and the argument count: "1". At line:1 char:23
+ ... arameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirecto ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
Seems PlatformParameters accepting no arg constructor
This is my working code in Powershell Desktop 5.1 version
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2" # well-known client ID for AzurePowerShell
$redirectUri = "urn:ietf:wg:oauth:2.0:oob" # redirect URI for Azure PowerShell
$resourceAppIdURI = "https://graph.windows.net"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList 'Auto'
$authResultTask = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters)
$authResultTask.Wait()
$authResult = $authResultTask.Result
But same code doesn't work in CloudShell
Is there any well known variation of acquiring token from Azure Cloud shell
I wanted to automate the application creation and configuration via powershell script
As mentioned in the comment, no need to call the MS Graph APIs manually, you can automate them via AzureAD powershell module, which is also available in the cloud shell.
Samples:
1.Create application - New-AzureADApplication
New-AzureADApplication -DisplayName "My new application" -IdentifierUris "http://mynewapp.contoso.com"
2.Update an application - Set-AzureADApplication
For example, set the API permissions for the application.
$req = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
$acc1 = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "311a71cc-e848-46a1-bdf8-97ff7156d8e6","Scope"
$acc2 = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "aaff0dfd-0295-48b6-a5cc-9f465bc87928","Role"
$req.ResourceAccess = $acc1,$acc2
$req.ResourceAppId = "00000002-0000-0000-c000-000000000000"
$reqe = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
$acc1e = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "ddb3ca45-a192-477d-acb2-46bf9dc586de","Scope"
$acc2e = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "28379fa9-8596-4fd9-869e-cb60a93b5d84","Role"
$reqe.ResourceAccess = $acc1e,$acc2e
$reqe.ResourceAppId = "00000009-0000-0000-c000-000000000000"
Set-AzureADApplication -ObjectId <ObjectId> -RequiredResourceAccess #($req,$reqe)
I test the script in local and cloud shell, both work fine. If you have other requirements, just look into the Azure AD PowerShell doc, you can do most things related to AAD via this module.
For more details about the sample, you could refer to the two links, 1 and 2.
I have a PowerShell script that connects to Azure, then downloads data. The script runs great with human interaction, but I'm trying to run it silently as a scheduled task. Currently, every time the script runs, it prompts for user credentials. I change 'Always' to 'Never' and it doesn't seem to store the credentials for any length of time.
$clientId = "<CLIENTIDHERE>" # PowerShell clientId
$redirectUri = "<REDIRECTURIHERE>"
$MSGraphURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/$tenantId"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$authResult = $authContext.AcquireToken($MSGraphURI, $clientId, $redirectUri, "Always")
$token = $authResult.AccessToken
Ideally the credentials would be passed through based on the credentials running in the scheduled task. If that isn't an option, at least I'm hoping to put the username and password in the script and have the script send those credentials to authenticate. How does one authenticate silently to Azure?
You could check the script shared by Bogdan Gavril from this thread .
#Require -Version 5.0
using namespace Microsoft.IdentityModel.Clients.ActiveDirectory
$adalDll = [Reflection.Assembly]::LoadFile("<path_to>\Microsoft.IdentityModel.Clients.ActiveDirectory.dll")
$ADAuthorityURL = "https://login.windows.net/common/oauth2/authorize/"
$resourceURL = "https://analysis.windows.net/powerbi/api"
$AADuserName = "foo"
$AADpassword = "bar"
Write-Host "Retrieving the AAD Credentials...";
$credential = New-Object UserPasswordCredential($AADuserName, $AADpassword);
$authenticationContext = New-Object AuthenticationContext($ADAuthorityURL);
$authenticationResult = [AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($authenticationContext, $resourceURL, $AADClientID, $credential).Result;
$ResultAAD = $authenticationResult.AccessToken;
I was able to figure this out. The initial authentication code I presented used an Azure-specific pop-up window to grab your credentials. Using the following link [1] I converted the code to the PowerShell Get-Credential method instead. From there, I used the information in this link [2] (Example 7) to configure the Get-Credential method to pull from plain text instead of a pop-up Window.
Now plain text passwords isn't ideal, but for our needs, it was good enough.
$clientId = "<CLIENTIDHERE>" # PowerShell clientId
$redirectUri = "REDIRECTURIHERE"
$MSGraphURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/$tenantId"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$User = "<USERNAMEHERE>"
$PWord = ConvertTo-SecureString -String "<PASSWORDHERE>" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $credential.UserName,$credential.Password
$authResult = $authContext.AcquireToken($MSGraphURI, $clientId, $AADCredential)
$token = $authResult.AccessToken
[1] https://blogs.technet.microsoft.com/cloudlojik/2017/09/05/using-powershell-to-connect-to-microsoft-graph-api/
[2] https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-credential?view=powershell-6
I have one dedicated server called PRODAZAD01-VM which is running Azure Active Directory Synch to Office 365.
[CmdletBinding()]
Param(
[Parameter(position = 0, mandatory = $true)]
[ValidateSet('Delta', 'Full')]
[String]$Type,
[string]$ComputerName = 'PRODAZAD01-VM'
)
Invoke-Command -ComputerName $ComputerName -ArgumentList $type -ScriptBlock {
Param($Type)
If ($Type -eq 'Full') {$Type = 'Initial'}
Import-Module adsync
Start-ADSyncSyncCycle -PolicyType $Type
}
How can I execute the PowerShell to force it to Synch from my PowerShell ISE on my laptop without installing the module & loading it every time?
What Drew is pointing you to, is to use PowerShell Implicit Remoting to a machine with those cmdlet, thus proxying those cmdlets to your workstation for your session in the same way as you would with other cmdlets, say ADDS. For example -
See: How to use AD cmdlets
*Update for OP *
[CmdletBinding()]
Param
(
[Parameter(position = 0, mandatory = $true)]
[ValidateSet('Delta', 'Initial')]
[String]$Type,
[string]$ComputerName = 'PRODAZAD01-VM',
[string]$Creds = 'YourAdminCreds' #(Get-Credential -Credential 'YourAdminCreds')
)
$ADSession = New-PSSession -Authentication Kerberos -ComputerName $ComputerName
Invoke-Command -Session $ADSession -ScriptBlock {
'ADSync','AzureADPreview','MSOnline' |
%{ Import-Module -Name $_ -Force }
}
Import-PSSession -Session $ADSession
Get-Module -Name 'ADSync','AzureADPreview','MSOnline'
Connect-MsolService -Credential $Creds
Connect-AzureAD -Credential $Creds
If ($Type -eq 'Delta') { Start-ADSyncSyncCycle -PolicyType $Type }
Else { Start-ADSyncSyncCycle -PolicyType $Type }
I am trying to build powershell program which would:
connect to the remote server
Show number of active IIS app pools on the active server
based on the selection (1,2,3,4,....n etc) it would reset app pool
Can you please give me some tips?
Give this a try:
[Reflection.Assembly]::LoadWithPartialName('Microsoft.Web.Administration')
$sm = [Microsoft.Web.Administration.ServerManager]::OpenRemote('server1')
$sm.ApplicationPools['AppPoolName'].Recycle()
Building upon the answers already given, try the following. It uses powershell remoting, specifically Invoke-Command so you need to familiarise yourself with that.
[cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
param
(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]$ComputerName,
[parameter(Mandatory=$false)]
[System.Management.Automation.PSCredential]$Credential
)
begin
{
if (!($Credential))
{
# Prompt for credentials if not passed in
$Credential = get-credential
}
$scriptBlock = {
Import-Module WebAdministration
# Get all running app pools
$applicationPools = Get-ChildItem IIS:\AppPools | ? {$_.state -eq "Started"}
$i = 0
# Display a basic menu
Write-Host "`nApplication Pools`n"
$applicationPools | % {
"[{0}]`t{1}" -f $i, $($applicationPools[$i].Name)
$i++
}
# Get their choice
$response = Read-Host -Prompt "`nSelect Application Pool to recycle"
# Grab the associated object, which will be null
# if an out of range choice was entered
$appPool = $applicationPools[$response]
if ($appPool)
{
"Recycling '{0}'" -f $appPool.name
$appPool.recycle()
}
}
}
process
{
Invoke-Command -ComputerName $computerName -Credential $credential -ScriptBlock $scriptBlock
}
I cannot help with existing code, but which some links
Check out remote powershell sessions here
Check out the Web Server (IIS) Administration Cmdlets in Windows PowerShell, specialy the Get-WebApplication and Get-WebAppPoolState
If reset means stop, then you could take a look on Stop-WebAppPool