TeamcenterKnowledge

Skills

TC Vm Operations

Skill tc-vm-operations. Operate and maintain the local Hyper-V Teamcenter 2606 demo VM (TC2606 / guest SIEMENSDC at 192.168.222.100) - snapshots, WinRM into the guest, service and port state, credential handling, and the release-matched 2606 documentation set. Use for anything about starting, stopping, checkpointing, reaching, diagnosing or connecting to the VM, and before any install or config change inside it.

A self-contained Teamcenter 2606 environment on MARTINI-GAMING. Everything here was verified against the live machine on 2026-08-05. Build history and the overlay-extraction saga live in the siemensdc-hyperv-tc2606-vm memory; this skill is the operating manual.

Thing Value
Hyper-V VM name TC2606
Guest hostname SIEMENSDC (Windows Server 2022)
Guest IP 192.168.222.100
Virtual switch TM_HYPERV_NAT (internal), host side 192.168.222.1/24
Web tier / AWC 3000 on pre-SSL checkpoints, 443 on SSL ones (mutually exclusive, see the HTTPS section)
FMS / FSC 4544
Deployment Center 8070 (/deploymentcenter/#!/login, plain HTTP)
TC root / data C:\apps\PLM\tc_root, C:\apps\PLM\tc_data
Database tc on MSSQL Express 2022

The four access layers

Each is separately revocable. If something is unreachable, work out which layer broke before touching the VM.

  1. VM lifecycle: MARTINI-GAMING\chris is in the local Hyper-V Administrators group, so Get-VM, Checkpoint-VM and Restore-VMCheckpoint all work unelevated. If Get-VM returns "You do not have the required permission", the group membership is missing or the logon token predates it: sign out and in.
  2. Guest OS: WinRM over the NAT switch. Credential is DPAPI-encrypted at %USERPROFILE%\.xcelerator\credentials\vm-admin.cred.xml.
  3. Teamcenter: tc-mcp profiles vm2606 (ordinary user, the default) and vm2606dba (DBA, use sparingly).
  4. Credentials: DPAPI credFile per profile. See below.

Getting a shell inside the guest

$c = Import-Clixml "$env:USERPROFILE\.xcelerator\credentials\vm-admin.cred.xml"
Invoke-Command -ComputerName 192.168.222.100 -Credential $c -ScriptBlock { ... }

Host-side prerequisites, elevated and one time only:

Set-Service WinRM -StartupType Automatic; Start-Service WinRM; Set-Item WSMan:\localhost\Client\TrustedHosts -Value '192.168.222.100' -Force

Use WinRM, not PowerShell Direct. New-PSSession -VMName hangs indefinitely against this guest even when it is up and logged in. WinRM works and does not need elevation per call. PowerShell Direct's only real advantage is surviving a broken guest network, so keep it as the emergency path, not the default.

Do not try to drive the guest desktop with computer-use. Mouse coordinates drift badly in Basic Session mode (requested 400,81 landed at 1188,356), and a stray click into an elevated window has input silently swallowed by UIPI. Hand GUI steps to Chris.

Ports: the failure mode that wastes an afternoon

The guest firewall is enabled on all three profiles, and the base image ships no rules for Teamcenter's ports. A completely healthy server can therefore look dead from the host.

☠ CORRECTED 2026-08-15: the default inbound is NOT block, and 8080 is WIDE OPEN

This section used to say "with a default inbound of block". Measured on the live guest, that is false, and the error runs in the dangerous direction: it makes the tier look more locked down than it is.

  • DefaultInboundAction reads NotConfigured on Domain, Private AND Public.
  • There is an enabled inbound Allow rule literally named ALL_Ports, with LocalPort Any and RemoteAddress Any.
  • Consequence: port 8080 is reachable from the host, and the Jetty web tier answers there in plaintext (HTTP 404 at /tc from the host, i.e. the server responded). The carefully host-scoped rules for 3000/4544/8070/3389 are decorative while ALL_Ports exists.

⇒ Do not cite the host-scoped rules as evidence that a port is closed. Test the port from the host. This surfaced only because a prediction that 8080 would be blocked was tested instead of asserted; had it been asserted, the skill would have kept saying the opposite of the truth.

Before concluding a service is down, check whether anything is listening inside the guest. These are different problems with identical symptoms:

Invoke-Command -ComputerName 192.168.222.100 -Credential $c -ScriptBlock {
  Get-NetTCPConnection -State Listen | Where-Object LocalPort -in 3000,4544,8080
}

Rules currently in place, scoped to the host only rather than Any:

New-NetFirewallRule -DisplayName "TC2606 web tier 3000 (Hyper-V host only)" -Direction Inbound -Protocol TCP -LocalPort 3000 -RemoteAddress 192.168.222.1 -Action Allow -Profile Any

FMS/FSC is 4544, not 7001. TcFSCService owns 4544. Nothing listens on 7001 on this build, despite 7001 being the familiar 4-tier port elsewhere. Get the port from the owning process, not from habit:

$p = Get-Process TcFSCService; Get-NetTCPConnection -State Listen | Where-Object OwningProcess -in $p.Id

★★★ Never force-kill DC_RepoService — it orphans a java child holding the ActiveMQ port

DC_RepoService (Deployment Center's repository service) can hang in StopPending. Do not force-kill the process to clear it. Doing so orphans its java child, which keeps holding tcp://SIEMENSDC:8073. Every subsequent attempt to start ANY DC service then fails: it cannot bind the ActiveMQ broker and exits after ~18 seconds. This looks exactly like a broken service — the service itself is fine, the port is just held by an orphan with no visible link back to the kill that caused it.

⇒ Fix is finding and killing whatever still holds port 8073 (Get-NetTCPConnection -LocalPort 8073Get-Process -Id <OwningProcess>), not re-installing or re-configuring DC. If a DC service exits quickly after start with no clear error, check this before anything else.

Install layout and desktop shortcuts

Almost everything is under C:\apps.

What Where
Teamcenter C:\apps\PLM (tc_root, tc_data, DSP for Dispatcher)
Deployment Center C:\apps\DC
Web tier C:\apps\PLM\tc_root\web_tier (bin\TcWebTierService.exe, port 8080)

Corrected 2026-08-15: there is no C:\apps\Tomcat9 on this build

This table previously read | Web tier (Tomcat) | `C:\apps\Tomcat9` |. Test-Path on that path returns False, and C:\apps holds exactly six entries: DC, Identifier, jdk-21.0.11.10, License Server, node, PLM. Port 8080 is owned by C:\apps\PLM\tc_root\web_tier\bin\TcWebTierService.exe; port 3000 is the Node gateway.

WHY IT MATTERS, and it is the house failure mode rather than a typo: Get-ChildItem C:\apps\Tomcat9\webapps -ErrorAction SilentlyContinue returns empty for a missing directory exactly as it does for an empty one. Searching there for deployed web applications therefore reads as no web applications are installed regardless of what is true. Hit on 2026-08-15 while establishing whether Teamcenter Security Services was installed. Use Test-Path, and carry a known-good and a known-bogus control in the same call, before concluding anything is absent. | License Server | C:\apps\License Server | | JDK | C:\apps\jdk-21.0.11.10 |

On the guest desktop:

Shortcut Notes
Start Teamcenter services Called automatically at login
Stop Teamcenter services Call this before shutting the VM down
Teamcenter 2606 (may render as Teamcenter 2606.0000) Rich client
Business Modeler IDE BMIDE, the tool that builds custom DC-compatible packages

Chrome on the guest has bookmarks for Active Workspace and Deployment Center.

Stop the services before shutdown. There is a stop shortcut for a reason: a Teamcenter tier plus MSSQL killed by a power-off is how you get a corrupt environment rather than a clean one. Sequence for a clean shutdown is stop services, then Stop-VM, then checkpoint if needed.

Starting the VM is not the same as starting Teamcenter

MEASURED on a real headless cold start, 2026-08-05. Start-VM, no interactive login (quser confirmed "No User exists"), observed for five minutes:

Result
WinRM (5985) reachable in ~12 seconds
Ports 3000 / 4544 / 8080 all still closed after 300s, and nothing listening for them inside the guest either
Running MSSQLSERVER, Siemens License Server, Active Workspace Indexing Service, TcFSCService, SQL support services (all Automatic)
Stopped WebTier, Server Manager TCDB_PoolA, Vault, Process Manager, Dispatcher x3, Discovery x3, Global Search Indexing, Partition Sync, Suggestion Builder, actionmgrd, am_read_expression_manager, revision_config_accelerator, schmgtwfd, subscripmgrd, taskmonitor, tess_server, and all four DC services (all Manual)

★★ Teamcenter does not come up on a headless boot. Only the Automatic services start. Everything that makes the tier usable is Manual, started by the desktop script at login.

TcFSCService is the trap inside the trap. It is Automatic, so it shows as Running on a headless boot, but it binds only an ephemeral port (observed 49671) and not 4544. A service showing Running is not the same as that service serving. Check the listener, not the service state.

Bringing the tier up headlessly (verified working)

$c = Import-Clixml "$env:USERPROFILE\.xcelerator\credentials\vm-admin.cred.xml"
Invoke-Command -ComputerName 192.168.222.100 -Credential $c -ScriptBlock {
  $b = "C:\apps\PLM\Misc\Startup\Services\Start\Start_All_Teamcenter_Win_Services.bat"
  $j = Start-Job { param($x) cmd.exe /c "`"$x`" 2>&1" } -ArgumentList $b   # NO < NUL - see below
  Wait-Job $j -Timeout 240 | Out-Null; Stop-Job $j; Remove-Job $j -Force
}

The script does not exit, but it works anyway. It was still running at 240s, yet every Teamcenter service was up and 3000 / 4544 / 8080 were listening and reachable from the host. A live tc_connect and getTCSessionInfo both succeeded afterwards. Judge success by service and port state, never by the script exiting. Fire it, bound the wait, then verify.

Why it does not exit (measured, and two hypotheses ruled out)

The script leaves ~33 cmd and ~90 conhost processes alive, the bulk of them stamped within a few seconds of the launch. The parent is waiting on children that never terminate. That is the mechanism.

Two plausible-sounding explanations that the evidence does not support, at least for a headless run:

  • Not the license server. lmgrd started at boot + 2 seconds, roughly nine minutes before the start script ran. The license server was long up. It may still be worth up to ten minutes on a slower cold start, so it remains a real concern for a login-time run, but it did not cause this.
  • No Vis Manager process existed at all. Nothing visualization-related was running, and the current Start\ folder has no vis script (the numbering runs 1, 2, 4, plus DB and DC). Note an older build had a 5_Start_Vis_Services.bat, so this may simply have been dropped from this overlay.

Caveat on the second point: a headless run has no interactive desktop, so a window that would be left open at login cannot be observed this way. MainWindowTitle was empty for every process. If a stray window is left open on an interactive login, confirm it from the console rather than over WinRM.

☠☠ RETRACTED 2026-08-14: do NOT use < NUL on this script. It is what makes the tier come up broken. Every pause in Start_All_Teamcenter_Win_Services.bat is commented out (rem pause), so < NUL buys nothing here - and it disables every timeout /T n /NObreak in the file, which is precisely how the script sequences its own startup (Vault, then a wait, then FSC / Process Manager / Server Manager). With the waits gone, Server Manager starts before Vault is serving, waits ~180s, and dies:

java.lang.IllegalStateException: Database user's password can't be retrieved from
TcVault. Timeout reached, Vault service still not ready.

The service then reads Stopped, no tcserver ever spawns, and AWC happily listens on 3000 while serving nothing - which looks like a broken web tier and is not. The warning about timeout was already recorded below and I walked into it anyway; "watch for that if a start sequence races" was not strong enough, so it is now a retraction of the recipe itself.

⇒ The < NUL trick still applies to scripts with a LIVE pause (see the overlay extraction and the Temp_Cleaner note further down). It does not apply here.

< NUL matters: it feeds EOF to any pause, which is the same trick that fixed the overlay extraction during the build. Without it a prompt can block forever.

But < NUL also breaks the Windows timeout command, which prints ERROR: Input redirection is not supported, exiting the process immediately and skips the wait. Services still start, because net start is synchronous, but any script logic that relies on a deliberate pause between steps is defeated. Watch for that if a start sequence races.

★★★ Server Manager races Vault on startup, and Vault can come up WEDGED

Measured 2026-08-14 on a post-reboot bring-up. Two separate faults with one symptom: AWC listening on 3000 and serving nothing, zero tcserver processes.

Fault 1 - the race. Server Manager reads the DB password from TcVault at init. If Vault is not serving yet it waits ~180s and dies:

Failed to initialize the pool manager.
java.lang.IllegalStateException: Database user's password can't be retrieved from
TcVault. Timeout reached, Vault service still not ready.

The service reads Stopped afterwards, so the tier looks half-up and the web tier takes the blame. The log is mgr.output in tc_root\pool_manager\confs\TCDB\, and the fuller one is C:\Users\Administrator\Siemens\logs\Teamcenter\ServerManagers\TcCluster\PoolA\ServerManager\process\ServerManager.log.

Fault 2 - the wedge, and this is the nastier one. vault.exe can come up with the process alive, port 8200 bound, and the service reporting Running, while refusing every connection on both IPv4 and IPv6 and writing nothing further to its own log. Get-Service and even a listener check both say healthy. Only an actual request tells the truth:

# from inside the guest; Vault uses a self-signed cert
Invoke-WebRequest 'https://SIEMENSDC:8200/v1/sys/health' -UseBasicParsing
# want: 200 with initialized:true, sealed:false

Fix is Restart-Service Teamcenter_Vault_Service (which redeploys vault.exe under a new pid), then confirm the health endpoint answers before starting Server Manager. A sealed Vault also answers 200, so check sealed:false, not just the code.

⚠⚠ CORRECTED 2026-08-15: the restart does NOT always clear it, and /health cannot tell you whether it did. A second, nastier variant was hit while enabling SSO: the service restarted cleanly under a new pid and the wedge came back within ~65 seconds, three consecutive times. Throughout, /v1/sys/health returned 200 sealed:false initialized:true on every poll, seconds before each Server Manager start that then died. ⇒ Treat the restart above as the fix for the simple wedge only, and never report Vault healthy on the strength of /health.

And most of the "obvious" Vault symptoms are not symptoms. Corrected 2026-08-16, measured on a demonstrably healthy tier at the same moment as a 200/2691 AWC and Total: 16, Assigned: 13, Warm: 3:

Reading On a HEALTHY tier Diagnostic?
VAULT_SECRET_ENGINE_ENABLED=false false NO
VAULT_AUTH_CERT_INITIALIZED=false false NO
VAULT_AUTH_APPROLE_INITIALIZED=false false NO
core.raft: ... keys are pending present, 1,751 lines NO
zero successful auth/tc/login absent (13,532 lines, live) yes

⇒ The two signals that actually discriminate are auth/tc/login activity in vault_utility_logs.log and the can't be retrieved from TcVault line in ServerManager.log. Check those. The config flags are the steady state of this Vault and reading them off a healthy tier will send you into a restart or a revert for nothing. Full workup in HANDOFF-health-checker.md section 8.

Both are now gated in the startup script (patched 2026-08-14, backup Start_All_Teamcenter_Win_Services.bat.bak-2026-08-14-vaultgate):

  • Utilities\Wait-VaultReady.ps1 replaces the blind timeout /T 20 after the Vault start. It polls the health endpoint, and if Vault never answers it restarts the service once and polls again - the wedged-vault recovery. Non-zero exit means do not bother starting Server Manager.
  • A [pool-check] step after net start "Teamcenter Server Manager TCDB_PoolA" waits for a real tcserver to spawn and prints a loud, greppable error if none does, instead of letting the script sail on.

Both were tested standalone against a healthy tier. The patched script as a whole has NOT been run end to end - that needs a maintenance window, because the script tears the tier down first (see below).

⚠ The startup script is DESTRUCTIVE: it wipes the Teamcenter logs

Start_All_Teamcenter_Win_Services.bat is a full clean-restart, not an idempotent "start what is stopped". Before starting anything it:

  • net stops the whole tier, then taskkill /F on vault.exe, java.exe, javaw.exe, tcserver.exe, node.exe, windowsService.exe and ~15 more
  • rmdir /S /Q on %userprofile%\FCCCache, %userprofile%\FSCCache, %userprofile%\Siemens and %userprofile%\Teamcenter

%userprofile%\Siemens is where the Teamcenter logs live - every tcserver syslog, every crash .dmp, and the Server Manager logs. So running this script destroys the evidence from the previous run.

⇒ If you are diagnosing anything, copy the logs off before restarting the tier. Running the start script is not a neutral act, and "the syslog is empty" after a restart may mean it was deleted rather than never written.

★★★ An interactive login re-fires this same destructive script, on top of an already-healthy tier

MEASURED 2026-08-14. The desktop shortcut "Start Teamcenter services" is not just a shortcut - it is Start Services.lnk in the all-users Startup folder (%ProgramData%\Microsoft\Windows\Start Menu\Programs\StartUp\), pointing directly at Start_All_Teamcenter_Win_Services.bat with no arguments. That folder fires for every interactive logon, including a Hyper-V Enhanced Session Mode login - unconditionally, with no check for whether Teamcenter is already up.

So the sequence that broke things: bring the tier up headlessly (over WinRM, before anyone logs in), then log in interactively, and the login itself immediately re-runs the full destructive teardown documented above - net stop, taskkill /F on ~20 processes, wipe %userprofile%\Siemens - on a tier that had nothing wrong with it. From the user's side this reads as "logging in stops Teamcenter," which is exactly what it does.

(A second candidate, the Scheduled Start task with a MSFT_TaskSessionStateChangeTrigger, was checked and ruled out - it is a Windows Update task, sc start wuauserv, and its session-state triggers are Disabled. The Startup-folder shortcut is the only live trigger.)

Fixed 2026-08-14: the script now guards itself. Right after the self-relaunch line (before anything else runs - not even clearlocks), it checks whether Teamcenter is already healthy by effect, the same standard used everywhere else in this file: real tcserver processes running AND port 3000 actually listening, never Get-Service status. If both are true it prints a message and exits immediately, before the teardown ever starts. Pass /force as an argument to bypass the guard and force a full stop/restart anyway - the desktop shortcut itself still has no arguments, so a normal login or a normal double-click both get the safe, non-destructive path; only an explicit /force does the destructive one.

if /I "%1"=="/force" goto :TC_GUARD_DONE
powershell -NoProfile -Command "$tc=@(Get-Process tcserver -ErrorAction SilentlyContinue).Count; $p=[bool](Get-NetTCPConnection -LocalPort 3000 -State Listen -ErrorAction SilentlyContinue); if($tc -gt 0 -and $p){exit 0}else{exit 1}"
if not errorlevel 1 (
	echo [startup-guard] Teamcenter already running - skipping restart.
	exit /b 0
)
:TC_GUARD_DONE

★★★ FIXED 2026-08-23: the guard used to test port 3000 only, and did not protect an SSL checkpoint

RETRACTED 2026-08-25. This section originally read "☠☠☠ ... DOES NOT PROTECT AN SSL CHECKPOINT" as a live, current warning. It is no longer current — flagged stale by the Eggplant session on 2026-08-25 after it read this section, believed it, and took it to Chris as a hard blocker on unrelated work, including a recommendation to patch a file that had already been patched. Caught and corrected the same night. Kept below for the history and the fix rationale, not as an active warning.

The original finding, EXERCISED 2026-08-16 by reading the script against a live 443 tier: the guard fired only when $tc -gt 0 -and $p, with $p as Get-NetTCPConnection -LocalPort 3000. The gateway binds one port, so on any checkpoint carrying the SSL/443 migration 3000 was never listening and the second term was always false — the guard was inert on an SSL checkpoint, and an interactive logon fired the all-users Startup shortcut straight into the destructive teardown.

The fix, applied 2026-08-23 and independently reverified live on 2026-08-25: line 13 of Start_All_Teamcenter_Win_Services.bat now reads

$p=[bool](Get-NetTCPConnection -LocalPort 3000,443 -State Listen -ErrorAction SilentlyContinue)

with the surrounding rem block and the guard's echo text both updated to say "port 3000 OR 443" as well, so the older caveat about the echo text lying is also resolved. Verified twice since by two different sessions, by two different methods: running the guard's own expression against a live tier (tcserver count > 0, port 3000-or-443 listening = True, guard correctly skips), and separately by comparing the guard's tested ports against the gateway's actual configured port from microservices\gateway\config.json (a genuine drift detector, not just a rerun of the same test).

Backup of the pre-3000/443-fix file: Start_All_Teamcenter_Win_Services.bat.bak-2026-08-23-guard-443fix. Backup of the original pre-guard file (2026-08-14): Start_All_Teamcenter_Win_Services.bat.bak-2026-08-14-idempotent-guard. Full incident writeup, including why the config.json-read alternative was considered and not taken: HANDOFF-health-checker.md §15b.

Still true and unchanged from the original finding: this class of bug fails in the dangerous direction. A guard that wrongly fires leaves a broken tier broken, which is visible. A guard that wrongly declines to fire produces a result indistinguishable from the script's normal, intended behavior — nothing reports it on its own. That is why this got missed for as long as it did, and why it is worth re-checking after any future change to the gateway's port.

Verified by state, not by captured output. This script's start "" /min self-relaunch detaches into a new console whose stdout never flows back to a caller - Receive-Job on it is reliably empty, guard or no guard, so do not expect to see the echo messages remotely. What proves the guard fired: the tier's oldest tcserver process kept a start time from before the test (unaffected by a run that would have killed every one of them), pid churn stayed at the scale of normal pool activity rather than a full reset, all 21 services stayed continuously Running, and AWC never dropped from 200. A real teardown would fail every one of those checks at once.

Known residual gap, not yet fixed: the guard checks health at the moment it runs. If a login happens while a headless bring-up is still mid-flight (tcservers not yet spawned), the guard would see "not healthy yet" and let the login-triggered run proceed concurrently with the one already in progress - two teardown/rebuild sequences racing each other. Not observed, but not guarded against either. If that becomes a real problem, the fix is a lock file (create-on-start, delete-on-finish) rather than the health check alone.

The second disk has no drive letter, and that is correct

Get-Volume in the guest shows only C:. The media disk is surfaced instead as a volume-GUID Junction at C:\apps\DC\repository\software, wiring it straight into the Deployment Center repository. Do not assign it a drive letter to "fix" it. See tc-deployment-center.

The service scripts

All under C:\apps\PLM\Misc\Startup\Services\:

Script Purpose
Start\Start_All_Teamcenter_Win_Services.bat The desktop "Start Services" target. Does not include DC
Start\1_Start_Tc_web_4tier_AWC_Solr.bat Web tier, 4-tier, AWC, Solr
Start\2_Start_Dispatcher_Services.bat Dispatcher
Start\4_Start_Other_Services.bat The remaining daemons (note: there is no 3_)
Start\Start_Database_Daemons.bat DB daemons
Start\Start_DC_Services.bat Deployment Center, separate on purpose
Stop\Stop_all_tc_services_sessions.bat The desktop "Stop Services" target
Stop\Stop_DC_Services.bat, Stop_Dispatcher_Services.bat, Stop_Process_Manager.bat, Stop_Database_Daemons.bat Targeted stops

Utilities\ is worth knowing about: Clearlocks_All_Dead.bat, cleanup_recoverytable.bat, Cleanup_tc_sessions_temp.bat, Stop_FSC_FCC_Clear_Cache.bat, Reset_Solr_Indexer_Password.bat, Update_metadata_Cache.bat, TCmenu.bat.

Desktop shortcut targets

Shortcut Target
Start Services C:\apps\PLM\Misc\Startup\Services\Start\Start_All_Teamcenter_Win_Services.bat
Stop Services C:\apps\PLM\Misc\Startup\Services\Stop\Stop_all_tc_services_sessions.bat
Teamcenter 2606 C:\apps\PLM\tc_root\portal\portal.bat
Business Modeler IDE C:\apps\PLM\tc_root\bmide\client\bmide.bat
Command Prompt cmd /k C:\apps\PLM\tc_root\tc_menu\tc_Vanilla_Env.bat (the TC-environment shell, use this to run TC utilities headlessly)

These live on C:\Users\Administrator\Desktop; only Chrome is on the Public desktop. The all-users Startup folder (C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp) holds Start Services.lnk, bg.bat - Shortcut.lnk and Welcome.bat - Shortcut.lnk (the latter two from C:\apps\Identifier\). Welcome.bat is the one that parks on "press a key to continue", which is why an interactive login stalls but the service script itself does not need a human.

Clock

The guest shipped as Pacific Standard Time while the host is Central Standard Time, so guest timestamps read two hours behind. That was timezone, not skew: guest UTC and host UTC always matched.

Changed to Central Standard Time on 2026-08-05 so guest and host agree:

Set-TimeZone -Id 'Central Standard Time'

A running Teamcenter tier does not pick this up. The tcserver and Java processes cache the zone at start. The services were restarted on 2026-08-05 and TC now agrees: a fresh syslog header reads created by Administrator on Wed Aug 5 16:39:54 2026 Central Daylight Time.

Note the syslog body lines are stamped in UTC (2026/08/05-21:39:54.000 UTC) while the header records local time. Do not read a UTC body line as a wrong clock.

Also check Teamcenter's own SiteTimeZone site preference: it is independent of the OS zone, and leaving it unset has blocked Schedule Manager on other tiers here.

Restarting the tier (verified 2026-08-05)

Disconnect any tc-mcp sessions first, then stop, then start, using the same job-plus-timeout pattern for both:

C:\apps\PLM\Misc\Startup\Services\Stop\Stop_all_tc_services_sessions.bat
C:\apps\PLM\Misc\Startup\Services\Start\Start_All_Teamcenter_Win_Services.bat

Observed: 21 Teamcenter services down and all ports closed, then 21 back up with 3000 / 4544 / 8080 listening. Full cycle took a few minutes.

The stop script is not just a stop. It also deletes WebTier and Windows service logs, clears C:\Temp, and ends on "Press any key to delete Temcenter Dir...". Running it with < NUL feeds EOF, which auto-answers that prompt. It appears to concern the temp directory it has just been clearing, and nothing was lost in the observed run, but know that you are answering a prompt sight-unseen. Unlike the start script, the stop script exits cleanly.

Services

Once started, all Teamcenter services run. The historical "System error 1069, service did not start due to a logon failure" blocker is resolved; do not re-diagnose it.

Expect roughly: Teamcenter WebTier, Teamcenter Server Manager TCDB_PoolA, Teamcenter FSC Service FSC_SIEMENSDC_siemensdcAdministrator, Teamcenter_Vault_Service, Teamcenter Process Manager, three Dispatcher services, Global Search Indexing, Partition Sync, Suggestion Builder, and three Discovery services. Most are Manual start type but running, because the guest startup scripts launch them.

Get-Service | Where-Object { $_.Name -match 'Teamcenter|FSC' } | Select-Object Status,StartType,Name

A cold login takes 15 to 25 seconds while the pool assigns a tcserver. That is normal, not a hang.

★★★ DispatcherClient stopped is SILENT: async work queues forever with no error

Measured 2026-08-14. Teamcenter DispatcherClient was Stopped while Dispatcher Module and Dispatcher Scheduler were both Running. Nothing anywhere reports a fault. What actually happens is that DispatcherRequest objects are created normally and then sit at state INITIAL with no Start Time, forever, because the client is the component that pulls work off the queue.

Two unrelated-looking failures came from this one cause in a single night, and neither presented as a Dispatcher problem:

  • An Active Workspace PLMXML export that "gave no notification". Searching the tier found no output file, no translator activity, no error - consistent with "the request was never made", which is the wrong conclusion. It had been made and was queued.
  • A CapitalForward workflow submission that appeared not to fire, which sent another session hunting through workflow templates and handler wiring that were all correct.

Add the three Dispatcher services to any post-revert or "why did nothing happen" check. A revert restores whatever service state the checkpoint captured, so a client that was down when the checkpoint was taken comes back down.

Get-Service | Where-Object { $_.DisplayName -match 'Dispatcher' } | Select-Object DisplayName,Status
Start-Service 'Teamcenter DispatcherClient V2606.2026052800'    # Scheduler -> Module -> Client

Manual start type plus Stopped is NOT evidence that a service is meant to be stopped. Nearly every Teamcenter service on this VM is Manual and started by the guest startup scripts, so "Manual and stopped" looks identical for a service that is deliberately idle and one that silently failed to start. This was seen as Stopped in two earlier service checks here and read as normal standing state both times. Check whether anything DEPENDS on it rather than pattern-matching the start type.

Verify by effect, not by Status : Running. A Dispatcher service that cannot bind exits after roughly 18 seconds while briefly reporting Running (same shape as the DC_RepoService port-8073 trap above). Re-check after 90 seconds, and confirm a queued task actually moves - the task log under DSP\Logs\Dispatcher\task\<taskid>\ shows Status = Started Translation!!! when the queue is genuinely draining.

★★ INITIAL does not mean "nothing claimed it" - always read the Module-side log

A DispatcherRequest stuck at INITIAL is identical when read over SOA whether (a) no client ever claimed it, or (b) a client claimed it and crashed before writing any state back. The crash happens during Extract, which precedes the state write, so the request never leaves INITIAL either way. Polling from TC cannot separate them: measured 2026-08-14, six minutes of repeated polls returned an honest, unchanging INITIAL and the inference drawn from it ("no client is configured for this service") was still false - the request had been claimed one second after creation and had died in TaskPrep.

⇒ The only place the two differ is on the Module host:

Get-ChildItem C:\apps\PLM\DSP\Logs\Dispatcher\task\<taskid>\   # exists => it WAS claimed
Get-Content   C:\apps\PLM\DSP\Logs\Dispatcher\task\<taskid>\<taskid>_dc.log
#   "Begin Extract of Request"        -> claimed
#   count of "Begin Extract" lines    -> attempts (no retry = claimed once, died)
#   an empty Stage\DC\<taskid>\ dir   -> died before producing input

⚠ Do not conclude "the translator is not registered" from a stale INITIAL. A translator can be fully registered, be claimed immediately, and still fail - and that failure is invisible to every TC-side query.

Snapshots

Checkpoints are the safety net for every install and config change. Take one first, always.

Checkpoint-VM -Name TC2606 -SnapshotName "pre-<task>-<date>"   # name MUST be < 100 chars

★★★ A failed Checkpoint-VM may have taken the checkpoint anyway

Snapshot names cap at 100 characters. Exceed it and Hyper-V creates the checkpoint, then fails to apply the name, and reports the whole thing as failed:

'TC2606' failed to modify settings.
An unexpected error occurred: The parameter is incorrect. (0x80070057)

Nothing in that says "name too long", and 2026-08-09 it followed a 107-character name. The checkpoint existed: count went 10 → 11, under an auto-generated name TC2606 - (8/9/2026 - 8:37:55 AM), with a fresh differencing disk on the timestamp. Fix is Rename-VMSnapshot, not a retry.

After ANY failed Hyper-V operation, list the snapshots before concluding anything. Both wrong moves here are expensive: retrying makes a duplicate and deepens the differencing chain, and reporting the failure leaves a session authoring with no rollback point they believe they lack while a good one sits under a name they will never recognise as theirs.

⇒ The standing rule is usually stated as "re-read the artifact after a write". This is the case that shows it applies harder on the failure path: on success you merely confirm what you expect, but on failure the reported state and the real state have already diverged.

Current chain:

TC2606 applied, windows updated, notepad++ updated        <- root, treat as READ-ONLY
  └─ pre-firewall-open-2026-08-05
       └─ TC2606 baseline + DC media disk (offline, pre-Deployment-Center) 2026-08-05

Prefer an offline checkpoint for anything structural. Shutting the VM down first gives a consistent capture with no memory state and no half-written files. That is how the third checkpoint was taken and it is the right pre-install restore point.

The two-disk trap

The VM has a second disk for installation media: D:\HyperV\Virtual Hard Disks\Deployment Center Software.vhdx, attached at SCSI 0:2, Dynamic, 127GB max.

A separate VHDX is NOT excluded from checkpoints. Hyper-V creates a differencing AVHDX for every attached disk. In practice this costs nothing here because the media is static, so the per-checkpoint delta measures 0GB. But do not describe the disk as "outside" the snapshot, because it is not.

★★ The trap runs the other way. The two checkpoints taken before the disk existed record only the OS disk, so reverting to either DETACHES the media disk from the VM. The .vhdx survives on the host; re-attach at SCSI 0:2. Always check what a checkpoint actually contains before reverting to it:

Get-VMSnapshot -VMName TC2606 | ForEach-Object { $_.Name; Get-VMHardDiskDrive -VMSnapshot $_ | ForEach-Object { "   " + (Split-Path $_.Path -Leaf) } }

If true exclusion is ever needed, serve the media over SMB from the host instead of attaching a VHDX.

★★★ Restoring a LIVE checkpoint needs its full RAM allocated atomically, not ramped

Measured 2026-08-11, reverting to a checkpoint taken while the VM stayed Running (a live/Standard checkpoint, saved memory state included). After Stop-VM -TurnOff + Restore-VMSnapshot + Start-VM, the start failed:

Start-VM : 'TC2606' failed to restore.
Unable to allocate 16384 MB of RAM: Insufficient system resources exist
to complete the requested service. (0x800705AA)

The VM was left in Saved state — not Off, not Running, not recoverable by retrying alone. This is a harder memory requirement than normal operation. A cold boot can start small and grow via dynamic memory (Min 4 GB, ramping up). Restoring a saved memory image has to reload that exact image, so Hyper-V demands the full startup allocation (here, 16 GB) as one atomic reservation before the VM will start at all. Host free memory sitting just under that line — even by a few hundred MB — blocks the restart outright, and it does not resolve itself: free memory measured 15,857 MB, then drifted down to 14,950 MB over the next minute as other host processes grew, not up.

⇒ Before reverting a live checkpoint specifically, confirm host free memory clears the VM's full startup RAM with real headroom, not just "probably enough." If the start fails this way, the fix is freeing host RAM (closing something large — closing Chrome recovered 24 GB here, evidently far more than its visible working set implied) and retrying Start-VM, not troubleshooting the VM or the checkpoint itself; nothing is wrong with either. An offline checkpoint (VM stopped before checkpointing) does not carry this trap, since there is no saved memory image to reload atomically — one more reason to prefer offline for anything you might need to revert under memory pressure.

After a checkpoint REVERT specifically: expect a ~10 minute license gap

Measured 2026-08-08, reverting to an older checkpoint (not a fresh boot): Teamcenter comes up but every SOA login fails with 70105 Error finding license for selected module for roughly ten minutes. Two full Teamcenter service restart cycles did not shorten this: the Siemens License Server genuinely needs that long to re-establish itself after the guest's saved state is restored, and nothing about the Teamcenter services themselves is broken. Don't diagnose 70105 as a package, config, or credential problem in the minutes right after a revert; wait it out first, then retest.

A Standard checkpoint also resets the guest's clock to checkpoint time until Hyper-V's own time synchronization pulls it forward again. If a script or log timestamp looks wrong immediately after a revert, check whether it's this transient clock skew before chasing a real bug.

★★ And AWC returns 401 JWT expired. Restarting WebTier does NOT fix it.

Third distinct post-revert symptom, measured 2026-08-08. Every Active Workspace page returns 401 Unauthorized request: JWT expired at <time>, with the same expiry timestamp on every attempt (fixed server state, not a per-request token) and reproducing on a fresh browser profile. Meanwhile SOA REST login still succeeds, so Teamcenter authentication is healthy: it is specifically the AWC gateway's signing state, restored from the checkpoint's saved memory.

Teamcenter WebTier is the wrong service and restarting it changes nothing. Port 3000 is served by a Node gateway microservice, not WebTier:

C:\apps\PLM\tc_root\microservices\gateway\stage\bin\wntx64\nodejs\node.exe server.js
    --config config.json --signerKeyPath="...\microservices\secrets/signer_keystore.pem"

Trace the supervisor instead of guessing, with Get-NetTCPConnection -LocalPort 3000 then walking Win32_Process.ParentProcessId:

services.exe -> ProcessManagerService.exe -> cmd /c ...\gateway\start_service.bat -> node server.js

Restart Teamcenter Process Manager. The gateway gets a new pid and the 401 clears immediately (verified: AWC root back to HTTP 200 issuing an XSRF-TOKEN).

★★ It also triggers a DARSI/SWF client rebuild that eats 4 GB for about five minutes. Measured 2026-08-08: minutes after the Process Manager restart, darsi_repo\node_modules\@swf\react-scripts\scripts\build.js appeared as a single node process at 4.1 GB and guest free memory fell to 0.12 GB of 16 GB. It is a legitimate webpack production build of the Active Workspace client bundle, not a leak. Do not kill it: you would leave the bundle half-built and it restarts anyway. Confirm it is progressing rather than wedged by sampling CPU twice a few seconds apart (it climbed 342s to 367s in 20s, so multi-threaded and working), then wait. It finished on its own in about five minutes and guest free memory went straight back to 6.9 GB. ⇒ Restart Process Manager only when you can afford five minutes of memory pressure, and do not run other I/O-heavy work (a checkpoint merge, say) against it.

★ Two operational caveats. It restarts the entire microservices stack (gateway, Solr, discovery, roughly twenty java/node processes), so it is heavier than a WebTier bounce. But it does not touch Teamcenter Server Manager, so a warm tcserver pool survives. If someone depends on pool state for a test, capture the tcserver pids before and after and report the diff honestly: the pool recycles individual servers on its own, so one pid changing is normal churn, whereas a real pool restart changes every pid.

★★★ Tell other sessions when you revert a shared tier

Five-plus sessions share this VM. Beyond destroying their writes, a revert silently invalidates their observations, and they cannot detect it. Both happened on 2026-08-08:

  • One session diagnosed a broken full-text index, zeroed dashboard counts and a stuck indexflow lock, and was about to run a destructive runTcFTSIndexer -task=objdata:clear. All three were artefacts of the swapped database, not real faults.
  • The same session measured serverPool.properties at PROCESS_TARGET=24/WARM=5, concluded a documented trim had never been applied, and committed and deployed that claim to the live KB site. It actually reads 8/3, modified 2026-08-07, predating the checkpoint. Their reading was of a state that no longer existed.

⇒ Announce the revert window, and tell them anything measured inside it is void. The cheap self-check to hand them: re-read an object you created after the checkpoint time, by uid, and expect it to be gone.

This working tree is shared too. An edit to a skill here was silently discarded three times on 2026-08-08. Edit and commit in the same breath, stage by name, and re-read the file to confirm your change is still present before you rely on it.

★★★ The mechanism, found on the third occurrence, and it is not another session being careless. Install-XceleratorWorkspace.ps1 copies skills like this:

$src = Join-Path $repoRoot      ".claude\skills"   # xcelerator-workspace's OWN snapshot
$dst = Join-Path $WorkspaceRoot ".claude\skills"   # a JUNCTION -> tc-automation-skills\skills
Copy-Item "$src\*" $dst -Recurse -Force

$dst is a directory junction into the tc-automation-skills git working tree, so -Force writes the distribution repo's stale copies straight over the live source of truth. Anyone running Install-XceleratorWorkspace.ps1 -Force for an unrelated reason (a CLAUDE.md change, say) silently reverts every uncommitted skill edit. The junction is deliberate ("one source of truth, no copy step") and the copy step defeats it.

⇒ Until the installer is fixed: commit skill edits before running that installer, and re-verify skills afterwards. The tell is git status showing a modified skill you did not touch, with the diff being pure deletions of recent work.

★★★ Do not run infrastructure operations under someone else's live experiment

Restarting the pool, restarting Process Manager, or reverting a checkpoint empties the very caches another session may be measuring. On 2026-08-08 this corrupted two separate investigations on the same afternoon: a protectionScope conclusion and a "dataset content is not cached" conclusion were both reached minutes after a restart the measuring session did not know about, and each looked like a clean result.

Wait for an acknowledgement before acting, not just a sent message. A queued cross-session message is not an acknowledgement; it may not be read until after the other session's turn completes, which can be after your operation has already landed. If the operation is genuinely urgent, do it and say so immediately and loudly, so the other session can discard measurements from that window instead of publishing them. Timestamps settle these arguments: Get-Process tcserver | Select StartTime gives the exact moment the pool went cold.

★★★ Tier starvation impersonates a defect in whatever you were building

Numbers corrected 2026-08-25, flagged stale by the Eggplant session. Originally written as "a 16 GB VM on a 32 GB host." EXERCISED that day: the host reports 127.9 GB total capacity, and TC2606 is currently assigned ~24 GB under dynamic memory (MemoryStartup is 16 GB, which is likely where the original figure came from — that is the VM's configured startup allocation, not its current assigned memory or the host's real capacity). The starvation mechanism and advice below are still sound; only the arithmetic that used to accompany it was out of date, and has been removed rather than corrected to a moving target — check Get-VMHost / Get-VM TC2606 | Select MemoryAssigned live rather than trusting a written-down number here.

When the host runs out, AWC returns HTTP 500 on loadObjects / getProperties / getDeclarativeStyleSheets and the page reads "Failure in loading Summary. Please try again." That is indistinguishable from a broken style sheet, and the wrong conclusion is always the more publishable one. Three instances in two days: "the search index is corrupt" (a destructive index clear was nearly run on it), a stale serverPool.properties measurement committed and deployed to a live KB site, and "AWC rejects transplanted sessions" written up as a harness defect. Each looked like a crisp, specific finding about someone's own work.

Check memory and pool state BEFORE believing any tier symptom, not after the investigation stalls. Get-VM TC2606 | Select MemoryAssigned,MemoryDemand plus guest FreePhysicalMemory takes one call and settles it.

Time-correlation beats structure. The same code succeeding at 07:40 and failing at 07:44 is starvation. Reach for that comparison before theorising about payload shape.

Recycling the pool does NOT relieve starvation, and neither does raising the cap. Measured 2026-08-09, and this is the counter-intuitive part:

tcserver WS   5.01 GB -> 2.88 GB     pool 17 -> 11, freed 2.1 GB in the guest
guest free    0.65 GB -> 0.55 GB     went DOWN
VM assigned  14.97 GB -> 13.23 GB    <- Hyper-V reclaimed every freed page
host free                  0.72 GB

Dynamic memory balloons anything freed straight back to the host. The recycle worked and bought the tier nothing, so a pool recycle here is a mechanism that succeeds with no effect. Capping the pool fails identically. With the host at <1 GB free the 16 GB cap is not the binding constraint either. The only lever is host RAM, which is not a Teamcenter setting: the host's own consumers (Claude sessions, a Cameo javaw at ~3.7 GB) compete directly with the guest.

⇒ Report starvation to Chris as a host question. Do not tune Teamcenter for it, and do not report a recycle as a fix without re-reading guest free memory afterwards.

A successful lightweight probe is not a healthy tier. tc_connect + tc_get_properties can succeed while renders still 500, because the render path is much heavier. Say which question the probe answered. Under starvation a success is conclusive and a failure is not, so hold any conclusion that rests on a failure.

Connecting to Teamcenter

tc_connect(profile="vm2606")      # ordinary user, default for day-to-day work
tc_connect(profile="vm2606dba")   # DBA, only when the task genuinely needs it

Credentials resolve in this order: explicit arguments, then the profile's credFile, then userEnv/passwordEnv. Both VM profiles use credFile (%USERPROFILE%\.xcelerator\credentials\tc-vm2606.cred.xml and ...dba.cred.xml), a PowerShell Export-Clixml PSCredential DPAPI-encrypted under Chris's Windows account, so the files are inert on any other machine. A declared credFile that cannot be read or decrypted raises rather than falling back to the env vars, because silently logging in as a different identity is the worse failure.

Confirm what a profile would use without decrypting anything:

tc_profiles()   # expect credentialSource: credFile, credFileExists: true

Two credential gotchas that cost time

A running process keeps the environment it inherited at launch. Clearing HKCU\Environment does not change an already-running MCP server, so tc_connect can keep succeeding on variables that no longer exist on disk. That is not evidence the server restarted. To prove a restart, look for a field only the new code emits (for example credentialSource in tc_profiles output), not for a successful login.

Get-Credential raises a separate interactive dialog, so chaining with ; on one line does not work: the chained command runs without the prompt ever appearing. Give Chris one credential per command block.

★★ DPAPI decryption commonly fails under WinRM's network logon, even for the same user and machine that created the credential file. Import-Clixml against a credFile from inside a plain Invoke-Command -ComputerName ... -Credential $c session is likely to fail with "Key not valid for use in specified state": the network logon type WinRM uses doesn't carry the per-user DPAPI master key the way an interactive logon does. Verified 2026-08-08 running a postinstall orchestrator that needed to decrypt a credFile: the fix was a scheduled task registered with -LogonType Interactive, so credential creation and script execution shared one real interactive logon context rather than crossing a WinRM session boundary. Confirmed working: Credential source: credential file ... (user infodba) printed cleanly from inside the scheduled task. If a script that decrypts a DPAPI file works when run by hand at the console but fails identically over WinRM, this is the first thing to check, not a re-investigation of the credential file itself.

Release-matched documentation

The 2506 and 2606 kits both live on this machine and are separate on purpose. Other work still targets 2506. For anything about this VM, use 2606.

Kit 2606 (this VM) 2506 (other work)
WSDL + XSD D:\Siemens\Help Server\tc2606_SOA_Client_wntx64\soa_client\wsdls\ D:\Siemens\Help Server\soa_client_wntx64\soa_client_wntx64\wsdls\
Services Reference D:\Siemens\Help Server\docs-teamcenter-2606-services_reference\ ...-2506-services_reference\
Javadoc D:\Siemens\Help Server\docs-teamcenter-2606-javadoc\ ...-2506-javadoc\
Product help PDFs D:\Siemens\Help Server\collections\documentation\external\PL20251212545240207\en-US\tc_help\ ...\PL20241125556497283\en-US\tc_help\

989 xsd and 985 wsdl files in the 2606 set, against 983 xsd in 2506. The collection IDs are opaque, so confirm a release by opening any PDF's title page rather than trusting the folder name.

The bug class this VM taught

getTCSessionInfo returned a bare InternalServerException and looked like a broken session or a dead server. It was neither: the operation is defined only in Core-2007-01-Session, in exactly one schema file (Core0701Session.xsd), and tc-mcp was calling it on Core-2011-06-Session.

A bare InternalServerException with no fault detail usually means the operation is not defined at the service version you asked for. Grep the release-matched kit for the operation name to find its one true version:

grep -l "getTCSessionInfo" "D:/Siemens/Help Server/tc2606_SOA_Client_wntx64/soa_client/wsdls"/*.xsd

See tc-soa-docs-navigation for why the schema is additive per version and why the filename release number is not the release you are running.

Before any install or module change

Deployment Center is installed, at C:\apps\DC, but its services are Manual and stopped by default and must be started before DC will run.

Adding a Teamcenter module is a real install, usually with a database schema change, not a config toggle. Stop the Teamcenter services with the desktop shortcut, shut the VM down, checkpoint, then work. See tc-deployment-center.

Do not conclude something is absent from a filtered service query. An earlier pass here reported "no DC service installed" purely because the query filtered on a Teamcenter|FSC name pattern that DC's services do not match. Enumerate without a name filter before claiming absence.

★★★ Running another session's install package on a shared tier

Several sessions author against this VM at once, so read what a package will overwrite before you run it, not after. Near miss, 2026-08-08: a Mission Engineering package asked to be deployed and would have written AWC_Fnd0LogicalBlockRevision.SUMMARYRENDERING. Another session had already claimed that preference for a Port Audit tab it had just got working after a day of debugging. A second SUMMARYRENDERING registration replaces, it does not merge, so the install would have silently deleted a working tab with no error anywhere. The package was corrected to inject an extra <page> into the existing host style sheet and write no preference at all.

The check that catches this class of thing, before running any deployment utility here:

  1. What preferences does it write? Read them back first (getPreferences, or preferences_manager -mode=export -scope=SITE -out_file=...; the flag is -out_file, not -file) and see whether anything already holds the name.
  2. Is the registration single-valued? If so, assume replace, not merge.
  3. Who owns the current value? Ask that session before overwriting it.

⚠ And the scope trap underneath it: a preference written at protectionScope: User is stored, reads back with the correct value, and is silently never resolved by AWC. preferences_manager landed one at User even with protectionScope="Site" in the XML and -scope=SITE on the command line. Assert on the scope in the read-back, not just the value, or you will chase a phantom cache problem and reach for a pool restart that cannot help.

Related skills

tc-soa-session, tc-soa-docs-navigation, tc-deployment-center, tc-verify-and-cleanup.

Serving Teamcenter over HTTPS (EXERCISED 2026-08-15)

Teamcenter Security Services requires SSL before install, so this is on the path to any SSO work. AWC here is a Node gateway, not Tomcat, and it holds the TLS switch.

★★ Checkpoints are split into pre-SSL (3000, plain HTTP) and SSL (443, HTTPS), and which one you are on changes what "healthy" looks like. Establish it before any port or URL check:

$vm = Get-VM TC2606
Get-VMSnapshot -VMName TC2606 | Where-Object { $_.Id -eq $vm.ParentSnapshotId } | Select-Object Name, CreationTime
  • The healthy baseline byte length is the same across the split: EXERCISED 2026-08-16 on 443, https://SIEMENSDC/ and https://192.168.222.100/ both returned HTTP 200 len 2691, identical to the figure recorded on plain 3000. So the length comparison stays valid.
  • Guest PowerShell 5.1 has no -SkipCertificateCheck and needs the ICertificatePolicy override with a distinct class name per remote call. The host runs PowerShell 7, which does have it.
  • The tc-mcp vm2606 profile is already https://siemensdc. Its default profile is cloud2506, a different tier, so pass profile= on every call.
  • The startup script's idempotency guard hard-codes 3000 and is therefore inert on an SSL checkpoint. See the guard section above before you rely on it.

The gateway binds ONE port and serves it as HTTP or HTTPS, never both. There is no way to keep plain 3000 alive alongside TLS in a single gateway process. Read out of the shipped source, microservices/gateway/lib/expressServer.js:

// :401  the switch, and it is purely "are both paths non-empty"
const secure = argv.https || keyPath && keyPath.length > 0 && certPath && certPath.length > 0 || false;
// :608
const _server = secure ? await createHttpsSvr( app ) : await createHttpSvr( app );
// :328-329  PEM TEXT. A PFX will NOT work.
options.key  = await readFile( keyPath,  { encoding: 'utf8' } );
options.cert = await readFile( certPath, { encoding: 'utf8' } );

Edit C:\apps\PLM\tc_root\microservices\gateway\config.json: set port, keyPath, certPath, and forceSecureAttributeOnCookies to true (otherwise session cookies are issued without Secure over a TLS connection). Use forward slashes in the paths to dodge JSON backslash escaping. Validate the JSON before writing it: a malformed config means the gateway never starts at all, and the failure looks like a dead web tier.

The gateway is supervised by Teamcenter Process Manager, NOT Teamcenter WebTier. Restarting WebTier will not reload it. Config is read only at process start.

Write PEM with no BOM. readFile(..., 'utf8') would carry a BOM straight into the PEM parser. Use [System.IO.File]::WriteAllText with UTF8Encoding($false) and assert the first three bytes are not EF BB BF.

Verifying it, with the controls that make the result mean something

A success alone cannot distinguish "trusted" from "validation silently disabled", so every positive needs a paired negative:

# POSITIVE: full validation
openssl s_client -connect <host>:443 -servername <fqdn> -verify_hostname <fqdn>   -CAfile ca.crt -verify_return_error        # want: Verify return code: 0 (ok)
# NEGATIVE 1: a decoy CA that never signed it  -> want verify error num=20
# NEGATIVE 2: right CA, wrong hostname        -> want code 62 hostname mismatch

Check a JVM client separately. A JVM does not read the Windows certificate store. The clean A/B is the JDK truststore against its own pre-CA backup:

java Fetch.java https://siemensdc/                                    # -> HTTP 200
java -Djavax.net.ssl.trustStore=...\cacerts.bak-<date>-pre-<ca> ...   # -> SSLHandshakeException PKIX

Two instrument traps that cost time here

  • Windows curl.exe uses schannel, which cannot do SNI against a bare IP and fails a private CA with curl: (60) the revocation status is unknown. That is a revocation complaint, not a trust failure, and it reads exactly like one. Use --ssl-no-revoke, or verify with openssl instead.
  • MSYS2_ARG_CONV_EXCL="*" in Git Bash stops path conversion, so an openssl -CAfile /posix/path silently fails to open the file. A negative control that never ran looks identical to one that passed. Pass Windows-style paths, and always print proof the control file exists before trusting a "failure".

Client trust is a separate job from serving TLS

Serving TLS does not make anything trust you. tc_client uses urllib, not requests, so REQUESTS_CA_BUNDLE does nothing; Python on Windows reads the Windows ROOT store, so importing the CA there fixes browser, curl and tc-mcp together. ⚠ That import needs an elevated interactive shell: from a non-interactive session Import-Certificate fails with UI is not allowed in this operation, and Cert:\CurrentUser\Root is no easier.


Generated from skills/tc-vm-operations/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.