Quantcast
Channel: Terence Luk
Viewing all 836 articles
Browse latest View live

Attempting to upload a file onto a datastore with vSphere Client 6.5 fails with: "The operation failed."

$
0
0

Problem

You attempt to upload a file onto a datastore in a vSphere 6.5 environment with the vSphere Client but notice that it fails with the error message:

image

Clicking onto the Details… link beside The operation failed. reveals the following message:

The operation failed

The operation failed for an undetermined reason. Typically this problem occurs due to certificates that the browser does not trust. If you are using self-signed or custom certificates, open the URL below in a new browser tab and accept the certificate, then retry the operation. https://esxi07.domain.com If this does not resolve the problem, other possible solutions are shown in this KB article: http://kb.vmware.com/kb/2147256

image

You proceed to view the certificate with the browser and install it into the Local Computer Trusted RootCertification Authorities but the upload continues to fail:

imageimage

Solution

Other than issuing a certificate from a trusted authority such as an Enterprise CA or public CA, you can quickly get around this by browsing to the webpage of the vCenter and download the self-signed certificate via the Download trusted root CA certificates link at the bottom right corner:

image

The download.zip file will contain a certs folder with a subfolders representing different operating systems such as Linux, Mac and Windows.  Open the appropriate operating system folder:

image

Then proceed to install the root certificate onto the desktop launching the vSphere Client:

imageimageimage

image

image

imageimage

imageimageimageimage

image

Browsing back to the vCenter’s root website should no longer present a certificate warning:

image

… and datastore uploads should now work.


VMware vCenter Site Recovery Manager 5.5.1.8569 service starts and stops

$
0
0

Problem

You’ve noticed that VMware vCenter Site Recovery Manager Server service briefly starts and then stops:

imageimage

The System event logs has the following error entry:

Log Name: System

Source: Service Control Manager

Event ID: 7034

Level: Error

The VMware vCenter Site Recovery Manager Server service terminated unexpectedly. It has done this 3 time(s).

image

Reviewing the SRM latest log in the folder:

C:\ProgramData\VMware\VMware vCenter Site Recovery Manager\Logs\

image

… reveals the following entry:

Section for VMware vCenter Site Recovery Manager, pid=5092, version=5.5.1, build=1647061, option=Release
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] Logging uses fast path: false
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] Handling bora/lib logs with VmaCore facilities
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] Initialized channel manager
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] Current working directory: C:\Program Files\VMware\VMware vCenter Site Recovery Manager\bin
2018-10-24T14:49:07.083+01:00 [03480 verbose 'Default'] Setting COM threading model to MTA
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] ThreadPool windowsStackImmediateCommit = true
2018-10-24T14:49:07.083+01:00 [03480 info 'ThreadPool'] Thread pool on asio: Min Io, Max Io, Min Task, Max Task, Max Concurency: 2, 401, 2, 200, 2147483647
2018-10-24T14:49:07.083+01:00 [03480 info 'ThreadPool'] Thread enlisted
2018-10-24T14:49:07.083+01:00 [03480 info 'Default'] Set dump dir to 'C:\ProgramData\VMware\VMware vCenter Site Recovery Manager\DumpFiles'
2018-10-24T14:49:07.083+01:00 [04204 info 'ThreadPool'] Thread enlisted
2018-10-24T14:49:07.083+01:00 [04684 info 'ThreadPool'] Thread enlisted
2018-10-24T14:49:07.083+01:00 [03652 info 'ThreadPool'] Thread enlisted
2018-10-24T14:49:07.083+01:00 [00496 info 'ThreadPool'] Thread enlisted
2018-10-24T14:49:07.177+01:00 [03480 info 'Default'] Vmacore::InitSSL: handshakeTimeoutUs = 20000000
2018-10-24T14:49:07.239+01:00 [03480 error 'Default'] Certificate has expired.
2018-10-24T14:49:07.270+01:00 [03480 verbose 'HttpConnectionPool-000000'] HttpConnectionPoolImpl created. maxPoolConnections = 200; idleTimeout = 900000000; maxOpenConnections = 50; maxConnectionAge = 0
2018-10-24T14:49:07.317+01:00 [03652 verbose 'Default'] Local and remote versions are the same.  Talking with version vim.version.version9
2018-10-24T14:49:07.426+01:00 [03480 info 'Default'] VC Connection: Logging in extension by subject name
2018-10-24T14:49:07.426+01:00 [03480 info 'vmomi.soapStub[0]'] Resetting stub adapter for server <cs p:00000000041821b0, TCP:vcenter03.contoso.com:80> : Closed
2018-10-24T14:49:07.442+01:00 [03480 error 'Default'] VC server does not trust our client certificate.
2018-10-24T14:49:07.520+01:00 [00496 info 'ThreadPool'] Thread delisted
2018-10-24T14:49:07.520+01:00 [03652 info 'ThreadPool'] Thread delisted
2018-10-24T14:49:07.520+01:00 [04684 info 'ThreadPool'] Thread delisted
2018-10-24T14:49:07.520+01:00 [04204 info 'ThreadPool'] Thread delisted

image

Solution

As indicated in the log file above, the certificate that SRM uses for communication with vCenter has expired.  This can be confirmed by launching the certificate console and reviewing the properties of the certificate used by SRM.

image

To correct this issue, simply renew the certificate and update SRM to use the certificate by using the Change option in Programs and Features:

image

image

Select the Modify option:

image

You will need the service account you use to connect to the vCenter server:

image

The Automatically generate a certificate. option will generate a self-signed certificate.  For this example, I have generated a certificate with an internal Enterprise CA so I’ll be selecting Use a PKCS#12 certificate file.:

image

**Note that the bottom indicates the Installed certificate status: Certificate has expired.

Proceed and enter the SRM database information in the wizard:

image

Select the Use existing database. option:

image

Continue by clicking Install to apply the changes:

imageimage

image

--------------------------------------------------------------------------------------------------------------------------------------------------------

A few items worth mentioning for the certificate are:

  • You can export a certificate as a PFX format the rename it to have the .p12 extension for importing it in the wizard.
  • The requirements for the certificate may not be what you typically anticipate (e.g. you need the IP address in it for some reason) so refer to the following KB and carefully read the requirements (https://kb.vmware.com/s/article/2085644).  The following are a few prompts that you may receive if the certificate being used does not meet the requirements:

Failed to validate certificate.

Details:

The certificate does not contain the SRM hots name. SRM server certificates must contain the SRM host name in the Subject Alternative Name field.

image

Failed to validate certificate.

Details:

The host name (somehostName.domain.com) in the Subject Alternative Name of the provided certificate does not identically match the SRM host name (10.31.30.12).

image

Attempting to install Server Certificate on NetScaler VPX fails with the error: "Object doesn't support property or method 'endsWith"

$
0
0

Problem

You’re attempting to install a Server Certificate on a Citrix NetScaler VPX NS12.1 49.23.nc with the required .cer and .key files but receive the following error:

"Object doesn't support property or method 'endsWith"

image

Solution

These error had me stumped for a while as I kept thinking this was caused by corrupted files but I realized a bit later that it was browser related.  This error would be thrown when I use IE 11.447.14393.0:

image

… but not when I use Chrome 70.0.3538.77.

Troubleshooting Citrix NetScaler VPX licensing issues

$
0
0

I receive a lot of calls from colleagues and customers for Citrix NetScaler licensing issues over the past few years so I thought I’d write a quick blog post to demonstrate what steps to take to troubleshoot the issue.

Problem

You’ve just allocated a license on the Citrix portal for the a NetScaler VPX appliance using the Host Id (which is the MAC address of the appliance’s NIC) with the following command:

shell

lmutil lmhostid

image

Or via the GUI:

image

image

You proceed to install it onto the appliance but noticed that none of the features are turned on and the top left corner still indicates that it is the Citrix AD VPX (Freemium) version with the following properties:

License Type: Standard

Model ID: 20

Licensing Mode: Express

image

Solution

The best way to determine why the applied license is not working is to review the license.log located on the appliance in the following directory:

/var/log/

Execute cat license.log to display the log entries:

root@ns# cat license.log
18:23:07 (lmgrd) -----------------------------------------------
18:23:07 (lmgrd)   Please Note:
18:23:07 (lmgrd)
18:23:07 (lmgrd)   This log is intended for debug purposes only.
18:23:07 (lmgrd)   In order to capture accurate license
18:23:07 (lmgrd)   usage data into an organized repository,
18:23:07 (lmgrd)   please enable report logging. Use Flexera Software LLC's
18:23:07 (lmgrd)   software license administration  solution,
18:23:07 (lmgrd)   FlexNet Manager, to  readily gain visibility
18:23:07 (lmgrd)   into license usage data and to create
18:23:07 (lmgrd)   insightful reports on critical information like
18:23:07 (lmgrd)   license availability and usage. FlexNet Manager
18:23:07 (lmgrd)   can be fully automated to run these reports on
18:23:07 (lmgrd)   schedule and can be used to track license
18:23:07 (lmgrd)   servers and usage across a heterogeneous
18:23:07 (lmgrd)   network of servers including Windows NT, Linux
18:23:07 (lmgrd)   and UNIX.
18:23:07 (lmgrd)
18:23:07 (lmgrd) -----------------------------------------------
18:23:07 (lmgrd)
18:23:07 (lmgrd)
18:23:07 (lmgrd) Server's System Date and Time: Tue Nov 06 2018 18:23:07 UTC
18:23:07 (lmgrd) SLOG: Summary LOG statistics is enabled.
18:23:07 (lmgrd) The license server manager (lmgrd) running as root:
18:23:07 (lmgrd)        This is a potential security problem
18:23:07 (lmgrd)        and is not recommended.
18:23:07 (lmgrd) FlexNet Licensing (v11.14.0.2 build 191018 i86_f8) started on ns () (11/6/2018)
18:23:07 (lmgrd) Copyright (c) 1988-2016 Flexera Software LLC. All Rights Reserved.
18:23:07 (lmgrd) World Wide Web: 
http://www.flexerasoftware.com
18:23:07 (lmgrd) License file(s): /nsconfig/license/FID_ea88c82a_a1d1_47c5_960c_b518d36f6413.lic
18:23:07 (lmgrd) lmgrd tcp-port 27000
18:23:07 (lmgrd) (@lmgrd-SLOG@) ===============================================
18:23:07 (lmgrd) (@lmgrd-SLOG@) === LMGRD ===
18:23:07 (lmgrd) (@lmgrd-SLOG@) Start-Date: Tue Nov 06 2018 18:23:07 UTC
18:23:07 (lmgrd) (@lmgrd-SLOG@) PID: 10015
18:23:07 (lmgrd) (@lmgrd-SLOG@) LMGRD Version: v11.14.0.2 build 191018 i86_f8 ( build 191018 (ipv4))
18:23:07 (lmgrd) (@lmgrd-SLOG@)
18:23:07 (lmgrd) (@lmgrd-SLOG@) === Network Info ===
18:23:07 (lmgrd) (@lmgrd-SLOG@) Listening port: 27000
18:23:07 (lmgrd) (@lmgrd-SLOG@)
18:23:07 (lmgrd) (@lmgrd-SLOG@) === Startup Info ===
18:23:07 (lmgrd) (@lmgrd-SLOG@) Server Configuration: Single Server
18:23:07 (lmgrd) (@lmgrd-SLOG@) Command-line options used at LS startup: -l /var/log/license.log -c /nsconfig/license
18:23:07 (lmgrd) (@lmgrd-SLOG@) License file(s) used:  /nsconfig/license/FID_ea88c82a_a1d1_47c5_960c_b518d36f6413.lic
18:23:07 (lmgrd) (@lmgrd-SLOG@) ===============================================
18:23:07 (lmgrd) Starting vendor daemons ...
18:23:07 (lmgrd) Started CITRIX (internet tcp_port 14389 pid 10016)
18:23:07 (CITRIX) FlexNet Licensing version v11.14.0.2 build 191018 i86_f8
18:23:07 (CITRIX) SLOG: Summary LOG statistics is enabled.
18:23:07 (CITRIX) Server started on ns for:     CNS_V200S_SSERVER
18:23:07 (CITRIX) CNS_V200_SERVER CNS_SSE_SERVER
18:23:07 (CITRIX)
18:23:07 (CITRIX) Licenses are case sensitive for CITRIX
18:23:07 (CITRIX)
18:23:07 (CITRIX) Wrong hostid on SERVER line for license file:
18:23:07 (CITRIX)       /nsconfig/license/FID_ea88c82a_a1d1_47c5_960c_b518d36f6413.lic
18:23:07 (CITRIX) SERVER line says 0050569224f9, hostid is 005056927696
18:23:07 (CITRIX) Invalid hostid on SERVER line

18:23:07 (CITRIX) Disabling 1 license from feature CNS_SSE_SERVER(0B9B 56BD C8F9 3B01 )
18:23:07 (CITRIX) Disabling 1 license from feature CNS_V200S_SSERVER(05D9 67D7 CE21 1146 )
18:23:07 (CITRIX) Disabling 1 license from feature CNS_V200_SERVER(1CB2 E478 6D73 1DE8 )
18:23:07 (CITRIX) EXTERNAL FILTERS are OFF
18:23:07 (lmgrd) CITRIX using TCP-port 14389
18:23:07 (CITRIX) SLOG: Statistics Log Frequency is 240 minute(s).
18:23:07 (CITRIX) (@CITRIX-SLOG@) ===============================================
18:23:07 (CITRIX) (@CITRIX-SLOG@) === Vendor Daemon ===
18:23:07 (CITRIX) (@CITRIX-SLOG@) Vendor daemon: CITRIX
18:23:07 (CITRIX) (@CITRIX-SLOG@) Start-Date: Tue Nov 06 2018 18:23:07 UTC
18:23:07 (CITRIX) (@CITRIX-SLOG@) PID: 10016
18:23:07 (CITRIX) (@CITRIX-SLOG@) VD Version: v11.14.0.2 build 191018 i86_f8 ( build 191018 (ipv4))
18:23:07 (CITRIX) (@CITRIX-SLOG@)
18:23:07 (CITRIX) (@CITRIX-SLOG@) === Startup/Restart Info ===
18:23:07 (CITRIX) (@CITRIX-SLOG@) Options file used: None
18:23:07 (CITRIX) (@CITRIX-SLOG@) Is vendor daemon a CVD: No
18:23:07 (CITRIX) (@CITRIX-SLOG@) Number of VD restarts since LS startup: 0
18:23:07 (CITRIX) (@CITRIX-SLOG@)
18:23:07 (CITRIX) (@CITRIX-SLOG@) === Network Info ===
18:23:07 (CITRIX) (@CITRIX-SLOG@) Listening port: 14389
18:23:07 (CITRIX) (@CITRIX-SLOG@) Daemon select timeout (in seconds): 1
18:23:07 (CITRIX) (@CITRIX-SLOG@)
18:23:07 (CITRIX) (@CITRIX-SLOG@) === Host Info ===
18:23:07 (CITRIX) (@CITRIX-SLOG@) Host used in license file: ns
18:23:07 (CITRIX) (@CITRIX-SLOG@) Running on Hypervisor: Not determined - treat as Physical
18:23:07 (CITRIX) (@CITRIX-SLOG@) ===============================================
18:23:07 (CITRIX) No valid hostids, exiting
18:23:07 (CITRIX) EXITING DUE TO SIGNAL 34 Exit reason 2
18:23:07 (lmgrd) CITRIX exited with status 34 (Invalid host)
18:23:07 (lmgrd) Please correct problem and restart daemons
lmstat - Copyright (c) 1989-2016 Flexera Software LLC. All Rights Reserved.
Flexible License Manager status on Tue 11/6/2018 18:23

License server status: 27000@ns
     License file(s) on ns: /nsconfig/license/FID_ea88c82a_a1d1_47c5_960c_b518d36f6413.lic:

        ns: license server UP (MASTER) v11.14.0

Vendor daemon status (on ns):

    CITRIX: The desired vendor daemon is down. (-97,121)


18:23:10 (lmgrd) lmgrd will now shut down all the vendor daemons

18:23:10 (lmgrd) EXITING DUE TO SIGNAL 15
root@ns#

Reviewing the output above will usually provide the reason why the appliance isn’t licensed as expected and in the case of this example, the cause is an incorrect Host Id used to generate the license:

18:23:07 (CITRIX) Wrong hostid on SERVER line for license file:
18:23:07 (CITRIX)       /nsconfig/license/FID_ea88c82a_a1d1_47c5_960c_b518d36f6413.lic
18:23:07 (CITRIX) SERVER line says 0050569224f9, hostid is 005056927696
18:23:07 (CITRIX) Invalid hostid on SERVER line

Proceeding to reallocate the license with the appropriate Host Id will license the appliance as expected:

image

VMware Horizon 7.6.0 agent unable to contact Connection Server with error: “com.vmware.vdi.messagesecurity.MessageSecurityException: Paired key does not exist”

$
0
0

Problem

You’ve noticed that some desktops are in Agent Unreachable state in the VMware Horizon 7 Administrator console and clicking on the elipsis displays the following detail:

Status:Agent unreachable

Pairing state:Paired and secured

Configured by:vcs.domain.com

Attempted theft by:

image

Reviewing the debug logs on the virtual desktop in the directory C:\programdata\VMware\VDM\Logs:

image

… displays the following entries:

2018-12-17T16:48:34.881-04:00 INFO  (0EAC-0D78) <Thread-4> [AgentJmsConfig] Attempting to securely pair agent for JMS communication
2018-12-17T16:48:34.881-04:00 DEBUG (0EAC-0D78) <Thread-4> [AgentJmsConfig] Using paired signing key
2018-12-17T16:48:34.881-04:00 DEBUG (0EAC-0D78) <Thread-4> [JmsManager] Unable to connect to JMS server conto-vcs.contoso.com com.vmware.vdi.logger.Logger.debug(Logger.java:44)
com.vmware.vdi.messagesecurity.MessageSecurityException: Paired key does not exist
     at com.vmware.vdi.agent.messageserver.AgentJmsConfig.pairOverJms(SourceFile:318)
     at com.vmware.vdi.agent.messageserver.JmsManager.connect(SourceFile:288)
     at com.vmware.vdi.agent.messageserver.Main.Start(SourceFile:1265)
2018-12-17T16:48:44.975-04:00 ERROR (0EAC-09C4) <Main Thread> [wsnm_jms] JavaBridge reader thread init: timed out
2018-12-17T16:48:44.975-04:00 DEBUG (0EAC-0400) <1024> [wsnm_jms] AddressCache::ThreadEntry: Stopping
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] SharedMem reader opt, reader 0x02EA21E8 for channel 0x00311280 detaching from thread 0x02E62168 handleCount 7
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] SharedMem reader opt, peer is dead - reader 0x02EA21E8 in thread 0x02E62168
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] MessageFrameWork Worker Shutdown OnChannelDelete, Name=EventLoggerService
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] MessageFrameWork Worker Shutdown OnChannelDelete, Name=JavaView-3756
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] MessageFrameWork Worker Shutdown OnChannelDelete, Name=JMSBridge
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] MessageFrameWork Worker Shutdown OnChannelDelete, Name=TimingProfilerService
2018-12-17T16:48:45.177-04:00 DEBUG (0644-0958) <SharedMemReaderThread> [MessageFrameWork] CORE::AuthChannelInt::~AuthChannelInt(): Closed incoming SharedMemory channel from machine conto-PLANET002.contoso.com, user contoso\conto-PLANET002$,  channel 00311280
2018-12-17T16:48:45.177-04:00 INFO  (0644-08F4) <JavaBridge> [wsnm_jmsbridge] wsnm_jms died, restarting in a minute

