PS:
Defined as a function (e.g. to use in PSProfile)
function fcreds(){
$GetCredential = Get-Credential
[string]$usr = $GetCredential.username
[string]$Credfilename = $usr.split("@")[0]
$loc = "C:\TEMP\EncryptedPasswords\"
$out = $loc + $Credfilename + ".txt"
$GetCredential.Password | ConvertFrom-SecureString | Out-File $out
}
PowerShell profile
As per: https://technet.microsoft.com/en-us/library/ff461033.aspx (almost - just skip step 6)
PS:
$check = test-path $profile
if($check -eq $false){New-item –type file –force $profile}
Set-ExecutionPolicy RemoteSigned
PS:
$check = test-path $profile
if($check -eq $false){New-item –type file –force $profile}
Set-ExecutionPolicy RemoteSigned
One-liners
Re-vamping this blog a bit. You'll notice that I do often like to squeeze my scripts into one-liners a lot of the time.
$T1="Processing ";$T2=" (";$T3=" of ";$T4=")";$cnt=0
$cnt++;$Act=$T1+$var.identity+$T2+$cnt+$T3+$vars.count+$T4;$pc=(($cnt/$vars.count)*100);$pcs=[String]([System.Math]::Round($pc,0))+"`% complete";Write-Progress -PercentComplete $pc -Activity $Act -Status $pcs
Here is an example. This will get the mailbox databases in the Org and check whether they are on their Activation Preference 1 and provide output to the screen. I have more elaborate scripts but this is just to show how the progress bar works. Exchange 2007 & later, EMS - PS 2.0
$T1="Processing ";$T2=" (";$T3=" of ";$T4=")";$cnt=0
$dbs=get-mailboxdatabase | ?{$_.ReplicationType -ne "None"} | sort -unique | sort name
foreach ($db in $dbs){
$cnt++;$Act=$T1+$db.name+$T2+$cnt+$T3+$dbs.count+$T4;$pc=(($cnt/$dbs.count)*100);$pcs=[String]([System.Math]::Round($pc,0))+"`% complete";Write-Progress -PercentComplete $pc -Activity $Act -Status $pcs
$mdb = Get-MailboxDatabase $db | Select ActivationPreference,Server
$DbPrefSvr=$mdb.ActivationPreference | ?{$_.value -eq 1}
$DbPref=$DbPrefSvr.key.name;$DbNowSrv=$mdb.Server.name
If ($DbPref -ne $DbNowSrv){write-host -foregroundcolor yellow $DB "is on" $DbNowSrv "but should be on" $DbPref}
}
$CheckADServerSettings = Get-AdServerSettings; if(($CheckADServerSettings).ViewEntireForest -ne $true){Set-ADServerSettings -viewentireforest $true}
Basic Progress bar
$T1="Processing ";$T2=" (";$T3=" of ";$T4=")";$cnt=0
$cnt++;$Act=$T1+$var.identity+$T2+$cnt+$T3+$vars.count+$T4;$pc=(($cnt/$vars.count)*100);$pcs=[String]([System.Math]::Round($pc,0))+"`% complete";Write-Progress -PercentComplete $pc -Activity $Act -Status $pcs
Here is an example. This will get the mailbox databases in the Org and check whether they are on their Activation Preference 1 and provide output to the screen. I have more elaborate scripts but this is just to show how the progress bar works. Exchange 2007 & later, EMS - PS 2.0
$T1="Processing ";$T2=" (";$T3=" of ";$T4=")";$cnt=0
$dbs=get-mailboxdatabase | ?{$_.ReplicationType -ne "None"} | sort -unique | sort name
foreach ($db in $dbs){
$cnt++;$Act=$T1+$db.name+$T2+$cnt+$T3+$dbs.count+$T4;$pc=(($cnt/$dbs.count)*100);$pcs=[String]([System.Math]::Round($pc,0))+"`% complete";Write-Progress -PercentComplete $pc -Activity $Act -Status $pcs
$mdb = Get-MailboxDatabase $db | Select ActivationPreference,Server
$DbPrefSvr=$mdb.ActivationPreference | ?{$_.value -eq 1}
$DbPref=$DbPrefSvr.key.name;$DbNowSrv=$mdb.Server.name
If ($DbPref -ne $DbNowSrv){write-host -foregroundcolor yellow $DB "is on" $DbNowSrv "but should be on" $DbPref}
}
View entire forest
$CheckADServerSettings = Get-AdServerSettings; if(($CheckADServerSettings).ViewEntireForest -ne $true){Set-ADServerSettings -viewentireforest $true}
This one is useful if you have a Forest Root and you're working from a sub domain. I get that more often than you'd think. Exchange 2007 & later, EMS - PS 2.0
Full mailbox access, send as
EMS:
FULL MAILBOX ACCESS
Add full mailbox access for a user:
Add full mailbox access for a user (without automapping):
FULL MAILBOX ACCESS
Add full mailbox access for a user:
$mailbox = Read-host "Mailbox to be actioned upon"; $user = Read-host "User to be assigned full mailbox access"; Add-MailboxPermission -User $user -AccessRights
'FullAccess' -Identity $mailbox
$mailbox = Read-host "Mailbox to be actioned upon"; $user = Read-host "User to be assigned full mailbox access"; Add-MailboxPermission -User $user -AccessRights 'FullAccess' -Identity $mailbox -Automapping $false
Remove full mailbox access for a user:
$mailbox = Read-host "Mailbox to be actioned upon"; $user = Read-host "User to have full mailbox access REMOVED"; Remove-MailboxPermission -User $user -AccessRights 'FullAccess' -Identity $mailbox -Confirm:$false
SEND AS
Add 'send as' permission for a user:
$mailbox = Read-host "Mailbox to be actioned upon"; $user = Read-host "User to be assigned send-as permission"; Get-Mailbox $mailbox | Add-ADPermission -User $user -Extendedrights "Send As"
Remove 'send as' permission for a user:
$mailbox = Read-host "Mailbox to be actioned upon"; $user = Read-host "User to have send-as permission REMOVED"; Get-Mailbox $mailbox | Remove-ADPermission -User $user -Extendedrights "Send As" -Confirm:$false
Monitor message queues & alert
EMS:
[string]$smtpserver = <HTServer>
[string]$recipient = <e-mail of recipient for alerts>
[string]$hostserver = $env:computername
[string]$sender = $hostname + "@" + ($recipient -split ("@"))[1]
[string]$smtpserver = <HTServer>
[string]$recipient = <e-mail of recipient for alerts>
[string]$hostserver = $env:computername
[string]$sender = $hostname + "@" + ($recipient -split ("@"))[1]
write-host -foregroundcolor Cyan "This script will monitor message queues and create an alert if the queue message count threshold is exceeded"
$threshold = read-host "Specify a value for the threshold"
write-host -foregroundcolor Cyan "The timeout between messages is configurable"
$timeout = read-host "Specify a timeout in seconds"
$mins = $timeout/60
$HTS = (Get-ExchangeServer | ?{$_.IsHubTransportServer -eq "True"})
while($true){foreach ($HT in $HTS){Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
$Queue = Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
if ($Queue.MessageCount -gt $threshold -and $Queue.DeliveryType -ne "ShadowRedundancy"){write-host -foregroundcolor yellow "Sent mail"; $MSGQueue = $Queue | out-string; Send-MailMessage -To $recipient -From $sender -Subject "Queues alert - threshold=$threshold, timeout to next alert $mins minute(s)" -body "A message queue has triggered an alert: $MSGQueue" -SmtpServer $smtpserver; sleep $timeout}}; write-host -foregroundcolor green "Script running... Timeout="$timeout" second(s), Queue message threshold= "$threshold; sleep 5}
$threshold = read-host "Specify a value for the threshold"
write-host -foregroundcolor Cyan "The timeout between messages is configurable"
$timeout = read-host "Specify a timeout in seconds"
$mins = $timeout/60
$HTS = (Get-ExchangeServer | ?{$_.IsHubTransportServer -eq "True"})
while($true){foreach ($HT in $HTS){Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
$Queue = Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
if ($Queue.MessageCount -gt $threshold -and $Queue.DeliveryType -ne "ShadowRedundancy"){write-host -foregroundcolor yellow "Sent mail"; $MSGQueue = $Queue | out-string; Send-MailMessage -To $recipient -From $sender -Subject "Queues alert - threshold=$threshold, timeout to next alert $mins minute(s)" -body "A message queue has triggered an alert: $MSGQueue" -SmtpServer $smtpserver; sleep $timeout}}; write-host -foregroundcolor green "Script running... Timeout="$timeout" second(s), Queue message threshold= "$threshold; sleep 5}
Show Queues
EMS one liner:
$HTS = (Get-ExchangeServer | ?{$_.IsHubTransportServer -eq "True"}); while ($true){foreach ($HT in $HTS){Get-queue -server $HT | ?{$_.MessageCount -gt "0" -and $_.DeliveryType -ne "ShadowRedundancy"}}; write-host -foregroundcolor green "Next..."; sleep 15}
Database Size Limit in GB script
EMS to set the database size limit in the registry
$DBsize = "200"
$loc = get-location
[string]$server = $env:computername
$var1 = "HKLM:\System\CurrentControlSet\services\MSExchangeIS\"
$var2 = "\"
$var3 = "Database Size Limit In GB"
$var4 = “*Database Size Limit In GB*”
$var5 = "\Logstate"
$regkeys = (Get-ChildItem -path $var1$server)
Set-location -path REGISTRY::
foreach ($regkey in $regkeys){
$key = (Get-ItemProperty -path $regkey); if ($key -notlike $var4){New-ItemProperty -path $regkey -name $var3 -value $DBsize -PropertyTYpe "DWord"}
}
Remove-itemProperty $var1$server$Var5 -name $var3 -confirm:$false
set-location -path $loc
$loc = get-location
[string]$server = $env:computername
$var1 = "HKLM:\System\CurrentControlSet\services\MSExchangeIS\"
$var2 = "\"
$var3 = "Database Size Limit In GB"
$var4 = “*Database Size Limit In GB*”
$var5 = "\Logstate"
$regkeys = (Get-ChildItem -path $var1$server)
Set-location -path REGISTRY::
foreach ($regkey in $regkeys){
$key = (Get-ItemProperty -path $regkey); if ($key -notlike $var4){New-ItemProperty -path $regkey -name $var3 -value $DBsize -PropertyTYpe "DWord"}
}
Remove-itemProperty $var1$server$Var5 -name $var3 -confirm:$false
set-location -path $loc
Method not allowed
Method not allowed
Whilst trying to perform DAG tasks in an Exchange 2010 environment (suspending replication, moving an active database copy), I was getting some warnings (in yellow) as follows:
WARNING: The cmdlet extension agent with the index 0 has thrown an exception in OnComplete(). The exception is:
System.Net.WebException: The request failed with HTTP status 405: Method Not Allowed.
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at Microsoft.Exchange.SoapWebClient.CustomSoapHttpClientProtocol.<>c__DisplayClass4.b__3()
at Microsoft.Exchange.SoapWebClient.HttpAuthenticator.NetworkServiceHttpAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocol client, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.SoapHttpClientAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocolclient, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.EWS.ExchangeServiceBinding.FindFolder(FindFolderType FindFolder1)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer.GetAdminAuditLogsFolder(ADUser adUser)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer..ctor(OrganizationId organizationId, ADUser adUser, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.Create(OrganizationId organizationId, ADUser mailbox, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.AdminLogAgentClassFactory.ConfigWrapper.get_MailboxLogger()
at Microsoft.Exchange.ProvisioningAgent.AdminLogProvisioningHandler.OnComplete(Boolean succeeded, Exception e)
at Microsoft.Exchange.Provisioning.ProvisioningLayer.OnComplete(Task task, Boolean succeeded, Exception exception)
They were just warnings, but I still needed to get to the bottom of them as they indicate a problem.
The crux of this is the “The request failed with HTTP status 405: Method Not Allowed” – implying to me an authentication problem. I was suspecting that the hardware load balancers were at fault. I understood that the action that I was doing would have result in Autodiscover being updated (or attempted updating). I examined the authentication types on the Autodiscover virtual directory and tested direct connectivity by tricking my client with a hosts file entry. This ruled out the load balancers as I still got the error. Nothing was amiss with the vdirs or authentication. This was also not limited to one CAS server, thus reducing the likelihood of it being an IIS that simply needed recycling. I had deliberately set the external url on the Autodiscover virtual directory when I installed Exchange 2010 into the environment so that the existing clients didn’t get any impact from an untested service. That testing has been successfully completed, so it is now safe to expose clients to Autodiscover and that is what fixed it.
In my case the above warning message actually meant “The external url of the Autodiscover Virtual Directory is blank”. Obvious really, eh? I populated the external url for all the new Exchange 2010 CAS servers and that’s the end of the warnings.
Whilst trying to perform DAG tasks in an Exchange 2010 environment (suspending replication, moving an active database copy), I was getting some warnings (in yellow) as follows:
WARNING: The cmdlet extension agent with the index 0 has thrown an exception in OnComplete(). The exception is:
System.Net.WebException: The request failed with HTTP status 405: Method Not Allowed.
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at Microsoft.Exchange.SoapWebClient.CustomSoapHttpClientProtocol.<>c__DisplayClass4.
at Microsoft.Exchange.SoapWebClient.HttpAuthenticator.NetworkServiceHttpAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocol client, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.SoapHttpClientAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocolclient, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.EWS.ExchangeServiceBinding.FindFolder(FindFolderType FindFolder1)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer.GetAdminAuditLogsFolder(ADUser adUser)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer..ctor(OrganizationId organizationId, ADUser adUser, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.Create(OrganizationId organizationId, ADUser mailbox, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.AdminLogAgentClassFactory.ConfigWrapper.get_MailboxLogger()
at Microsoft.Exchange.ProvisioningAgent.AdminLogProvisioningHandler.OnComplete(Boolean succeeded, Exception e)
at Microsoft.Exchange.Provisioning.ProvisioningLayer.OnComplete(Task task, Boolean succeeded, Exception exception)
They were just warnings, but I still needed to get to the bottom of them as they indicate a problem.
The crux of this is the “The request failed with HTTP status 405: Method Not Allowed” – implying to me an authentication problem. I was suspecting that the hardware load balancers were at fault. I understood that the action that I was doing would have result in Autodiscover being updated (or attempted updating). I examined the authentication types on the Autodiscover virtual directory and tested direct connectivity by tricking my client with a hosts file entry. This ruled out the load balancers as I still got the error. Nothing was amiss with the vdirs or authentication. This was also not limited to one CAS server, thus reducing the likelihood of it being an IIS that simply needed recycling. I had deliberately set the external url on the Autodiscover virtual directory when I installed Exchange 2010 into the environment so that the existing clients didn’t get any impact from an untested service. That testing has been successfully completed, so it is now safe to expose clients to Autodiscover and that is what fixed it.
In my case the above warning message actually meant “The external url of the Autodiscover Virtual Directory is blank”. Obvious really, eh? I populated the external url for all the new Exchange 2010 CAS servers and that’s the end of the warnings.
Relay Connectors & multi-valued properties
I’ve been working with Receive connectors, specifically relay connectors.
101 Relay Connectors – A basic Receive Connector can be used to allow hosts to relay internally. By adding an AD Permission, you can allow hosts to relay externally. So why not create two relay connectors?
New-ReceiveConnector -Server -RemoteIPRanges 1.2.3.4 -Name "Allowed to Relay Internally HTS_Server" -AuthMechanism Tls, BasicAuth -PermissionGroups AnonymousUsers -MaxMessageSize 20MB -Bindings 0.0.0.0:25,:::25
New-ReceiveConnector -Server -RemoteIPRanges 6.7.8.9 -Name "Allowed to Relay Externally HTS_Server" -AuthMechanism Tls, BasicAuth -PermissionGroups AnonymousUsers -MaxMessageSize 20MB -Bindings 0.0.0.0:25,:::25
Get-ReceiveConnector | ?{$_.name -like "Allowed to Relay Externally*"} | Add-ADPermission -User "NT AUTHORITY\ANONYMOUS LOGON" -ExtendedRights "ms-Exch-SMTP-Accept-Any-Recipient"
So now we have Relay Connectors, but adding IP Addresses to them is not such a simple task. When you use Set-ReceiveConnector –RemoteIPRanges the existing values get overwritten.
I tried to apply the instruction given in article http://technet.microsoft.com/en-gb/library/bb684908(v=exchg.150).aspx as follows:
Set-ReceiveConnector <RelayConnector> –RemoteIPRanges @{Add=”1.2.3.4”}
It should have worked. But it didn’t :-(
So I tried this instead: http://exchangepedia.com/2007/02/how-to-update-multi-valued-attributes-in-powershell.html as follows:
$var= Get-ReceiveConnector <RelayConnector>; $var.RemoteIPRanges +=”1.2.3.4”; $var | Set-ReceiveConnector "RelayConnector"
And even:
$var= Get-ReceiveConnector <RelayConnector>; Get-Content .\IPs.txt | foreach {$rc.RemoteIPRanges += "$_"}; $var | Set-ReceiveConnector
Still no joy :-(
I don't want to have to do this in EMC. I may have a load of addresses to add. And the trouble with listing them is that the buffer size of the Exchange Management Shell causes the field to be truncated. Even after using Out-String. So what’s the quickest and easiest way to append IP addresses to a receive connector?
By using the ‘View Exchange Management Shell Command Log’ (http://technet.microsoft.com/en-gb/library/ee332355.aspx). If you add one IP address (e.g. 1.2.3.4) then you’ll see the command that was run to add it, listing all the existing values. Copy, paste, massage in notepad and use in EMS to add the additional IP addresses or ranges. E.g.:
Set-ReceiveConnector -Identity 'SERVER01\Allowed to Relay Internally SERVER01' -RemoteIPRanges '1.2.3.4', '2.3.4.5'
And then to list multiple IP addresses or address ranges in a Receive Connector's RemoteIPRanges property:
(Get-ReceiveConnector "RelayConnector").RemoteIPRanges | ft Lowerbound,Upperbound,RangeFormat -AutoSize
101 Relay Connectors – A basic Receive Connector can be used to allow hosts to relay internally. By adding an AD Permission, you can allow hosts to relay externally. So why not create two relay connectors?
New-ReceiveConnector -Server
New-ReceiveConnector -Server
Get-ReceiveConnector | ?{$_.name -like "Allowed to Relay Externally*"} | Add-ADPermission -User "NT AUTHORITY\ANONYMOUS LOGON" -ExtendedRights "ms-Exch-SMTP-Accept-Any-Recipient"
So now we have Relay Connectors, but adding IP Addresses to them is not such a simple task. When you use Set-ReceiveConnector –RemoteIPRanges the existing values get overwritten.
I tried to apply the instruction given in article http://technet.microsoft.com/en-gb/library/bb684908(v=exchg.150).aspx as follows:
Set-ReceiveConnector <RelayConnector> –RemoteIPRanges @{Add=”1.2.3.4”}
It should have worked. But it didn’t :-(
So I tried this instead: http://exchangepedia.com/2007/02/how-to-update-multi-valued-attributes-in-powershell.html as follows:
$var= Get-ReceiveConnector <RelayConnector>; $var.RemoteIPRanges +=”1.2.3.4”; $var | Set-ReceiveConnector "RelayConnector"
And even:
$var= Get-ReceiveConnector <RelayConnector>; Get-Content .\IPs.txt | foreach {$rc.RemoteIPRanges += "$_"}; $var | Set-ReceiveConnector
Still no joy :-(
I don't want to have to do this in EMC. I may have a load of addresses to add. And the trouble with listing them is that the buffer size of the Exchange Management Shell causes the field to be truncated. Even after using Out-String. So what’s the quickest and easiest way to append IP addresses to a receive connector?
By using the ‘View Exchange Management Shell Command Log’ (http://technet.microsoft.com/en-gb/library/ee332355.aspx). If you add one IP address (e.g. 1.2.3.4) then you’ll see the command that was run to add it, listing all the existing values. Copy, paste, massage in notepad and use in EMS to add the additional IP addresses or ranges. E.g.:
Set-ReceiveConnector -Identity 'SERVER01\Allowed to Relay Internally SERVER01' -RemoteIPRanges '1.2.3.4', '2.3.4.5'
And then to list multiple IP addresses or address ranges in a Receive Connector's RemoteIPRanges property:
(Get-ReceiveConnector "RelayConnector").RemoteIPRanges | ft Lowerbound,Upperbound,RangeFormat -AutoSize
Powershell remoting
I always configure my new Exchange servers for remote management and Powershell remoting but also run commands on each server. Sometimes these are commands that I want to run in a disconnected session but the default for MaxShellsPerUser is set to 5, which can cause issues with running remote scripts. The value is by design as per this article (http://blogs.msdn.com/b/powershell/archive/2010/05/03/configuring-wsman-limits.aspx) but if you know what you're doing (and you should do if you're trying to overcome this problem) then raising the value to 25 is not harmful. Unfortunately I couldn't do this remotely (by design to prevent malicious reconfiguration), but it's a simple one liner on each server:
$l = Get-Location; cd WSMan:\localhost\Shell; Set-Item .\MaxShellsPerUser 25; dir; Set-Location $l
If I break that down - assigning the current location to a variable just allows us to return to the same place afterwards; change to WSMan path; modify the value; show some output; return to the original location
Now I can run remote commands without exceeding the MaxShells limit, such as this one for restarting the Exchange Transport service on multiple servers:
$s = new-pssession -computername SERVER1, SERVER2, SERVER3
invoke-command -session $s {$h = Restart-Service MSExchangeTransport}
exit-pssession
More info on Powershell remoting can be found at:
http://technet.microsoft.com/en-us/library/dd819505.aspx
$l = Get-Location; cd WSMan:\localhost\Shell; Set-Item .\MaxShellsPerUser 25; dir; Set-Location $l
If I break that down - assigning the current location to a variable just allows us to return to the same place afterwards; change to WSMan path; modify the value; show some output; return to the original location
Now I can run remote commands without exceeding the MaxShells limit, such as this one for restarting the Exchange Transport service on multiple servers:
$s = new-pssession -computername SERVER1, SERVER2, SERVER3
invoke-command -session $s {$h = Restart-Service MSExchangeTransport}
exit-pssession
More info on Powershell remoting can be found at:
http://technet.microsoft.com/en-us/library/dd819505.aspx
Subscribe to:
Posts (Atom)