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.
- VM lifecycle:
MARTINI-GAMING\chrisis in the localHyper-V Administratorsgroup, soGet-VM,Checkpoint-VMandRestore-VMCheckpointall work unelevated. IfGet-VMreturns "You do not have the required permission", the group membership is missing or the logon token predates it: sign out and in. - Guest OS: WinRM over the NAT switch. Credential is DPAPI-encrypted at
%USERPROFILE%\.xcelerator\credentials\vm-admin.cred.xml. - Teamcenter: tc-mcp profiles
vm2606(ordinary user, the default) andvm2606dba(DBA, use sparingly). - Credentials: DPAPI
credFileper 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.
DefaultInboundActionreadsNotConfiguredon Domain, Private AND Public.- There is an enabled inbound Allow rule literally named
ALL_Ports, withLocalPort AnyandRemoteAddress Any. - Consequence: port 8080 is reachable from the host, and the Jetty web tier
answers there in plaintext (HTTP 404 at
/tcfrom the host, i.e. the server responded). The carefully host-scoped rules for 3000/4544/8070/3389 are decorative whileALL_Portsexists.
⇒ 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 8073 → Get-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.
lmgrdstarted 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 a5_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.ps1replaces the blindtimeout /T 20after 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 afternet start "Teamcenter Server Manager TCDB_PoolA"waits for a realtcserverto 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, thentaskkill /Fonvault.exe,java.exe,javaw.exe,tcserver.exe,node.exe,windowsService.exeand ~15 morermdir /S /Qon%userprofile%\FCCCache,%userprofile%\FSCCache,%userprofile%\Siemensand%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
indexflowlock, and was about to run a destructiverunTcFTSIndexer -task=objdata:clear. All three were artefacts of the swapped database, not real faults. - The same session measured
serverPool.propertiesatPROCESS_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 reads8/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:
- What preferences does it write? Read them back first (
getPreferences, orpreferences_manager -mode=export -scope=SITE -out_file=...; the flag is-out_file, not-file) and see whether anything already holds the name. - Is the registration single-valued? If so, assume replace, not merge.
- 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/andhttps://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
-SkipCertificateCheckand needs theICertificatePolicyoverride with a distinct class name per remote call. The host runs PowerShell 7, which does have it. - The tc-mcp
vm2606profile is alreadyhttps://siemensdc. Its default profile iscloud2506, a different tier, so passprofile=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.exeuses schannel, which cannot do SNI against a bare IP and fails a private CA withcurl: (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 withopensslinstead. - ⚠
MSYS2_ARG_CONV_EXCL="*"in Git Bash stops path conversion, so anopenssl -CAfile /posix/pathsilently 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.