image

Further review of the logs displays the following entries:

2018-12-18T11:08:00.715-04:00 DEBUG (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] coregate 'KeyVault' locking

2018-12-18T11:08:00.715-04:00 DEBUG (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] coregate 'KeyVault' locked

2018-12-18T11:08:00.715-04:00 INFO (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVaultCAPI upgrade rekey start for wsnm_jms

2018-12-18T11:08:00.716-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVaultCAPI encipher key 'local storage cipher key#1' MISSING, error:

2018-12-18T11:08:00.716-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'DataRef-9IQloB7D' from CAPI

2018-12-18T11:08:00.718-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.718-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.719-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'DataRef-AmH0fBxY' from CAPI

2018-12-18T11:08:00.719-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVaultCAPI encipher key 'local storage cipher key#1' MISSING, error:

2018-12-18T11:08:00.720-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'DataRef-hfBerq31' from CAPI

2018-12-18T11:08:00.720-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.720-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.721-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'DataRef-I4nSYR9Q' from CAPI

2018-12-18T11:08:00.721-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.722-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.722-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'DataRef-IYonrwPb' from CAPI

2018-12-18T11:08:00.722-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 87 (The parameter is incorrect.)

2018-12-18T11:08:00.722-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.722-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local master encryption key' from CAPI

2018-12-18T11:08:00.723-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.723-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.723-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local master encryption key' from CAPI

2018-12-18T11:08:00.724-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 87 (The parameter is incorrect.)

2018-12-18T11:08:00.724-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.724-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local master encryption key' from CAPI

2018-12-18T11:08:00.725-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.725-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.725-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local storage context' from CAPI

2018-12-18T11:08:00.726-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.726-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.726-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local storage context' from CAPI

2018-12-18T11:08:00.766-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault cannot verify signature, error = 0x80090006 (Invalid Signature.)

2018-12-18T11:08:00.766-04:00 ERROR (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault registry data BAD SIGNATURE

2018-12-18T11:08:00.767-04:00 WARN (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVault failed to convert 'local storage context' from CAPI

2018-12-18T11:08:00.767-04:00 INFO (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] KeyVaultCAPI upgrade rekey completed for wsnm_jms

2018-12-18T11:08:00.767-04:00 DEBUG (12A0-0E88) <delayedRekeyCAPI> [MessageFrameWork] coregate 'KeyVault' un-locked

image

Solution

This issue had me stumped for a while as I could not find any results from the internet with the error messages above.  What ended up correcting the problem was a solution to another issue I had at another client that displayed a different error.  The solution can be found in my previous blog post here:

Troubleshooting VMware Horizon View 7.5.1 Virtual Desktop in “Agent unreachable” status
http://terenceluk.blogspot.com/2018/10/troubleshooting-vmware-horizon-view-751.html

Executing the vdmadmin command to reset the key pair resolved the issue:

vdmadmin -A -d desktop-pool-name -m name-of-machine-in-pool -resetkey

C:\Program Files\VMware\VMware View\Server\tools\bin>vdmadmin -A -d desktop-pool

-name -m planet002 -resetkey

Agent Public Key

================

MIHwMIGoBgcqhkjOOAQBMIGcAkEA/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t

9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFwIVAJYu3cw2nLqOuyYO5rahJtk0bjjFAkBnhHGyepz0Tuka

ScUUfbGpqvJE8FpDTWSGkx0tFCcbnjUDC3H9c9oXkGmzLik1Yw4cIGI1TQ2iCmxBblC+eUykA0MAAkBA

lgbnFPWs2bOZJHQqyOtFVDf5IndS2riwKqaJTTUxCDutBJg4AsJqRVVa/Ktfc2Nq1joL+FF6AbAOxMtG

tyHT

C:\Program Files\VMware\VMware View\Server\tools\bin>

image

Attempting to open Excel files within Outlook 2016 in protected mode fails with: “Microsoft Excel cannot open or save any more documents because there is not enough available memory or disk space.”

$
0
0

Problem

You’ve noticed that attempting to open Excel files from within Outlook 2016:

image

… fails with the following message:

Microsoft Excel cannot open or save any more documents because there is not enough available memory or disk space.

To make more memory available, close workbooks or programs you no longer need.

To free disk space, delete files you no longer need from the disk you are saving to.

image

image

Further troubleshooting of the issue shows that this only happens to Excel files received from external senders outside of the Exchange organization and files sent from internal users are fine because files received from external senders are blocked:

image

… while files from internal senders are not:

image

**Note that you can determine where the temporary Excel file is stored when you launch Outlook by navigating to the following registry key item:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\outlook\Security

OutlookSecureTempFolder

imageimage

Reviewing forums and KB articles suggest the following workaround of disabling Enable Protected View for Outlook attachments:

image

image

While this solution works, it reduces the security protection for users because they will now open Excel Outlook attachments in non-protected mode.

Attempting to add the Outlook OutlookSecureTempFolder path as a Microsoft Office Trusted Location would not be a good solution because it also reduces security and Excel would not allow it either:

image

Solution

I wasn’t able to figure this out as all the forum posts I came across suggested the workaround so I opened up a ticket with Microsoft and the engineer eventually found the following KB:

Unable to open to Microsoft Word Documents or Excel Spreadsheets from Outlook 2013
https://support.microsoft.com/en-gb/help/3020607/unable-to-open-to-microsoft-word-documents-or-excel-spreadsheets-from

Reviewing the RDS server that had this problem revealed that the firewall was indeed disabled:

image

Re-enabling it and restarting the server corrected the problem.

Citrix NetScaler CLI command cheat sheet

$
0
0

I worked with a Citrix NetScaler engineer a year ago on a case where we had to had to review historic and live logs to troubleshoot an issue and was told that they had a cheat sheet of commonly used commands so I asked her to send it to me.  Below are commands I’ve come to find very useful during troubleshooting:

Uncompress an archived log file:

gunzip newnslog.21.gz

Discover the time period covered by the log:

nsconmsg -K newnslog.21 –d setime

View load-balancing statistics from the archived log:

nsconmsg -K newnslog.21 -s ConLb=2 -d oldconmsg

Extract logging information for a shorter duration:

nsconmsg -K newnslog.21 -s time=12Jan2006:00:00 -k short_log.nsl -T 1200 -d copy

Start the log process for newnslog:

nsconmsg -k /var/nslog/newnslog -T 172800 &

If you want to:

View the time span of the current newnslog file:

nsconmsg -K newnslog -d setime

View the time span of the archived newnslog file:

zcat filename | nsconmsg -K pipe -d setime

View events in the current newnslog file:

nsconmsg -K newnslog -d event

View console messages in the current newnslog file:

nsconmsg -K newnslog -d consmsg

View counter values in the current newnslog file:

nsconmsg -K newnslog -d stats

View counter values in the current newnslog file:

nsconmsg -K newnslog -d stats –d current

View non-zero counter values in the current newnslog file:

nsconmsg -K newnslog -d statswt0 –d current

To display event information, such as entity up/down, alerts and configuration saves in the shell:

nsconmsg -K newnslog -d event

To display CPU usage in the shell:

nsconmsg -K newnslog -s totalcount=200 -g cpu_use -d current

To display memory utilization in the shell:

nsconmsg -s ConMEM=1 -d oldconmsg

To display established HTTP connections in the shell:

nsconmsg -j server_NSSVC_HTTP_vserver -d current

To display load balancing statistics in the shell:

nsconmsg -K newnslog –s ConLb=x –d oldconmsg

This command gives basic information when x=1 and detailed information when x=2.

Use the following command to view traffic distribution from the shell:

nsconmsg -K /var/nslog/newnslog -s time -s ConLB=2 -2 distrconmsg

To display load-balancing information in the shell:

nsconmsg -s ConLb=1 -d oldconmsg

To display monitoring statistics in the shell:

nsconmsg -K newnslog –s ConMon=x –d oldconmsg

This command gives basic information when x=1 and gives detailed information when x=2.

If you want to:

View SSL stats for front-end connections:

nsconmsg -K newnslog -s ConSSL=1 -d oldconmsg

View SSL stats for back-end connections:

nsconmsg -K newnslog -s ConSSL=2 -d oldconmsg

View SSL stats for front and back-end connections:

nsconmsg -K newnslog -s ConSSL=3 -d oldconmsg

To display content switching statistics in the shell:

nsconmsg -K newnslog –s ConCSW=1 -d oldconmsg

To display compression statistics in the shell:

nsconmsg -K newnslog –s ConCMP=x -d oldconmsg

This command gives old compression method related statistics when x=1 and gives new compression method related statistics when x=2

To display integrated caching statistics in the shell:

nsconmsg -K newnslog -s ConIC=1 -d oldconmsg

Batch file to disable a Windows service that can be deployed via Group Policy (GPO)

$
0
0

I’ve been asked several times in the past by colleagues about what I typically use to disable services on domain joined Windows desktops or servers and my response is that it depends. One of the ways through the use of importing Security Templates, which I’ve used in the past is demonstrated in this old blog post:

Creating a new security policy and applying it via GPO to disable VMware View 5.0 Thinprint’s “TP AutoConnect Service” and “TP VC Gateway Service” service
http://terenceluk.blogspot.com/2012/03/creating-new-security-policy-and.html

In the event that using the Security Template is not a viable option then I would use a GPO to apply a batch file as a startup script. The following is an example of the breakdown of what the batch file does to disable the TightVNC service:

  1. Check to see if the TightVNC service exists
  2. If TightVNC service exists then:
    1. Set service to disabled
    2. Gracefully stop service
    3. Taskkill the service
  3. If it doesn’t exist then do nothing

The following are the actual commands in the batch file that can be modified for any Windows service:

@echo off

REM --- Set variables for service name and task manager process

SET serviceName=tvnserver

SET taskManagerProcess=tvnserver.exe

REM --- Test to see if service exists in the services console

SC QUERY %serviceName% > NUL

IF ERRORLEVEL 1060 GOTO MISSING

REM --- Set service to disabled, gracefully stop service and taskkill process

sc config %serviceName% start= disabled

net stop %serviceName%

taskkill /im %taskManagerProcess% /f

REM --- ECHO Disabled and stopped TightVNC

GOTO END


Microsoft Exchange Server 2013 Transport Service Stuck on Starting

$
0
0

Problem

You’ve noticed that you are unable to access the Microsoft Exchange Admin Center (EAC) via the /ECP directory to manage Exchange:

Server Error in ‘/ecp’ Application.

Configuration Error

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Could not load file or assembly ‘Microsoft.Exchange.Clients.Strings, Version-15.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The system cannot find the file specified.

Reviewing the services in the services console show that the Microsoft Exchange Transport service is stuck at Starting:

Attempting to launch the Management Shell will also fail:

Making an attempt to perform a recover server install of the will also fail during the Finalizing Setup step:

Performing Microsoft Exchange Server Prerequisite Check

Configuring Prerequisites COMPLETED

Prerequisite Analysis COMPLETED

Configuring Microsoft Exchange Server

Preparing Setup COMPLETED

Stopping Services COMPLETED

Copying Exchange Files COMPLETED

Language Files COMPLETED

Restoring Services COMPLETED

Language Configuration COMPLETED

Mailbox role: Transport service COMPLETED

Mailbox role: Client Access service COMPLETED

Mailbox role: Unified Messaging service COMPLETED

Mailbox role: Mailbox service COMPLETED

Exchange Management Tools COMPLETED

Client Access role: Client Access Front End service COMPLETED

Client Access role: Front End Transport service COMPLETED

Finalizing Setup FAILED

The following error was generated when "$error.Clear();

start-SetupService -ServiceName MSExchangeFrontendTransport

" was run: "Microsoft.Exchange.Configuration.Tasks.ServiceDidNotReachStatusExcep

tion: Service 'MSExchangeFrontendTransport' failed to reach status 'Running' on

this server.

at Microsoft.Exchange.Configuration.Tasks.Task.ThrowError(Exception exception

, ErrorCategory errorCategory, Object target, String helpUrl)

at Microsoft.Exchange.Configuration.Tasks.Task.WriteError(Exception exception

, ErrorCategory category, Object target)

at Microsoft.Exchange.Management.Tasks.ManageSetupService.WaitForServiceStatu

s(ServiceController serviceController, ServiceControllerStatus status, Unlimited

`1 maximumWaitTime, Boolean ignoreFailures, Boolean sendWatsonReportForHungServi

ce)

at Microsoft.Exchange.Management.Tasks.ManageSetupService.StartService(Servic

eController serviceController, Boolean ignoreServiceStartTimeout, Boolean failIf

ServiceNotInstalled, Unlimited`1 maximumWaitTime, String[] serviceParameters)

at Microsoft.Exchange.Management.Tasks.ManageSetupService.StartService(String

serviceName, Boolean ignoreServiceStartTimeout, Boolean failIfServiceNotInstall

ed, Unlimited`1 maximumWaitTime, String[] serviceParameters)

at Microsoft.Exchange.Management.Tasks.StartSetupService.InternalProcessRecor

d()

at Microsoft.Exchange.Configuration.Tasks.Task.<ProcessRecord>b__b()

at Microsoft.Exchange.Configuration.Tasks.Task.InvokeRetryableFunc(String fun

cName, Action func, Boolean terminatePipelineIfFailed)".

The Exchange Server setup operation didn't complete. More details can be found

in ExchangeSetup.log located in the <SystemDrive>:\ExchangeSetupLogs folder.

C:\SP3 CU10>

Solution

This issue had me stumped for a while because there was no way to get into the management console or shell to review the configuration and determine how the receive and send connectors were configured and which one of them was causing the transport service to be stuck starting.  What I ended up being to find as a workaround for this issue was to download the following tool:

Exchange Management Console Troubleshooter
https://gallery.technet.microsoft.com/Exchange-Management-b9d918b1

Run the EMTshooter.ps1 PS script:

Run the following command to add the PowerShell snapin:

Add-PSsnapin Microsoft.Exchange.Management.PowerShell.E2010

Then use the Get-ReceiveConnector to review the receive connector configuration.  Having the ability to execute cmdlets allowed me to discover that one of the connectors (Allow Anonymous Relay) was mistakenly created as a HubTransport receive connector when it was supposed to be a FrontendTransport type causing the transport service to not be able to start:

Get-ReceiveConnector | where-object {$_.TransportRole -match "HubTransport"} | where-object {$_.Identity -like "SVR-MAIL-03*"}

Disabling the connector allowed with the cmdlet allowed the transport service to start and the EAC to be accessible again.

Unable to install Service Pack 1 onto Windows Server 2008 R2

$
0
0

Problem

You have a Windows Server 2008 R2 server that currently does not have SP1 installed:

You proceed to download Windows Server 2008 R2 SP1 and run the install:

windows6.1-KB976932-X64.exe

… but it fails with the following error message:

Installation was not successful

An unknown error has occurred. (Details)

Error: 0x800f0818

Reviewing the CBS.log log located in the directory:

C:\Windows\Logs\CBS

Reveals the following error messages:

2019-01-25 15:16:10, Info CBS Mark store corruption flag because there is a mismatch between package identity and its content on package: Package_for_KB2618444_RTM~31bf3856ad364e35~amd64~~6.1.1.2. [HRESULT = 0x00000000 - S_OK]

2019-01-25 15:16:10, Info CBS Identity mismatch: Specified Identity: Package_for_KB2618444_RTM~31bf3856ad364e35~amd64~~6.1.1.2, actual package Identity: Microsoft-Windows-Foundation-Package~31bf3856ad364e35~amd64~~6.1.7600.16385 [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to resolve package [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to populate children. [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to initialize package: Microsoft-Windows-InternetExplorer-Package~31bf3856ad364e35~amd64~~8.0.7600.16385, from path: \\?\C:\Windows\Servicing\Packages\Microsoft-Windows-InternetExplorer-Package~31bf3856ad364e35~amd64~~8.0.7600.16385.mum, existing package: 1 [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to resolve package [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to populate children. [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to initialize internal package [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Info CBS Failed to create package. [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Error CBS Failed to internally open package. [HRESULT = 0x800f0818 - CBS_E_IDENTITY_MISMATCH]

2019-01-25 15:16:10, Error CBS SPI: (CSPICbsClient::EnumPackages:156)Failed to open package hr=0x800f0818

2019-01-25 15:16:10, Error CBS SPI: (CSystem::EnumerateCbsPackages:360)Failed to populate CBS package list hr=0x800f0818

2019-01-25 15:16:10, Info CBS Session: 30717154_1779796761 finalized. Reboot required: no [HRESULT = 0x00000000 - S_OK]

2019-01-25 15:16:10, Info CBS SPI: Failed enumerating CBS packages

2019-01-25 15:16:10, Error CBS SPI: (GetLatestVersionFromCBSStore:131)ATL exception hr=0x800f0818

2019-01-25 15:16:10, Error CBS SPI: (CSPCInstallTask::ApplicabilityScan:632)Failed to get latest version of Package_for_KB976902~31bf3856ad364e35~amd64~~6.1.1.17514 from the store hr=0x800f0818

2019-01-25 15:16:10, Error CBS SPI: (CSPInstall::CompatibilityApplicabilityScan:1303)Failed in applicability check of task hr=0x800f0818

2019-01-25 15:16:10, Info CBS SPI: Ending Compatibility\Applicability scan

2019-01-25 15:16:10, Error CBS SPI: (PerformSPInstallation:833)Failed to install SP using UI hr=0x800f0818

2019-01-25 15:16:10, Error CBS SPI: (wmain:1105)Failed to perform SP installation hr=0x800f0818

2019-01-25 15:16:10, Info CBS SPI: Reporting Failed event

You navigate the directory C:\Windows\servicing\Packages and can confirm that the Package_for_KB2618444_RTM~31bf3856ad364e35~amd64~~6.1.1.2. exists.

Searching for this error message results in recommendations to install the:

System Update Readiness Tool for Windows Server 2008 R2 x64 Edition (KB947821) [October 2014]

https://www.microsoft.com/en-us/download/details.aspx?id=14668

Windows6.1-KB947821-v34-x64.msu

You proceed to install the package:

Then attempt to install SP1 again but now receive the following error:

Installation was not successful

A required certificate is not twithin its validity period when verifying against the current system clock or the timestampe in the signed file.

Error: CERT_E_EXPIRED(0x800b0101)

Proceeding to review the SP1 install logs reveal the following:

2019-01-25 15:58:21, Info CBS WinVerifyTrust failed [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Error CBS Failed to verify if catalog file \\?\C:\Windows\Servicing\Packages\Package_15_for_KB2722913~31bf3856ad364e35~amd64~~6.1.1.0.cat is valid. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to initialize package: Package_15_for_KB2722913~31bf3856ad364e35~amd64~~6.1.1.0, from path: \\?\C:\Windows\Servicing\Packages\Package_15_for_KB2722913~31bf3856ad364e35~amd64~~6.1.1.0.mum, existing package: 1 [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to resolve package [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to populate children. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to initialize package: Microsoft-Windows-InternetExplorer-Package~31bf3856ad364e35~amd64~~8.0.7600.16385, from path: \\?\C:\Windows\Servicing\Packages\Microsoft-Windows-InternetExplorer-Package~31bf3856ad364e35~amd64~~8.0.7600.16385.mum, existing package: 1 [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to resolve package [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to populate children. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to initialize internal package [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Info CBS Failed to create package. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Error CBS Failed to internally open package. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

2019-01-25 15:58:21, Error CBS SPI: (CSPICbsClient::EnumPackages:156)Failed to open package hr=0x800b0101

2019-01-25 15:58:21, Error CBS SPI: (CSystem::EnumerateCbsPackages:360)Failed to populate CBS package list hr=0x800b0101

2019-01-25 15:58:21, Info CBS Session: 30717160_1382002389 finalized. Reboot required: no [HRESULT = 0x00000000 - S_OK]

2019-01-25 15:58:21, Info CBS SPI: Failed enumerating CBS packages

2019-01-25 15:58:21, Error CBS SPI: (GetLatestVersionFromCBSStore:131)ATL exception hr=0x800b0101

2019-01-25 15:58:21, Error CBS SPI: (CSPCInstallTask::ApplicabilityScan:632)Failed to get latest version of Package_for_KB976902~31bf3856ad364e35~amd64~~6.1.1.17514 from the store hr=0x800b0101

2019-01-25 15:58:21, Error CBS SPI: (CSPInstall::CompatibilityApplicabilityScan:1303)Failed in applicability check of task hr=0x800b0101

2019-01-25 15:58:21, Info CBS SPI: Ending Compatibility\Applicability scan

2019-01-25 15:58:21, Error CBS SPI: (PerformSPInstallation:833)Failed to install SP using UI hr=0x800b0101

2019-01-25 15:58:21, Error CBS SPI: (wmain:1105)Failed to perform SP installation hr=0x800b0101

2019-01-25 15:58:21, Info CBS SPI: Reporting Failed event

Further research shows that rolling back the clock would allow for the install:

CERT_E_EXPIRED(0x800b0101)

https://social.technet.microsoft.com/Forums/Lync/en-US/5c95bd6f-041e-4d68-b19b-304c5162aa27/certeexpired0x800b0101?forum=w7itproSP

Doing so did not work for this situation. A bit more digging in the logs revealed this line referencing this package:

2019-01-25 14:50:28, Info CBS Read out cached package applicability for package: WUClient-SelfUpdate-Core-TopLevel~31bf3856ad364e35~amd64~~7.6.7600.256, ApplicableState: 112, CurrentState:112

Locating the file and opening the package confirmed that the WUClient-SelfUpdate-Core~31bf3856ad364e35~amd64~~7.6.7600.256.cat had a timestamp dating back to June 2, 2012 (it’s 2019 right now):

Another line in the logs that actually referenced a certificate was the following:

CBS Failed to verify if catalog file \\?\C:\Windows\Servicing\Packages\Package_15_for_KB2722913~31bf3856ad364e35~amd64~~6.1.1.0.cat is valid. [HRESULT = 0x800b0101 - CERT_E_EXPIRED]

Locating this package and viewing its properties confirmed that the Digital Signature had expired on June 28, 2012:

Solution

It took a bit of time but the solution to this issue was to install the following update to correct the signatures before installing SP1:

Microsoft Security Advisory: Compatibility issues affecting signed Microsoft binaries: November 13, 2012

https://support.microsoft.com/en-us/help/2749655/microsoft-security-advisory-compatibility-issues-affecting-signed-micr

Using InstallSoftwareRemotely.ps1 to upgrade VMware Horizon View Agent

$
0
0

I’ve had a lot of colleagues in the past ask me what I would use for smaller clients who did not have a software deployment tool such as SCCM to install or upgrade applications and the solution I typically present is the InstallSoftwareRemotely.ps1 PowerScript found here:

Install software on multiple computers remotely with PowerShell
https://gallery.technet.microsoft.com/scriptcenter/Install-software-on-9278d883

I’ve always used this script to perform small deployments and this blog post serves as a demonstration of how to use it.

Prerequisites for InstallSoftwareRemotely.ps1

The items that you’ll need to prepare as prerequisites before executing the command are as follows:

  1. An administrator account on the target computers that you’ll be installing applications on (a domain admin would suffice)
  2. Download Psexec.exe from https://docs.microsoft.com/en-us/sysinternals/downloads/psexec and place the executable C:\Windows\System32 so the PS script could execute the command to enable PSRemoting
  3. Place the software installation file, in this case the VMware Horizon View agent, into a network share accessible by the target computer and the account you will be running the PS script and ensure that it is not blocked (see one of my previous blog posts for more information about why this is important http://terenceluk.blogspot.com/2018/05/attempting-to-use-invoke-command.html)
  4. Arguments (if reqiured) for the installation executable
  5. A CSV file containing the computers that the software will be installed on (see the TechNet article’s description of the ComputerList for more information about the accepted sources)
  6. Amount numeric value for the number of retries for the installation
  7. The application name to check and see if it exists before installing the application (more information provided below)
  8. The application version to check and see if it exists before installing the application (more information provided below)
  9. Determine whether the target computer is 32-bit or 64-bit

There are several other switches available for the InstallSoftwareRemotely.ps1 but the ones mentioned above or the ones I typically use.  Please refer to the TechNet article for more information about the other ones available.

-----------------------------------------------------------------------------------------------------------------------------

Determining the application name and application version to check before installing

One of the things you can do with the InstallSoftwareRemotely.ps1 PS script is to have it check to see if the software you’re attempting to install already exists on the target computer.  The information required can be found via the following registry path:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{IdentifyingNumber}

DisplayName

DisplayVersion

Here is an example of where the VMware Horizon View Agent 6.2.1.3284564 can be found:

Note that you can also use PowerShell to required information as described here:

Use PowerShell to Find and Uninstall Software
https://blogs.technet.microsoft.com/heyscriptingguy/2011/12/14/use-powershell-to-find-and-uninstall-software/

----------------------------------------------------------------------------------------------------------------------------

Using a CSV for the target computers

Below is a screenshot of the format for a CSV file if you choose to use it as a list of the target computers:

Modification to the PS Script (for VMware Horizon View Agent only)

The slight modification I made to the PS script includes a restart command that executes when the installation of the application is completed.  The reason why chose to suppress the reboot with the switch that the VMware Horizon View Agent accepts is because allow the agent installer to restart the target computer would interrupt the InstallSoftwareRemotely.ps1 script before it completes thus causing errors to be thrown.  This should be applied to any applications that automatically restart upon completing its install.

Simply insert the Restart-Computer -ComputerName $Computer as shown in the screenshot below to achieve this:

The PowerShell script and required parameters

The following is the final PS script and its required parameters for the VMware Horizon View Agent install:

.\InstallSoftwareRemotely.ps1 -AppPath '\\fp09\tech\Software\VMware\VMware Horizon View\VMware Horizon View 6.2.1\Agent\VMware-viewagent-x86_64-6.2.1-3284564.exe' -AppArgs '/S /V"/qn REBOOT=Reallysuppress' -CSV 'C:\Scripts\VDIs.csv' -Retries 2 -AppName 'VMware Horizon View Agent' -AppVersion '6.2.1.3284564' -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' -EnablePSRemoting -Credential

Note that the:

WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

… is to determine whether the target computer is a 32-bit or 64-bit Windows OS.  This is necessary because the agent we’re installing is for a 64-bit OS.

The resulting output of the script should look similar to the screenshot below:

Note that I am unable to determine what causes the error code 1641 during the VMware Horizon View Agent install even though it is a successful as this does not happen to other applications I’ve used this PS script with so be aware of the following:

  • 1641 is success
  • 1603 is failure

-----------------------------------------------------------------------------------------------------------------------------

InstallSoftwareRemotely.ps1 Script with modification

Below is a copy and paste of the actual script I used for this demonstration.  Be advised that the script would likely receive updates and changes in the future.

<#

.SYNOPSIS

Install software remotely in a group of computers and retry the installation in case of error.

.DESCRIPTION

This script install software remotely in a group of computers and retry the installation in case of error.

It uses PowerShell to perform the installation. Target computer must allow Windows PowerShell Remoting.

Script can try to enable Windows PowerShell Remoting using Microsoft Sysinternals Psexec with the paramenter -EnablePSRemoting.

If PSExec is not found on computer, script asks to the user for download it and extract in system folder.

.PARAMETER AppPath

Path to the application executable, It can be a network or local path because entire folder will be copied to remote computer before installing and deleted after installation.

Example: 'C:\Software\TeamViewer\TeamvieverHost.msi' (Folder TeamViewer will be copied to remote computer before run ejecutable)

.PARAMETER AppArgs

Application arguments to perform silent installation.

Example: '/S /R settings.reg'

.PARAMETER LocalPath

Local path of the remote computer where copy application directory.

Default: 'C:\temp'

.PARAMETER Retries

Number of times to retry failed installations.

Default: 5

.PARAMETER TimeBetweenRetries

Seconds to wait before retrying failed installations.

Default: 60

.PARAMETER ComputerList

List of computers in install software. You can only use one source of target computers: ComputerList, OU or CSV.

Example: Computer001,Computer002,Computer003 (Without quotation marks)

.PARAMETER OU

OU containing computers in which install software.

RSAT for AD module for PowerShell must be installed in order to query AD.

If you run script from a Domain Controller, AD module for PowerShell is already enabled.

You can only use one source of target computers: ComputerList, OU or CSV.

Example: 'OU=Test,OU=Computers,DC=CONTOSO,DC=COM'

.PARAMETER CSV

CSV file containing computers in which install software. You can only use one source of target computers: ComputerList, OU or CSV.

Example: 'C:\Scripts\Computers.csv'

CSV Format:

Name

Computer001

Computer002

Computer003

.PARAMETER LogPath

Path where save log file.

Default: My Documents

.PARAMETER Credential

Script will ask for an account to perform remote installation.

.PARAMETER EnablePSRemoting

Try to enable PSRemoting on failed computers using Psexec. Psexec has to be on system path.

If PSExec is not found. Script ask to download automatically PSTools and copy them to C:\Windows\System32.

.PARAMETER AppName

App name as shown in registry to check if app is installed on remote computer and not reinstall it.

You can check app name on a computer with it installed looking at:

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'

Example: 'TightVNC'

Default: None

.PARAMETER AppVersion

App name as shown in registry to check if app is installed on remote computer and not reinstall it.

If not specified and AppName has a value, version will be ignored.

You can check app version on a computer with it installed looking at:

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'

Example: '2.0.8.1'

Default: all

.PARAMETER WMIQuery

WMI Query to execute in remote computers. Software will be installed if query returns values.

Example: 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' (64 bit computers)

Example: 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="32"' (32 bit computers)

Default: None

.EXAMPLE

TightVNC -> .\InstallSoftwareRemotely.ps1 -AppPath 'C:\Scripts\TightVNC\tightvnc-2.8.8-gpl-setup-64bit.msi' -AppArgs '/quiet /norestart ADDLOCAL="Server" SERVER_REGISTER_AS_SERVICE=1 SERVER_ADD_FIREWALL_EXCEPTION=1 SERVER_ALLOW_SAS=1 SET_USEVNCAUTHENTICATION=1 VALUE_OF_USEVNCAUTHENTICATION=1 SET_PASSWORD=1 VALUE_OF_PASSWORD=Password.01 SET_USECONTROLAUTHENTICATION=1 VALUE_OF_USECONTROLAUTHENTICATION=1 SET_CONTROLPASSWORD=1 VALUE_OF_CONTROLPASSWORD=3digits.01' -OU 'OU=Central,OU=Computers,DC=Contoso,DC=local' -Retries 2 -AppName 'TightVNC' -AppVersion '2.8.8.0' -EnablePSRemoting -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

.EXAMPLE

TightVNC Mirage Driver -> .\InstallSoftwareRemotely.ps1 -AppPath 'C:\Scripts\TightVNC\dfmirage-setup-2.0.301.exe' -AppArgs '/verysilent /norestart' -OU 'OU=Central,OU=Computers,OU=MyBusiness,DC=Contoso,DC=local' -Retries 2 -AppName 'DemoForge Mirage Driver for TightVNC 2.0' -AppVersion '2.0' -EnablePSRemoting -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "C:\Temp\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -ComputerList Computer001,Computer002,Computer003 -AppName "Miranda IM 0.10.75" -AppVersion "0.10.75"

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "C:\Temp\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -CSV "C:\Computers.csv" -Credential -EnablePSRemoting

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "\\Server01\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -OU "OU=Test,OU=Computers,DC=CONTOSO,DC=COM"

.NOTES

Author: Juan Granados

Date: November 2017

#>

Param(

[Parameter(Mandatory=$true,Position=0)]

[ValidateNotNullOrEmpty()]

[string]$AppPath,

[Parameter(Mandatory=$false,Position=1)]

[ValidateNotNullOrEmpty()]

[string]$AppArgs="None",

[Parameter(Mandatory=$false,Position=2)]

[ValidateNotNullOrEmpty()]

[string]$LocalPath="C:\temp",

[Parameter(Mandatory=$false,Position=3)]

[ValidateNotNullOrEmpty()]

[int]$Retries=5,

[Parameter(Mandatory=$false,Position=4)]

[ValidateNotNullOrEmpty()]

[int]$TimeBetweenRetries=60,

[Parameter(Mandatory=$false,Position=5)]

[ValidateNotNullOrEmpty()]

[string[]]$ComputerList,

[Parameter(Mandatory=$false,Position=6)]

[ValidateNotNullOrEmpty()]

[string]$OU,

[Parameter(Mandatory=$false,Position=7)]

[ValidateNotNullOrEmpty()]

[string]$CSV,

[Parameter(Mandatory=$false,Position=7)]

[ValidateNotNullOrEmpty()]

[string]$LogPath=[Environment]::GetFolderPath("MyDocuments"),

[Parameter(Position=9)]

[switch]$EnablePSRemoting,

[Parameter(Position=10)]

[switch]$Credential,

[Parameter(Mandatory=$false,Position=11)]

[ValidateNotNullOrEmpty()]

[string]$AppName="None",

[Parameter(Mandatory=$false,Position=12)]

[ValidateNotNullOrEmpty()]

[string]$AppVersion="all",

[Parameter(Mandatory=$false,Position=13)]

[ValidateNotNullOrEmpty()]

[string]$WMIQuery="None"

)

#Functions

Add-Type -AssemblyName System.IO.Compression.FileSystem

function Unzip

{

param([string]$zipfile, [string]$outpath)

[System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)

}

Function Copy-WithProgress

{

Param([string]$Source,[string]$Destination)

$Source=$Source.tolower()

$Filelist=Get-Childitem $Source –Recurse

$Total=$Filelist.count

$Position=0

If(!(Test-Path $Destination)){

New-Item $Destination -Type Directory | Out-Null

}

foreach ($File in $Filelist){

$Filename=$File.Fullname.tolower().replace($Source,'')

$DestinationFile=($Destination+$Filename)

try{

Copy-Item $File.FullName -Destination $DestinationFile -Force

}catch{throw $_.Exception}

$Position++

Write-Progress -Activity "Copying data from $source to $Destination" -Status "Copying File $Filename" -PercentComplete (($Position/$Total)*100)

}

}

Function Set-Message([string]$Text,[string]$ForegroundColor="White",[int]$Append=$True){

if ($Append){

$Text | Out-File $LogPath -Append

}

else {

$Text | Out-File $LogPath

}

Write-Host $Text -ForegroundColor $ForegroundColor

}

function Get-InstalledApps

{

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

#$result = Get-InstalledApps | where {$_.DisplayName -like $appToMatch}

}

Function CheckSoftwareInstalled([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$AppName = $args[0]

$AppVersion = $args[1]

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

$InstalledApps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

If ($AppVersion -ne "all"){

Return $InstalledApps | where {$_.DisplayName -eq $AppName -and $_.DisplayVersion -eq $AppVersion}

}

Else{

Return $InstalledApps | where {$_.DisplayName -eq $AppName}

}

} -ArgumentList $AppName, $AppVersion -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$AppName = $args[0]

$AppVersion = $args[1]

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

$InstalledApps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

If ($AppVersion -ne "all"){

Return $InstalledApps | where {$_.DisplayName -eq $AppName -and $_.DisplayVersion -eq $AppVersion}

}

Else{

Return $InstalledApps | where {$_.DisplayName -eq $AppName}

}

} -ArgumentList $AppName, $AppVersion

}catch{throw $_.Exception}

}

}

Function CheckWMIQuery([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$WMIQuery = $args[0]

Write-Host "Executing $($WMIQuery)"

Return gwmi -Query $WMIQuery

} -ArgumentList $WMIQuery -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$WMIQuery = $args[0]

Write-Host "Executing $($WMIQuery)"

Return gwmi -Query $WMIQuery

} -ArgumentList $WMIQuery

}catch{throw $_.Exception}

}

}

Function InstallRemoteSoftware([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$Application = $args[0]

$AppArgs = $args[1]

$ApplicationName = $Application.Substring($Application.LastIndexOf('\')+1)

$ApplicationFolderPath = $Application.Substring(0,$Application.LastIndexOf('\'))

$ApplicationExt = $Application.Substring($Application.LastIndexOf('.')+1)

Write-Host "Installing $($ApplicationName) on $($env:COMPUTERNAME)"

If($ApplicationExt -eq "msi"){

If ($AppArgs -ne "None"){

Write-Host "Installing as MSI: msiexec /i $($Application) $($AppArgs)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) $($AppArgs)" -Wait -Passthru

}

else{

Write-Host "Installing as MSI: msiexec /i $($Application)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) /quiet /norestart" -Wait -Passthru

}

}

ElseIf ($AppArgs -ne "None"){

Write-Host "Executing $Application $AppArgs"

$p = Start-Process $Application -ArgumentList $AppArgs -Wait -Passthru

}

Else{

Write-Host "Executing $Application"

$p = Start-Process $Application -Wait -Passthru

}

$p.WaitForExit()

if ($p.ExitCode -ne 0) {

Write-Host "Failed installing with error code $($p.ExitCode)" -ForegroundColor Red

$Return = $($env:COMPUTERNAME)

}

else{

$Return = 0

}

Write-Host "Deleting $($ApplicationFolderPath)"

Remove-Item $($ApplicationFolderPath) -Force -Recurse

Return $Return

} -ArgumentList "$($LocalPath)\$($ApplicationFolderName)\$($ApplicationName)", $AppArgs -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$Application = $args[0]

$AppArgs = $args[1]

$ApplicationName = $Application.Substring($Application.LastIndexOf('\')+1)

$ApplicationFolderPath = $Application.Substring(0,$Application.LastIndexOf('\'))

$ApplicationExt = $Application.Substring($Application.LastIndexOf('.')+1)

Write-Host "Installing $($ApplicationName) on $($env:COMPUTERNAME)"

If($ApplicationExt -eq "msi"){

If ($AppArgs -ne "None"){

Write-Host "Installing as MSI: msiexec /i $($Application) $($AppArgs)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) $($AppArgs)" -Wait -Passthru

}

else{

Write-Host "Installing as MSI: msiexec /i $($Application)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) /quiet /norestart" -Wait -Passthru

}

}

ElseIf ($AppArgs -ne "None"){

Write-Host "Executing $Application $AppArgs"

$p = Start-Process $Application -ArgumentList $AppArgs -Wait -Passthru

}

Else{

Write-Host "Executing $Application"

$p = Start-Process $Application -Wait -Passthru

}

$p.WaitForExit()

if ($p.ExitCode -ne 0) {

Write-Host "Failed installing with error code $($p.ExitCode)" -ForegroundColor Red

$Return = $($env:COMPUTERNAME)

}

else{

$Return = 0

}

Write-Host "Deleting $($ApplicationFolderPath)"

Remove-Item $($ApplicationFolderPath) -Force -Recurse

Return $Return

} -ArgumentList "$($LocalPath)\$($ApplicationFolderName)\$($ApplicationName)", $AppArgs

}catch{throw $_.Exception}

}

}

Function CheckPSRemoting([string]$Computer){

If ($EnablePSRemoting){

Set-Message "Enabling PSRemoting on computer: psexec.exe /accepteula -h -d \\$($Computer) -s powershell Enable-PSRemoting"

try{

psexec.exe /accepteula -h -d "\\$($Computer)" -s powershell Enable-PSRemoting -Force 2>&1 | Out-Null

}catch{

Set-Message "PSExec running on background. Continue with next computer."

}

}

Else{

Set-Message "You can try to enable PowerShell Remoting on computer using parameter -EnablePSRemoting" -ForegroundColor DarkYellow

}

}

$ErrorActionPreference = "Stop"

#Initialice log

$LogPath += "\InstallSoftwareRemotely_" + $(get-date -Format "yyyy-mm-dd_hh-mm-ss") + ".txt"

Set-Message "Start remote installation on $(get-date -Format "yyyy-mm-dd hh:mm:ss")" -Append $False

#Initial validations.

If (!(Test-Path $AppPath)){

Set-Message "Error accessing $($AppPath). The script can not continue"

Exit 1

}

If ($EnablePSRemoting){

if (!(Get-Command "psexec.exe" -ErrorAction SilentlyContinue)){

Set-Message "Error. Microsoft Psexec not found on system. Download it from https://download.sysinternals.com/files/PSTools.zip and extract all in C:\Windows\System32" -ForegroundColor Yellow

$Answer=Read-Host "Do you want to download and install PSTools (y/n)?"

if (($Answer -eq "y") -or ($Answer -eq "Y")){

Set-Message "Downloading PSTools"

If (Test-Path "$($env:temp)\PSTools.zip"){

Remove-Item "$($env:temp)\PSTools.zip" -Force

}

(New-Object System.Net.WebClient).DownloadFile("https://download.sysinternals.com/files/PSTools.zip", "$($env:temp)\PSTools.zip")

if (Test-Path "$($env:temp)\PSTools.zip"){

Set-Message "Unzipping PSTools"

If (Test-Path "$($env:temp)\PSTools"){

Remove-Item "$($env:temp)\PSTools" -Force -Recurse

}

Unzip "$($env:temp)\PSTools.zip""$($env:temp)\PSTools"

Copy-Item "$($env:temp)\PSTools\*.exe""$($env:SystemRoot)\System32" -Force

if (Test-Path "$($env:SystemRoot)\System32\psexec.exe"){

Set-Message "PSTools installed" -ForegroundColor Green

}

else{

Set-Message "Error unzipping PSTools" -ForegroundColor Red

Remove-Item "$($env:temp)\PSTools.zip" -Force

Exit 1

}

}

else{

Set-Message "Error downloading PSTools" -ForegroundColor Red

Exit 1

}

}

else{

Exit 1

}

}

}

If ($OU){

if (!(Get-Command "Get-ADComputer" -ErrorAction SilentlyContinue)){

Set-Message "Error. Get-ADComputer not found on system. You have to install the PowerShell Active Directory module order to query Active Directory. https://4sysops.com/archives/how-to-install-the-powershell-active-directory-module/" -ForegroundColor Red

Exit 1

}

try{

$ComputerList = Get-ADComputer -Filter * -SearchBase "$OU" | Select-Object -Expand name

}catch{

Set-Message "Error querying AD: $($_.Exception.Message)" -ForegroundColor Red

Exit 1

}

}

ElseIf ($CSV){

try{

$ComputerList = Get-Content $CSV | where {$_ -notmatch 'Name'} | Foreach-Object {$_ -replace '"', ''}

}catch{

Set-Message "Error getting CSV content: $($_.Exception.Message)" -ForegroundColor Red

Exit 1

}

}

ElseIf(!$ComputerList){

Set-Message "You have to set a list of computers, OU or CSV." -ForegroundColor Red

Exit 1

}

If ($Credential){

$Cred = Get-Credential

}

If(!$Cred -or !$Credential){

Set-Message "No credential specified. Using logon account"

}

Else{

Set-Message "Using user $($Cred.UserName)"

}

$ApplicationName = $AppPath.Substring($AppPath.LastIndexOf('\')+1)

$ApplicationFolderPath = $AppPath.Substring(0,$AppPath.LastIndexOf('\'))

$ApplicationFolderName = $ApplicationFolderPath.Substring($ApplicationFolderPath.LastIndexOf('\')+1)

$ComputerWithError = [System.Collections.ArrayList]@()

$ComputerWithSuccess = [System.Collections.ArrayList]@()

$ComputerSkipped = [System.Collections.ArrayList]@()

$TotalRetries = $Retries

$TotalComputers = $ComputerList.Count

Do{

Set-Message "-----------------------------------------------------------------"

Set-Message "Attempt $(($TotalRetries - $Retries) +1) of $($TotalRetries)" -ForegroundColor Cyan

Set-Message "-----------------------------------------------------------------"

$Count = 1

ForEach ($Computer in $ComputerList){

Set-Message "COMPUTER $($Computer.ToUpper()) ($($Count) of $($ComputerList.Count))" -ForegroundColor Yellow

$Count++

If($AppName -ne "None"){

Set-Message "Checking if $($AppName) version $($AppVersion) is installed on remote computer."

try{

If(CheckSoftwareInstalled $Computer){

Set-Message "Software found on computer. Skipping installation." -ForegroundColor Green

$ComputerSkipped.Add($Computer) | Out-Null

Continue

}

Else{

Set-Message "Software not found on remote computer."

}

}catch{

Set-Message "Error connecting: $($_.Exception.Message)" -ForegroundColor Red

CheckPSRemoting $Computer

$ComputerWithError.Add($Computer) | Out-Null

Continue

}

}

If($WMIQuery -ne "None"){

Set-Message "Checking WMI Query on remote computer."

try{

If(!(CheckWMIQuery $Computer)){

Set-Message "WMI Query result is false. Skipping installation."

$ComputerSkipped.Add($Computer) | Out-Null

Continue

}

Else{

Set-Message "WMI Query result is true. Continue installation."

}

}catch{

Set-Message "Error connecting: $($_.Exception.Message)" -ForegroundColor Red

CheckPSRemoting $Computer

$ComputerWithError.Add($Computer) | Out-Null

Continue

}

}

Set-Message "Coping $($ApplicationFolderPath) to \\$($Computer)\$($LocalPath -replace ':','$')"

try{

Copy-WithProgress "$ApplicationFolderPath""\\$($Computer)\$("$($LocalPath)\$($ApplicationFolderName)" -replace ':','$')"

}catch{

Set-Message "Error copying folder: $($_.Exception.Message)" -ForegroundColor Red

$ComputerWithError.Add($Computer) | Out-Null

Continue;

}

try{

$ExitCode = InstallRemoteSoftware $Computer

If ($ExitCode){

$ComputerWithError.Add($Computer) | Out-Null

Set-Message "Error installing $($ApplicationName)." -ForegroundColor Red

}

else{

Set-Message "$($ApplicationName) installed successfully." -ForegroundColor Green

$ComputerWithSuccess.Add($Computer) | Out-Null

}

}catch{

Set-Message "Error on remote execution: $($_.Exception.Message)" -ForegroundColor Red

$ComputerWithError.Add($Computer) | Out-Null

try{

Set-Message "Deleting \\$($Computer)\$($LocalPath -replace ':','$')\$($ApplicationFolderName)"

}catch{

Set-Message "Error on remote deletion: $($_.Exception.Message)" -ForegroundColor Red

}

Remove-Item "\\$($Computer)\$($LocalPath -replace ':','$')\$($ApplicationFolderName)" -Force -Recurse

CheckPSRemoting $Computer

}

}

If ($ComputerWithError.Count -eq 0){

break

}

$Retries--

If ($Retries -gt 0){

$ComputerList=$ComputerWithError

$ComputerWithError = [System.Collections.ArrayList]@()

If ($TimeBetweenRetries -gt 0){

Set-Message "Waiting $($TimeBetweenRetries) seconds before next retry..."

Sleep $TimeBetweenRetries

}

}

}While ($Retries -gt 0)

If($ComputerWithError.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "Error installing $($ApplicationName) on $($ComputerWithError.Count) of $($TotalComputers) computers:"

Set-Message $ComputerWithError

$csvContents = @()

ForEach($Computer in $ComputerWithError){

$row = New-Object System.Object

$row | Add-Member -MemberType NoteProperty -Name "Name" -Value $Computer

$csvContents += $row

}

$CSV=(get-date).ToString('yyyyMMdd-HH_mm_ss') + "ComputerWithError.csv"

$csvContents | Export-CSV -notype -Path "$([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -Encoding UTF8

Set-Message "Computers with error exported to CSV file: $([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -ForegroundColor DarkYellow

Set-Message "You can retry failed installation on this computers using parameter -CSV $([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -ForegroundColor DarkYellow

}

If ($ComputerWithSuccess.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "$([math]::Round((($ComputerWithSuccess.Count * 100) / $TotalComputers), [System.MidpointRounding]::AwayFromZero) )% Success installing $($ApplicationName) on $($ComputerWithSuccess.Count) of $($TotalComputers) computers:"

Set-Message $ComputerWithSuccess

}

Else{

Set-Message "-----------------------------------------------------------------"

Set-Message "Installation of $($ApplicationName) failed on all computers" -ForegroundColor Red

}

If ($ComputerSkipped.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "$($ComputerSkipped.Count) skipped of $($TotalComputers) computers:"

Set-Message $ComputerSkipped

}

Mapping drives and printers for Citrix XenApp / XenDesktop published applications with batch and VBS scripts based on AD group membership

$
0
0

One of the more frequent questions I’ve been asked for Citrix XenApp / XenDesktop published applications is whether there is a way to map network drives and printers based on AD group membership for applications that are published via a batch file. There are several ways to do this but if you want to specifically control the mappings from within a VBS script then below is an example of how to do this.

The first step in the process is to create two files:

  1. A batch file that the published application within Citrix XenApp / XenDesktop will execute to launch the application
  2. A VBS script that will map the respective network drives and printers based on group membership

For this example, the batch file will be named MapLaunch.bat and the vbs script that will control the network drives and printers mapping will be MapDrivesPrinters.vbs as shown below:

The contents of the batch file MapLaunch.bat will be simple as it simply calls the VBS script to map the network drives and printers, followed by starting the Citrix application defined in the Command line argument (option): field in the Application Settings:

cscript C:\Scripts\MapDrivesPrinters.vbs

start "Citrix" %*

timeout /T 1 /nobreak

The next step is to create a vbs script that would map the network drives and printers based on AD membership. There are plenty of scripts available and one of the scripts I’ve used in the past is by Richard L. Mueller and can be found here: https://www.rlmueller.net/Logon6.htm

The script is fairly straight forward to use but I’ll paste a sample one below with the customizations highlighted in red.

' Logon6.vbs
' VBScript logon script program.
'
' ----------------------------------------------------------------------
' Copyright (c) 2004-2010 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net
' Version 1.0 - March 28, 2004
' Version 1.1 - July 30, 2007 - Escape any "/" characters in DN's.
' Version 1.2 - November 6, 2010 - No need to set objects to Nothing.
'
' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.

Option Explicit

Dim objRootDSE, objTrans, strNetBIOSDomain, objNetwork, strNTName
Dim strUserDN, strComputerDN, objGroupList, objUser, strDNSDomain
Dim strComputer, objComputer
Dim strHomeDrive, strHomeShare
Dim adoCommand, adoConnection, strBase, strAttributes

' Constants for the NameTranslate object.
Const ADS_NAME_INITTYPE_GC = 3
Const ADS_NAME_TYPE_NT4 = 3
Const ADS_NAME_TYPE_1779 = 1

Set objNetwork = CreateObject("Wscript.Network")

' Loop required for Win9x clients during logon.
strNTName = ""
On Error Resume Next
Do While strNTName = ""
     strNTName = objNetwork.UserName
     Err.Clear
     If (Wscript.Version > 5) Then
         Wscript.Sleep 100
     End If
Loop
On Error GoTo 0

' Determine DNS domain name from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use the NameTranslate object to find the NetBIOS domain name from the
' DNS domain name.
Set objTrans = CreateObject("NameTranslate")
objTrans.Init ADS_NAME_INITTYPE_GC, ""
objTrans.Set ADS_NAME_TYPE_1779, strDNSDomain
strNetBIOSDomain = objTrans.Get(ADS_NAME_TYPE_NT4)
' Remove trailing backslash.
strNetBIOSDomain = Left(strNetBIOSDomain, Len(strNetBIOSDomain) - 1)

' Use the NameTranslate object to convert the NT user name to the
' Distinguished Name required for the LDAP provider.
objTrans.Set ADS_NAME_TYPE_NT4, strNetBIOSDomain & "\"& strNTName
strUserDN = objTrans.Get(ADS_NAME_TYPE_1779)
' Escape any forward slash characters, "/", with the backslash
' escape character. All other characters that should be escaped are.
strUserDN = Replace(strUserDN, "/", "\/")

' Bind to the user object in Active Directory with the LDAP provider.
Set objUser = GetObject("LDAP://"& strUserDN)

' Map a network drive if the user is a member of the group.
If (IsMember(objUser, "Contoso_Services") = True) Then
     On Error Resume Next
     objNetwork.MapNetworkDrive "G:", "\\fileserver\conto"
     If (Err.Number <> 0) Then
         On Error GoTo 0
         objNetwork.RemoveNetworkDrive "G:", True, True
         objNetwork.MapNetworkDrive "G:", "\\fileserver\conto"
     End If
     On Error GoTo 0
End If

' Map a network drive if the user is a member of the group.
If (IsMember(objUser, "HPC_Users") = True) Then
     On Error Resume Next
     objNetwork.MapNetworkDrive "R:", "\\HPCServer\HPC_WorkArea$"
     If (Err.Number <> 0) Then
         On Error GoTo 0
         objNetwork.RemoveNetworkDrive "R:", True, True
         objNetwork.MapNetworkDrive "R:", "\\HPCServer\HPC_WorkArea$"
     End If
     On Error GoTo 0
End If

' Map a network drive if the user is a member of the group.
If (IsMember(objUser, "Domain Users") = True) Then
     On Error Resume Next
     objNetwork.MapNetworkDrive "S:", "\\fileserver\s_root$"
     If (Err.Number <> 0) Then
         On Error GoTo 0
         objNetwork.RemoveNetworkDrive "S:", True, True
         objNetwork.MapNetworkDrive "S:", "\\fileserver\s_root$"
     End If
     On Error GoTo 0
End If

' Map a network drive if the user is a member of the group.
If (IsMember(objUser, "SecureClaims") = True) Then
     On Error Resume Next
     objNetwork.MapNetworkDrive "T:", "\\fileserver\SecureClaimsArea$"
     If (Err.Number <> 0) Then
         On Error GoTo 0
         objNetwork.RemoveNetworkDrive "T:", True, True
         objNetwork.MapNetworkDrive "T:", "\\fileserver\SecureClaimsArea$"
     End If
     On Error GoTo 0
End If

' Map a network drive if the user is a member of the group.
If (IsMember(objUser, "ReInsur_Services") = True) Then
     On Error Resume Next
     objNetwork.MapNetworkDrive "X:", "\\fileserver\ReLife"
     If (Err.Number <> 0) Then
         On Error GoTo 0
         objNetwork.RemoveNetworkDrive "X:", True, True
         objNetwork.MapNetworkDrive "X:", "\\fileserver\ReLife"
     End If

     On Error GoTo 0
End If

' Use the NameTranslate object to convert the NT name of the computer to
' the Distinguished name required for the LDAP provider. Computer names
' must end with "$".
strComputer = objNetwork.computerName
objTrans.Set ADS_NAME_TYPE_NT4, strNetBIOSDomain _
     & "\"& strComputer & "$"
strComputerDN = objTrans.Get(ADS_NAME_TYPE_1779)
' Escape any forward slash characters, "/", with the backslash
' escape character. All other characters that should be escaped are.
strComputerDN = Replace(strComputerDN, "/", "\/")

' Add a printer connection if the user is a member of the group.
If (IsMember(objUser, "Domain Users") = True) Then
     Set objNetwork = CreateObject("WScript.Network")
     objNetwork.AddWindowsPrinterConnection "\\PrintServer\Xerox7855B PCL6"
     objNetwork.AddWindowsPrinterConnection "\\PrintServer\Xerox7855A PCL6"
     objNetwork.AddWindowsPrinterConnection "\\PrintServer\Lexmark Universal v2 PS3"
     objNetwork.SetDefaultPrinter "\\PrintServer\Xerox7855A PCL6"
End If

' Clean up.
If (IsObject(adoConnection) = True) Then
     adoConnection.Close
End If

Function IsMember(ByVal objADObject, ByVal strGroupNTName)
     ' Function to test for group membership.
     ' objADObject is a user or computer object.
     ' strGroupNTName is the NT name (sAMAccountName) of the group to test.
     ' objGroupList is a dictionary object, with global scope.
     ' Returns True if the user or computer is a member of the group.
     ' Subroutine LoadGroups is called once for each different objADObject.

    ' The first time IsMember is called, setup the dictionary object
     ' and objects required for ADO.
     If (IsEmpty(objGroupList) = True) Then
         Set objGroupList = CreateObject("Scripting.Dictionary")
         objGroupList.CompareMode = vbTextCompare

        Set adoCommand = CreateObject("ADODB.Command")
         Set adoConnection = CreateObject("ADODB.Connection")
         adoConnection.Provider = "ADsDSOObject"
         adoConnection.Open "Active Directory Provider"
         adoCommand.ActiveConnection = adoConnection

        Set objRootDSE = GetObject("LDAP://RootDSE")
         strDNSDomain = objRootDSE.Get("defaultNamingContext")

        adoCommand.Properties("Page Size") = 100
         adoCommand.Properties("Timeout") = 30
         adoCommand.Properties("Cache Results") = False

        ' Search entire domain.
         strBase = "<LDAP://"& strDNSDomain & ">"
         ' Retrieve NT name of each group.
         strAttributes = "sAMAccountName"

        ' Load group memberships for this user or computer into dictionary
         ' object.
         Call LoadGroups(objADObject)
     End If
     If (objGroupList.Exists(objADObject.sAMAccountName & "\") = False) Then
         ' Dictionary object established, but group memberships for this
         ' user or computer must be added.
         Call LoadGroups(objADObject)
     End If
     ' Return True if this user or computer is a member of the group.
     IsMember = objGroupList.Exists(objADObject.sAMAccountName & "\" _
         & strGroupNTName)
End Function

Sub LoadGroups(ByVal objADObject)
     ' Subroutine to populate dictionary object with group memberships.
     ' objGroupList is a dictionary object, with global scope. It keeps track
     ' of group memberships for each user or computer separately. ADO is used
     ' to retrieve the name of the group corresponding to each objectSid in
     ' the tokenGroup array. Based on an idea by Joe Kaplan.

    Dim arrbytGroups, k, strFilter, adoRecordset, strGroupName, strQuery

    ' Add user name to dictionary object, so LoadGroups need only be
     ' called once for each user or computer.
     objGroupList.Add objADObject.sAMAccountName & "\", True

    ' Retrieve tokenGroups array, a calculated attribute.
     objADObject.GetInfoEx Array("tokenGroups"), 0
     arrbytGroups = objADObject.Get("tokenGroups")

    ' Create a filter to search for groups with objectSid equal to each
     ' value in tokenGroups array.
     strFilter = "(|"
     If (TypeName(arrbytGroups) = "Byte()") Then
         ' tokenGroups has one entry.
         strFilter = strFilter & "(objectSid=" _
             & OctetToHexStr(arrbytGroups) & ")"
     ElseIf (UBound(arrbytGroups) > -1) Then
         ' TokenGroups is an array of two or more objectSid's.
         For k = 0 To UBound(arrbytGroups)
             strFilter = strFilter & "(objectSid=" _
                 & OctetToHexStr(arrbytGroups(k)) & ")"
         Next
     Else
         ' tokenGroups has no objectSid's.
         Exit Sub
     End If
     strFilter = strFilter & ")"

    ' Use ADO to search for groups whose objectSid matches any of the
     ' tokenGroups values for this user or computer.
     strQuery = strBase & ";"& strFilter & ";" _
         & strAttributes & ";subtree"
     adoCommand.CommandText = strQuery
     Set adoRecordset = adoCommand.Execute

    ' Enumerate groups and add NT name to dictionary object.
     Do Until adoRecordset.EOF
         strGroupName = adoRecordset.Fields("sAMAccountName").Value
         objGroupList.Add objADObject.sAMAccountName & "\" _
             & strGroupName, True
         adoRecordset.MoveNext
     Loop
     adoRecordset.Close

End Sub

Function OctetToHexStr(ByVal arrbytOctet)
     ' Function to convert OctetString (byte array) to Hex string,
     ' with bytes delimited by \ for an ADO filter.

    Dim k
     OctetToHexStr = ""
     For k = 1 To Lenb(arrbytOctet)
         OctetToHexStr = OctetToHexStr & "\" _
             & Right("0"& Hex(Ascb(Midb(arrbytOctet, k, 1))), 2)
     Next
End Function

Attempting to use VMware vSphere PowerCLI's Connect-VIServer to a vCenter instance fails with: "The underlying connection was closed: An unexpected error occurred on a send."

$
0
0

Problem

You have VMware vSphere PowerCLI version 5.1.0.4977 installed:

You attempt to connect to a vSphere vCenter 6.7:

… using the Connect-VIServer cmdlet but it immediately fails with:

PowerCLI C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI> Connect-VIServer vCenter.contoso.local

Connect-VIServer : 2/22/2019 11:41:37 AM Connect-VIServer The underlying connection was closed: An unexpected error

occurred on a send.

At line:1 char:1

+ Connect-VIServer vCenter.contoso.local

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [Connect-VIServer], ViError

+ FullyQualifiedErrorId : Client20_ConnectivityServiceImpl_Reconnect_WebException,VMware.VimAutomation.ViCore.Cmdl

ets.Commands.ConnectVIServer

PowerCLI C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI>

Solution

The reason why this error is thrown is because vCenter 6.7 only has TLS 1.2 enabled while TLS 1.0 and 1.1 is disabled by default so the older PowerCLI version installed, which attempts to connect via a one of the lower TLS versions will fail. The proper method of resolving this issue is to upgrade the old PowerCLI version to the latest one with the cmdlet Install-Module -Name VMware.PowerCLI as shown in the following PowerShell Gallery:

https://www.powershellgallery.com/packages/VMware.PowerCLI/11.1.0.11289667

The alternate solution is to force the .NET to use the appropriate TLS version for connecting to the vCenter:

Enabling the TLSv1.1 and TLSv1.2 protocols for PowerCLI (2137109)
https://kb.vmware.com/s/article/2137109

· For 32-bit processes, change the following registry key value to 1.

Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\[.NET_version]
Value: SchUseStrongCrypto (DWORD)

· For 64-bit processes, in addition to the above registry key, change the following registry key value to 1.

Key: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\[.NET_version]
Value: SchUseStrongCrypto (DWORD)

This workaround was handy for the project where the environment did not allow me to download or install any binaries on the endpoint I was using but I did find that the workaround with vSphere PowerCLI 5.1.0.4977 would only work with the 32-Bit version:

Adding the two registry keys in did not allow the 64-Bit version to connect.

Additional information - Invalid server certificate

Note that if you attempt to use the IP address of the vCenter to connect then you will receive the following error indicating that the certificate being presented is invalid:

PS C:\users\tluk\Downloads\vCheck-vSphere-master\vCheck-vSphere-master> Connect-VIServer 10.10.10.24

Connect-VIServer : 2/22/2019 3:47:15 PM Connect-VIServer Error: Invalid server certificate. Use

Set-PowerCLIConfiguration to set the value for the InvalidCertificateAction option to Prompt if you'd like to connect

once or to add a permanent exception for this server.

Additional Information: Could not establish trust relationship for the SSL/TLS secure channel with authority

'10.10.10.24'.

At line:1 char:1

+ Connect-VIServer 10.10.10.24

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : SecurityError: (:) [Connect-VIServer], ViSecurityNegotiationException

+ FullyQualifiedErrorId : Client20_ConnectivityServiceImpl_Reconnect_CertificateError,VMware.VimAutomation.ViCore.

Cmdlets.Commands.ConnectVIServer

It is best practice to have a trusted certificate installed and to connect with the FQDN of the vCenter but if you do not meet either of the criteria then you can configure PowerCLI to ignore the certificate error with the following cmdlet:

Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false

Scope ProxyPolicy DefaultVIServerMode InvalidCertificateAction DisplayDeprecationWarnings WebOperationTimeout

Seconds

----- ----------- ------------------- ------------------------ -------------------------- -------------------

Session UseSystemProxy Multiple Ignore True 300

User Ignore

AllUsers

Alternatively, if you want to install the self-signed certificate on the device that you are initiating the connection from so you can connect via the FQDN, you can use the same instructions I provided in the solution of one of my previous blog posts to download and install the certificate into the trusted store:

Attempting to upload a file onto a datastore with vSphere Client 6.5 fails with: "The operation failed."
http://terenceluk.blogspot.com/2018/11/attempting-to-upload-file-onto.html

Deploying VMware SRM (Site Recovery Manager) 8.1.1 with vSphere vCenter Server 6.5

$
0
0

It has been a while since I’ve deployed SRM (VMware Site Recovery Manager) and since I recently had the opportunity to do so at a client, I took a few screenshots during the process to write this blog post that demonstrates the process.

Prerequisites

Before you begin, make sure you use the VMware Product Interoperability Matrices to verify that the vCenter Server, vSphere Replication Appliance (if you’re using it for replication), and VMware Site Recovery Manger versions are supported:

VMware Product Interoperability Matrices
https://www.vmware.com/resources/compatibility/sim/interop_matrix.php

Deployment

The official deployment guide can be found at the following URL:

Install Site Recovery Manager Server
https://docs.vmware.com/en/Site-Recovery-Manager/8.1/com.vmware.srm.install_config.doc/GUID-723EAC1B-AC21-4CAA-9867-627CA8CB680A.html

Begin by downloading the installation file and then run the install:

Select appropriate drive or destination folder you would like to use:

Enter the information for the PSC (Platform Services Controller) for the site this SRM will be deployed in and an administrative account. Note that the administrator account is going to be used for creating the Site Recovery Manager solution user so you can use any account with administrative privileges:

Accept the PSC’s certificate if it a trusted certificate was not installed:

Select the appropriate vCenter that this SRM will register with:

Enter the appropriate values for the configuration below:

Unless you are going to use a custom Site Recovery Manger Plug-in, proceed with the Default option:

If there is a certificate issued for this deployment then select the second option, otherwise, generate one with the first option:

Fill in the information for the new certificate if one is being generated:

The default option for the Database Server Selection step is to use a custom server with a Data Source already configured. Older SRM version used to only support databases such as Microsoft SQL, Oracle, DB2 (https://pubs.vmware.com/srm-51/index.jsp?topic=%2Fcom.vmware.srm.install_config.doc%2FGUID-84218C7E-3242-4A33-A771-71F944850319.html) so it was a requirement to have a DSN already configured but newer versions of SRM provide the option of using an embedded PostgreSQL database so for the purpose of this demonstration, we’ll use the embedded option:

Provide a Data Source Name which will be used to setup the DSN on the Windows server, a database user name for accessing the database, and a password:

Select whether to use a Local System account or a service account:

Proceed with the install:

Note the following 2 new items after the successful install of SRM:

Also note the new Roles created for SRM in vCenter:

A new user would user with a GUID will also be created:

Clicking on the Site Recovery view should now display the Site Recovery manager component:

There are times when it will be necessary to log off and back on to allow the SRM plugin loads properly so if the above items are not displayed, try relogging on.

Clicking the OPEN Site Recovery button will launch a separate window for the Site Recovery console:

Proceed with deploying the other SRM site and pair them.

Backing up vPostgres Database

Below is a demonstration of how to backup the vPostgres Database.

The VMware official guide can be found here:

Back Up and Restore the Embedded vPostgres Database
https://docs.vmware.com/en/Site-Recovery-Manager/5.8/com.vmware.srm.install_config.doc/GUID-E1FC67CD-48E3-4B25-AA1D-8A2408F5B517.html

All of the vPostgres executables are located in the following directory:

C:\Program Files\VMware\VMware vCenter Site Recovery Manager Embedded Database\bin

The following is the syntax to backup the database:

pg_dump -Fc --host 127.0.0.1 --port port_number --username=db_username srm_db > srm_backup_name

If you had forgotten the parameters used for the deployment, you can retrieve them from the ODBC Data Source Administrator console as shown here:

Note that the install does not allow changing the database name so it will always be srm_db.

The following is an example of the backup command:

pg_dump -Fc --host 127.0.0.1 --port 5678 --username=administrator srm_db > SRM-Backup

A backup should be placed in the same directory as the pg-dump.exe executable:

Installing Exchange Server 2019 fails at Step 1 of 14: Organization Preparation - “…Microsoft.Exchange.Data.Directory.ADObjectAlreadyExistsException”

$
0
0

Problem

You’re installing Microsoft Exchange Server 2019 into an organization with Exchange Server 2013 and will use the installer to run Setup /PrepareAD:

The installer begins at Step 1 of 14: Organization Preparation but fails with the following error:

Error:

The following error was generated when "$error.Clear();

install-AdministrativeGroup -DomainController $RoleDomainController

" was run: "Microsoft.Exchange.Data.Directory.ADObjectAlreadyExistsException: Active Directory operation failed on dc05.contoso.com. The object 'CN=Folder Hierarchies,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=contoso,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com' already exists. ---> System.DirectoryServices.Protocols.DirectoryOperationException: The object exists.

at System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32 messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, Boolean exceptionOnTimeOut)

at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)

at Microsoft.Exchange.Data.Directory.GuardedDirectoryExecution.Execute[T](String bucketName, Func`1 action, Int64& concurrency)

at Microsoft.Exchange.Data.Directory.PooledLdapConnection.GuardedSendRequest(String forestName, GuardedDirectoryExecution guardedDirectoryExecution, DirectoryRequest request, TimeSpan timeout, Func`3 sendRequestDelegate, Int64& concurrency)

at Microsoft.Exchange.Data.Directory.PooledLdapConnection.SendRequest(DirectoryRequest request, LdapOperation ldapOperation, Nullable`1 clientSideSearchTimeout, IADLogContext logContext, Boolean shouldLogLastFilter)

at Microsoft.Exchange.Data.Directory.ADDataSession.ExecuteModificationRequest(ADObject entry, DirectoryRequest request, ADObjectId originalId, Boolean emptyObjectSessionOnException, Boolean isSync)

--- End of inner exception stack trace ---

at Microsoft.Exchange.Data.Directory.ADDataSession.AnalyzeDirectoryError(PooledLdapConnection connection, DirectoryRequest request, DirectoryException de, Int32 totalRetries, Int32 retriesOnServer, String callerFilePath, Int32 callerFileLine, String memberName)

at Microsoft.Exchange.Data.Directory.ADDataSession.ExecuteModificationRequest(ADObject entry, DirectoryRequest request, ADObjectId originalId, Boolean emptyObjectSessionOnException, Boolean isSync)

at Microsoft.Exchange.Data.Directory.ADDataSession.Save(ADObject instanceToSave, IEnumerable`1 properties, Boolean bypassValidation)

at Microsoft.Exchange.Data.Directory.SystemConfiguration.ADConfigurationSession.Save(ADConfigurationObject instanceToSave, String callerFilePath, Int32 callerFileLine, String memberName)

at Microsoft.Exchange.Management.Deployment.InstallAdministrativeGroup.InstallConfigurationObject[TObject](ADObjectId id, Boolean force, TObject instance)

at Microsoft.Exchange.Management.Deployment.InstallAdministrativeGroup.InternalProcessRecord()

at Microsoft.Exchange.Configuration.Tasks.Task.<ProcessRecord>b__91_1()

at Microsoft.Exchange.Configuration.Tasks.Task.InvokeRetryableFunc(String funcName, Action func, Boolean terminatePipelineIfFailed)".

Solution

This has been a common problem with previous Exchange 2013 and 2016 environments where this step could fail during cumulative updates to the organization. The following TechNet Blog describes the issue:

https://blogs.technet.microsoft.com/manjubn/2013/09/02/1-exchange-server-2013-preparead-or-cumultive-update-installation-fails/

The remediation steps in the blog are fairly straight forward but can cause a bit of confusing when you reach these items:

Click More Attributes.

In the Select a class list, select msExchPFTreeType, and then click Next.

In the Edit Attribute box, type 1, click Set, click OK, and then click Finish

The following steps are the ones I took to correct this issue allowing Exchange Server 2019 to install:

Launch ADSIedit and navigate to:

CN=Configuration,CN=Services,CN=Microsoft Exchange,CN=Organization,CN=Administrative Groups,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Folder Hierarchies

Note that there are no objects defined on the right window in the screenshot above.

Right click on either CN=Folder Hierarchies or the blank space on the right window and select New > Object, select msExchPFTree as the class then click Next:

Type in msExchPFTree as the Value then click Next:

Complete the creation of the object by clicking Finish (don’t worry about the More Attributes button):

Note the new CN=msExchPFTree object created:

Open the properties of the object, navigate to the msExchPFTreeType attribute:

Update the <not set> value to 1:

Proceed with the install and the process should move past Step 1:


Configuring a custom shell launcher with VMware Horizon View Client on a Dell Wyse 7020 Windows 10 IoT device

$
0
0

I’ve recently had to assist a client with configuring their Dell Wyse 7020 Windows 10 IoT thin clients with a custom shell launcher mimicking a kiosk type of mode where only the VMware Horizon View Client is available to the user but the local admin account should still have the regular explorer shell.  These thin clients will not be joined to the Active Directory domain in the organization so users should not have to log into the thin client and the thin client OS should never lock.

The Dell Wyse 7020 (Z90QQ10) thin client I was working with is the following:

Prerequisites

You must ensure that the Shell Launcher feature is installed before beginning the configuration and although the OS on this thin client indicates it is Windows 10 Enterprise 2015 LTSB, there are some subtle differences with a full desktop and one of them is that the Shell Launcher is labeled as the following in the Windows Features:

Embedded Shell Launcher

This is different than where you would find the feature on a regular Windows 10 OS:

It is also possible to add the feature with the following command:

Dism /online /Enable-Feature /all /FeatureName:Client-EmbeddedShellLauncher

Configuring the customized shell with a PowerShell Script

Microsoft provides the following 2 articles with a PowerShell script to configure the custom shell:

Shell Launcher
https://docs.microsoft.com/en-us/windows-hardware/customize/enterprise/shell-launcher

Use Shell Launcher to create a Windows 10 kiosk
https://docs.microsoft.com/en-us/windows/configuration/kiosk-shelllauncher

However, those who may not be familiar with the behavior of customizing the shell on a Windows OS may be confused so here is a breakdown of the script and the changes required.

Original Script:

# Check if shell launcher license is enabled

function Check-ShellLauncherLicenseEnabled

{

[string]$source = @"

using System;

using System.Runtime.InteropServices;

static class CheckShellLauncherLicense

{

    const int S_OK = 0;

    public static bool IsShellLauncherLicenseEnabled()

    {

        int enabled = 0;

        if (NativeMethods.SLGetWindowsInformationDWORD("EmbeddedFeature-ShellLauncher-Enabled", out enabled) != S_OK) {

            enabled = 0;

        }

        return (enabled != 0);

    }

    static class NativeMethods

    {

        [DllImport("Slc.dll")]

        internal static extern int SLGetWindowsInformationDWORD([MarshalAs(UnmanagedType.LPWStr)]string valueName, out int value);

    }

}

"@

$type = Add-Type -TypeDefinition $source -PassThru

return $type[0]::IsShellLauncherLicenseEnabled()

}

[bool]$result = $false

$result = Check-ShellLauncherLicenseEnabled

"`nShell Launcher license enabled is set to " + $result

if (-not($result))

{

"`nThis device doesn&#39;t have required license to use Shell Launcher"

exit

}

$COMPUTER = "localhost"

$NAMESPACE = "root\standardcimv2\embedded"

# Create a handle to the class instance so we can call the static methods.

try {

$ShellLauncherClass = [wmiclass]"\\$COMPUTER\${NAMESPACE}:WESL_UserSetting"

    } catch [Exception] {

write-host $_.Exception.Message;

write-host "Make sure Shell Launcher feature is enabled"

exit

    }

# This well-known security identifier (SID) corresponds to the BUILTIN\Administrators group.

$Admins_SID = "S-1-5-32-544"

# Create a function to retrieve the SID for a user account on a machine.

function Get-UsernameSID($AccountName) {

$NTUserObject = New-Object System.Security.Principal.NTAccount($AccountName)

$NTUserSID = $NTUserObject.Translate([System.Security.Principal.SecurityIdentifier])

return $NTUserSID.Value

}

# Get the SID for a user account named "Cashier". Rename "Cashier" to an existing account on your system to test this script.

$Cashier_SID = Get-UsernameSID("Cashier")

# Define actions to take when the shell program exits.

$restart_shell = 0

$restart_device = 1

$shutdown_device = 2

# Examples. You can change these examples to use the program that you want to use as the shell.

# This example sets the command prompt as the default shell, and restarts the device if the command prompt is closed.

$ShellLauncherClass.SetDefaultShell("cmd.exe", $restart_device)

# Display the default shell to verify that it was added correctly.

$DefaultShellObject = $ShellLauncherClass.GetDefaultShell()

"`nDefault Shell is set to " + $DefaultShellObject.Shell + " and the default action is set to " + $DefaultShellObject.defaultaction

# Set Internet Explorer as the shell for "Cashier", and restart the machine if Internet Explorer is closed.

$ShellLauncherClass.SetCustomShell($Cashier_SID, "c:\program files\internet explorer\iexplore.exe www.microsoft.com", ($null), ($null), $restart_shell)

# Set Explorer as the shell for administrators.

$ShellLauncherClass.SetCustomShell($Admins_SID, "explorer.exe")

# View all the custom shells defined.

"`nCurrent settings for custom shells:"

Get-WmiObject -namespace $NAMESPACE -computer $COMPUTER -class WESL_UserSetting | Select Sid, Shell, DefaultAction

# Enable Shell Launcher

$ShellLauncherClass.SetEnabled($TRUE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

# Remove the new custom shells.

$ShellLauncherClass.RemoveCustomShell($Admins_SID)

$ShellLauncherClass.RemoveCustomShell($Cashier_SID)

# Disable Shell Launcher

$ShellLauncherClass.SetEnabled($FALSE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

Breakdown:

Check to see if the Shell Launcher is enabled:

# Check if shell launcher license is enabled

function Check-ShellLauncherLicenseEnabled

{

[string]$source = @"

using System;

using System.Runtime.InteropServices;

static class CheckShellLauncherLicense

{

    const int S_OK = 0;

    public static bool IsShellLauncherLicenseEnabled()

    {

        int enabled = 0;

        if (NativeMethods.SLGetWindowsInformationDWORD("EmbeddedFeature-ShellLauncher-Enabled", out enabled) != S_OK) {

            enabled = 0;

        }

        return (enabled != 0);

    }

    static class NativeMethods

    {

        [DllImport("Slc.dll")]

        internal static extern int SLGetWindowsInformationDWORD([MarshalAs(UnmanagedType.LPWStr)]string valueName, out int value);

    }

}

"@

$type = Add-Type -TypeDefinition $source -PassThru

return $type[0]::IsShellLauncherLicenseEnabled()

}

[bool]$result = $false

$result = Check-ShellLauncherLicenseEnabled

"`nShell Launcher license enabled is set to " + $result

if (-not($result))

{

"`nThis device doesn't have required license to use Shell Launcher"

exit

}

Create a handle to the class instance to call the static methods (pretty much what the comment says):

$COMPUTER = "localhost"

$NAMESPACE = "root\standardcimv2\embedded"

# Create a handle to the class instance so we can call the static methods.

try {

$ShellLauncherClass = [wmiclass]"\\$COMPUTER\${NAMESPACE}:WESL_UserSetting"

    } catch [Exception] {

write-host $_.Exception.Message;

write-host "Make sure Shell Launcher feature is enabled"

exit

    }

Assigning a variable with the SID of the Windows 10 local admin account:

# This well-known security identifier (SID) corresponds to the BUILTIN\Administrators group.

$Admins_SID = "S-1-5-32-544"

Create a function to retrieve the SID for a local account on the thin client then assigns a variable with the SID of the Windows 10 local user account:

# Create a function to retrieve the SID for a user account on a machine.

function Get-UsernameSID($AccountName) {

$NTUserObject = New-Object System.Security.Principal.NTAccount($AccountName)

$NTUserSID = $NTUserObject.Translate([System.Security.Principal.SecurityIdentifier])

return $NTUserSID.Value

}

# Get the SID for a user account named "User". Rename "User" to an existing account on your system to test this script.

$Cashier_SID = Get-UsernameSID("Cashier")

Defining actions to take when the shell program exits (pretty much what the comment says):

# Define actions to take when the shell program exits.

$restart_shell = 0

$restart_device = 1

$shutdown_device = 2

This section typically confuses most administrators because it indicates that the command prompt is being configured as the default shell and the reason for this is because when Shell Launcher is enabled, the default shell is set to cmd.exe.  It is possible to change this to the regular explorer.exe shell but for this example, we’ll modify the admin account’s shell instead:

# Examples. You can change these examples to use the program that you want to use as the shell.

# This example sets the command prompt as the default shell, and restarts the device if the command prompt is closed.

$ShellLauncherClass.SetDefaultShell("cmd.exe", $restart_device)

This section just provides an output indicating what the default shell was configured as:

# Display the default shell to verify that it was added correctly.

$DefaultShellObject = $ShellLauncherClass.GetDefaultShell()

"`nDefault Shell is set to " + $DefaultShellObject.Shell + " and the default action is set to " + $DefaultShellObject.defaultaction

This section is where you would modify the shell you intend on configuring for the locked down user account:

# Set Internet Explorer as the shell for "Cashier", and restart the machine if Internet Explorer is closed.

$ShellLauncherClass.SetCustomShell($Cashier_SID, "c:\program files\internet explorer\iexplore.exe www.microsoft.com", ($null), ($null), $restart_shell)

This section is where you would modify the shell you intend on configuring for the admin account:

# Set Explorer as the shell for administrators.

$ShellLauncherClass.SetCustomShell($Admins_SID, "explorer.exe")

This section retrieves the custom shells defined:

# View all the custom shells defined.

"`nCurrent settings for custom shells:"

Get-WmiObject -namespace $NAMESPACE -computer $COMPUTER -class WESL_UserSetting | Select Sid, Shell, DefaultAction

This section enables the shell launcher:

# Enable Shell Launcher

$ShellLauncherClass.SetEnabled($TRUE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

This section removes the custom shells and will need to be commented out:

# Remove the new custom shells.

$ShellLauncherClass.RemoveCustomShell($Admins_SID)

$ShellLauncherClass.RemoveCustomShell($Cashier_SID)

This section disables the shell launcher and will need to be commented out:

# Disable Shell Launcher

$ShellLauncherClass.SetEnabled($FALSE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

Customized for Dell Wyse 7020 thin client:

The following is the slightly modified version that will:

  1. Configure the VMware Horizon View client as shell launcher for the User account
  2. Configure the explorer.exe (regular shell) as the shell launcher for the Admin account
  3. The default shell will be configured with cmd.exe

Note that I’ve made changes to the Cashier_SID variable name to User_SID so it reflects the current context better.

# Check if shell launcher license is enabled

function Check-ShellLauncherLicenseEnabled

{

[string]$source = @"

using System;

using System.Runtime.InteropServices;

static class CheckShellLauncherLicense

{

    const int S_OK = 0;

    public static bool IsShellLauncherLicenseEnabled()

    {

        int enabled = 0;

        if (NativeMethods.SLGetWindowsInformationDWORD("EmbeddedFeature-ShellLauncher-Enabled", out enabled) != S_OK) {

            enabled = 0;

        }

        return (enabled != 0);

    }

    static class NativeMethods

    {

        [DllImport("Slc.dll")]

        internal static extern int SLGetWindowsInformationDWORD([MarshalAs(UnmanagedType.LPWStr)]string valueName, out int value);

    }

}

"@

$type = Add-Type -TypeDefinition $source -PassThru

return $type[0]::IsShellLauncherLicenseEnabled()

}

[bool]$result = $false

$result = Check-ShellLauncherLicenseEnabled

"`nShell Launcher license enabled is set to " + $result

if (-not($result))

{

"`nThis device doesn't have required license to use Shell Launcher"

exit

}

$COMPUTER = "localhost"

$NAMESPACE = "root\standardcimv2\embedded"

# Create a handle to the class instance so we can call the static methods.

try {

$ShellLauncherClass = [wmiclass]"\\$COMPUTER\${NAMESPACE}:WESL_UserSetting"

    } catch [Exception] {

write-host $_.Exception.Message;

write-host "Make sure Shell Launcher feature is enabled"

exit

    }

# This well-known security identifier (SID) corresponds to the BUILTIN\Administrators group.

$Admins_SID = "S-1-5-32-544"

# Create a function to retrieve the SID for a user account on a machine.

function Get-UsernameSID($AccountName) {

$NTUserObject = New-Object System.Security.Principal.NTAccount($AccountName)

$NTUserSID = $NTUserObject.Translate([System.Security.Principal.SecurityIdentifier])

return $NTUserSID.Value

}

# Get the SID for a user account named "User". Rename "User" to an existing account on your system to test this script.

$User_SID = Get-UsernameSID("User")

# Define actions to take when the shell program exits.

$restart_shell = 0

$restart_device = 1

$shutdown_device = 2

# Examples. You can change these examples to use the program that you want to use as the shell.

# This example sets the command prompt as the default shell, and restarts the device if the command prompt is closed.

$ShellLauncherClass.SetDefaultShell("cmd.exe", $restart_device)

# Display the default shell to verify that it was added correctly.

$DefaultShellObject = $ShellLauncherClass.GetDefaultShell()

"`nDefault Shell is set to " + $DefaultShellObject.Shell + " and the default action is set to " + $DefaultShellObject.defaultaction

# Set Internet Explorer as the shell for "User", and restart the machine if Internet Explorer is closed.

# $ShellLauncherClass.SetCustomShell($User_SID, "c:\program files\internet explorer\iexplore.exe www.microsoft.com", ($null), ($null), $restart_shell)

# Set VMware Horizon View Client as the shell for "User", and restart the machine if VMware Horizon View Client is closed.

$ShellLauncherClass.SetCustomShell($User_SID, "C:\Program Files (x86)\VMware\VMware Horizon View Client\vmware-view.exe", ($null), ($null), $restart_shell)

# Set Explorer as the shell for administrators.

$ShellLauncherClass.SetCustomShell($Admins_SID, "explorer.exe")

# View all the custom shells defined.

"`nCurrent settings for custom shells:"

Get-WmiObject -namespace $NAMESPACE -computer $COMPUTER -class WESL_UserSetting | Select Sid, Shell, DefaultAction

# Enable Shell Launcher

$ShellLauncherClass.SetEnabled($TRUE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

# Remove the new custom shells.

# $ShellLauncherClass.RemoveCustomShell($Admins_SID)

# $ShellLauncherClass.RemoveCustomShell($User_SID)

# Disable Shell Launcher

# $ShellLauncherClass.SetEnabled($FALSE)

# $IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

# "`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

It is important to comment out the lines for removing and disabling the shell launcher or executing the PowerShell script would not change anything.

The output should look similar to the following upon successfully executing it on the thin client:

Note that if you want the VMware Horizon View client to automatically connect to a server then you can append the -serverURL <connection_server> to the shell.

If you ever had to revert the user account’s shell back to the original explorer.exe then uncomment these lines and rerun the PowerShell script under the admin account.

Additional items and the order in which I configured the thin client are as follows:

1. Disable write filter

2. Configure Power Options to High performance (powercfg -setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c)

3. Configure Turn off the display value toafter 15 mins (powercfg -x -monitor-timeout-ac 15)

4. Configure Put the computer to sleep value to Never (powercfg -x -standby-timeout-ac 0)

(preventing the thin client to go to sleep prevents the user from being prompted with the thin client OS login screen where they would need to enter the user password)

5. Uninstall unused applications on the thin client

6. Update TightVNC access password or remove the application completely

7. Edit registry to force Num Lock on:

[HKEY_USERS\.DEFAULT\Control Panel\Keyboard]

"InitialKeyboardIndicators"="2"

"KeyboardDelay"="1"

"KeyboardSpeed"="31"

8. Disable the shade of the VMware Horizon View client via the registry key: HKCU\Software\VMware, Inc.\VMware VDM\Client\EnableShade

9. Enable firewall

10. Disable remember credentials for Windows which would also cause the Horizon View client to not remember the previous login via the registry key: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System] "dontdisplaylastusername"=dword:00000001

11. Change the local administrator and user password (the default DellCCCvdi for both accounts makes it vulnerability)

12. Update auto logon for user account

13. Enable write filter

14. Disable boot from USB in the BIOS

15. Change the BIOS password (the default Fireport should not be used)

If this thin client is going to be captured then run the Build_Master.cmd in the C:\Windows\Setup folder on the thin client.

Note that you should select the Enable local account credential changes under the Configure local account credentials heading so you can change the default password of the admin and user account after the preparation:

There will be settings that do not end up getting retained after the image preparation and they are:

  1. The name of the Windows OS does not change
  2. The Power Scheme configuration will be reverted back to defaults (monitor and computer would go to sleep)

More information about the Custom Sysprep tool can be found here: https://www.dell.com/support/manuals/us/en/04/wyse-7020/wie10_th_mr4/running-custom-sysprep-tool?guid=guid-5bd77921-f2e6-4c84-b55f-dbffddc1a89f&lang=en-us

Deploying Exchange Server 2019 on Windows Server 2019

$
0
0

I’ve recently had the opportunity to deploy Exchange Server 2019 CU1 on Windows Server 2019 and decided to capture the process so I can write this blog post demonstrating what the deployment process looks like.

Before I proceed, the following are some URLs that will be useful to review prior to beginning the deployment:

Exchange Server build numbers and release dates
https://docs.microsoft.com/en-us/exchange/new-features/build-numbers-and-release-dates?view=exchserver-2019

Exchange Server prerequisites
https://docs.microsoft.com/en-us/exchange/plan-and-deploy/prerequisites?view=exchserver-2019

Exchange Server system requirements – Network and directory servers
https://docs.microsoft.com/en-us/Exchange/plan-and-deploy/system-requirements?view=exchserver-2019#network-and-directory-servers

Exchange Server system requirements – Operating system
https://docs.microsoft.com/en-us/exchange/plan-and-deploy/system-requirements?view=exchserver-2019#operating-system

Prerequisites

Forest and Domain Functional Level

Verify that the forest and domain functional level is at Windows Server 2012 R2 or higher.

.NET Framework

As the base operating system will be Windows Server 2019, it already has the correct .NET Framework 4.7.2 requirement installed but if you’re on an older OS, use the following documentation to determine the version:

How to: Determine which .NET Framework versions are installed
https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed

As shown in the screenshot below via the registry key HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full, the Windows Server 2019 base OS has Release 461814 installed:

Remote Server Administration Tools (RSAT)

Install the Remote Server Administration Tools (RSAT) on the Exchange server with the following cmdlet:

Install-WindowsFeature RSAT-ADDS

Visual C++ Redistributable for Visual Studio

Download and install the following Visual Studio packages and install them onto the Exchange server:

Visual C++ Redistributable for Visual Studio 2012 Update 4
https://www.microsoft.com/en-us/download/details.aspx?id=30679

Visual C++ Redistributable Packages for Visual Studio 2013
https://www.microsoft.com/en-us/download/details.aspx?id=40784

Note that the two above files have the same names but slightly different file sizes.

Microsoft Media Foundation

Install the Microsoft Windows Media Foundation on the Exchange server with the following cmdlet:

Install-WindowsFeature Server-Media-Foundation

Unified Communications Managed API 4.0 Runtime

Download and install Unified Communications Managed API 4.0 Runtime onthe Exchange server:

https://www.microsoft.com/en-us/download/details.aspx?id=34992

Once the installs have completed, the Programs and Features window should list the following:

Installing Exchange Server 2019

Proceed to run the Microsoft Exchange Server 2019 setup.exe to start the installer:

Select whether to use the recommended settings from Microsoft or not (you can configure these later as well):

We’ll be installing the Mailbox role which automatically selects the Management tools as well. Note that you will also need to check the Automatically install Windows Server roles and features that are required to install Exchange Server option as that would install all the other required roles and features we did not install in the prerequisites:

Whether you install Exchange on the C drive or another is up to you. I used to prefer leaving everything on C as it is easier when performing a recovery install but as Exchange continues to require more space for logs, I’ve began installing it onto a separate drive.

Decide whether to enable or disable malware scanning:

The readiness checks will now begin:

Note the following warning messages:

Warning:

Setup will prepare the organization for Exchange Server 2019 by using 'Setup /PrepareAD'. No Exchange Server 2016 roles have been detected in this topology. After this operation, you will not be able to install any Exchange Server 2016 roles.

For more information, visit: https://docs.microsoft.com/Exchange/plan-and-deploy/deployment-ref/readiness-checks?view=exchserver-2019

Warning:

MAPI over HTTP, the preferred Outlook desktop client connectivity with Exchange server, is currently not enabled. Consider enabling it using: Set-OrganizationConfig -MapiHttpEnabled $true

For more information, visit: http://technet.microsoft.com/library(EXCHG.150)/ms.exch.setupreadiness.WarnMapiHttpNotEnabled.aspx

The first warning simply indicates the setup would prepare Active Directory and the second is that the current environment does not have MAPI over HTTP enabled.

Proceed with the install and depending on various factors, the install can take 30 minutes or longer:

… and that’s it! The install looks a lot like Exchange Server 2016 and is fairly straight forward.

Note that one of the first things I proceed with after the install is to update the certificate and URLs for the newly installed server to avoid certificate warning message users may receive via their Outlook clients when the self-signed certificate is presented.

Attempting to click on a Exchange 2019 mailbox database throws the error: "Your request couldn’t be completed. Please try again in a few minutes."

$
0
0

Problem

You’ve just completed installing Exchange Server 2019 into an existing organization then proceed to create mailbox databases on the servers but notice that you receive the following error when you attempt to browse the mailbox databases:

error

Your request couldn’t be completed. Please try again in a few minutes.

Continuing to click around would display the following symptoms:

The right hand pane will display the Please wait… message, which will never complete:

The following error would also be displayed at times:

500

Unexpected Error :(

An error occurred and your request couldn't be completed. Please try again.

Browsing the event logs may does not reveal any useful errors such as the one below (this lead me down an unrelated path of checking attributes which did not contribute to the error):

The MaxActiveDatabases attribute on the Information Store object in Active Directory has not been configured.

Solution

I spent a whole evening troubleshooting this issue without getting closer to the solution so I opened a call with Microsoft support. The engineer did not appear to have seen this before so she spent 2 full hours making changes to my account such as adding it to the Exchange Servers and Exchange Trusted Subsystem Groups (I questioned why these groups with computer accounts are even relevant), granting various accounts Full Control in the security tab of my account, asking to modify the Default Domain Policy setting Manage auditing and security logs to include the Exchange computer object groups (I had to stop her as I told her this would overwrite all of the computer objects in the directory), telling me I did not configure the Exchange server with 128GB of memory (argued with her that it was recommended, not required), and all sorts of actions I felt were not relevant.

Long story short, how she ended up getting to the resolution was something I can’t believe I did not try, and that is to move my admin mailbox to a mailbox database on the Exchange 2019 server. After moving the mailbox over and waiting for Active Directory to replicate, the mailbox databases displayed properly. It was a frustrating 2 hours but I’m glad she was able to get to the bottom of this and I hope she’ll write a KB about this so all the previous actions she made aren’t replicated with other engineers because anyone who has worked with Exchange for many years would become just as annoyed as I was.

VMware vSphere 6.5 CPU per socket calculation

$
0
0

I’ve recently had a developer ask me why the socket calculation in VMware vSphere 6.5 is seemingly so different than the legacy 5.5 environment he was used to and I found it difficult to verbally walk him through the changes so I went ahead and quickly wrote up an explanation, which I thought would be useful to blog in case I need to explain it again in the future.

First off, the following VMware blog does a great job with explaining this but I try not to respond to people with a link as they may feel I haven’t put much effort into an answer.

Virtual Machine vCPU and vNUMA Rightsizing – Rules of Thumb
https://blogs.vmware.com/performance/2017/03/virtual-machine-vcpu-and-vnuma-rightsizing-rules-of-thumb.html

Here is the write up I sent:

The following are the two parameters we are presented with when attempting to allocate processors and cores to the VM:

CPU

Cores per Socket

The CPU value is used to determine the total amount of cores that we want to present to the VM.

The Cores per Socket is a setting we use to determine the amount of physical sockets that VM would have.  By this, I mean that if we wanted the VM to have, say, 10 cores of processing power, we can use either of the three settings:

Option #1

CPU: 10

Cores per Socket: 1

The above would yield a socket configuration of 10:

Option #2

CPU: 10

Cores per Socket: 5

The above would yield a socket configuration of 2:

Option #3

CPU: 10

Cores per Socket: 2

The above would yield a socket configuration of 5:

The Cores per Socket actually has no bearing on the amount of usable cores available to the VMs as it is used to determine how many sockets are presented to yield the amount of cores available.

With the above in mind, it is not possible to have a Cores per Socket value that is higher than the CPU value because that would equate to a total amount of cores that is higher than the CPU value or the last socket would need to have zero cores for math to work.  The following are options help demonstrate this:

Note how the maximum value allowed for Cores per Socket is always equal to or less thanvalue of theCPU.

If I took the last example of having 16 for the value of CPU and configuring 8 cores per socket then that configuration would consist of 2 physical sockets with 8 cores each totaling 16 CPUs:

You’ve probably already noticed but the math is basically CPU divide by Cores Per Socket.

This has been a significant change to what was previously used to in version 5.5 (the above is 6.5) where the calculation makes more sense to many who have used that environment:

Notice how the way the older version calculates the cores is simply Number of virtual sockets multiple by the Number of cores per socket.

Part 2: Logging into SecurEnvoy 2fa enabled Exchange 2016 OWA portal loops back to the login page

$
0
0

As a follow up to my previous post:

Logging into SecurEnvoy 2fa enabled Exchange 2013/2016 OWA portal loops back to the login page
http://terenceluk.blogspot.com/2018/05/logging-into-securenvoy-2fa-enabled.html

I eventually had to upgrade the environment with the latest SecurEnvoy server 9.3.502:

… but quickly noticed that IE and Edge browser allowed me to successfully log into Outlook Web App but Chrome would loop back to the login page:

After not having any luck with the configuration I included in my previous blog post, I opened up a ticket with SecurEnvoy to troubleshoot and the engineer asked that I turn on the debug (Trace) mode to collect logs of the login process:

The logs were generated in the directory: C:\DEBUG

… which I sent over so he could analyze the entries and the response I received was:

Thanks for sending over the trace files.  I notice that the redirect to your OWA site performed after 2FA has been achieved contains favicon.ico at the end of the path.  The linked patch has been created to overcome this issue. 

I went ahead and downloaded the patch provided:

… replaced the webauthfilter64.dll file as per instructions, performed an IISReset on the Exchange server and was able to successfully log into OWA with the Chrome browser.

Viewing all 836 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>