Skills
TC Deployment Center
Skill
tc-deployment-center. Teamcenter Deployment Center - the centralized web app that models environments, holds the software repository, and generates the deployment scripts that actually install or update Teamcenter on target machines. Covers the concept model (Environment / Software / Applications / Components), the repository rules that silently hide kits, quick deploy and its utilities, and how custom DC-compatible packages get built. Use for installing or updating Teamcenter modules, and for authoring Deployment Center packages.
Grounding status, read this first. Everything marked VERIFIED was checked against the live
TC2606VM. Everything else is distilled from the 2606 product documentation and has not yet been executed here. Deployment Center is not installed or running on this VM as of 2026-08-05. Do not report a DC action as done on the strength of this skill alone: run it, then verify. Seetc-verify-and-cleanup.
What it actually is
A centralized web application. You model environments in it, point it at a software repository, configure components, and it generates deployment scripts. The scripts are what install or update software on each target machine. Deployment Center does not reach out and install things itself.
That split matters for automation: the DC web app is a configuration and script-generation surface, and the real work happens when the generated script runs on the target. Two phases, always.
The concept model
Four terms, used precisely throughout the product. Getting them straight avoids most confusion.
| Term | What it means |
|---|---|
| Environment | A centrally-managed Teamcenter environment you model in DC |
| Software | DC-compatible software kits: either downloaded from Support Center, or custom packages created by Business Modeler IDE |
| Applications | The binaries, files and data model comprising a set of Teamcenter functionality, contained in a DC-compatible package |
| Components | Logical representation of infrastructure (Corporate Server, Database Server, FSC, Server Manager, Web Tier, Indexer, Licensing Server, Microservice Node, ...) |
★ For the package-authoring work: custom DC-compatible software packages are
produced by Business Modeler IDE, not hand-assembled. That is the supported
route into the Software tier, and the starting point for any "create a
Deployment Center package" task.
The software repository, and the rule that hides kits
DC scans configured repository locations on a schedule. Scanned kits show up on the Software Repositories page, which also flags missing dependencies.
★★ Kits must be UNZIPPED, and they must sit in a software directory. A
zipped kit, or an unzipped kit in the wrong folder, simply does not appear. It
does not error. This is the first thing to check when a kit you know you
downloaded is not selectable.
Three storage strategies, per the docs:
- Single local repository on the DC web server. Simplest, use when there is no spare space near the targets.
- Multiple locations shared with the target environment, reachable by UNC path. Plan for the full software set.
- Copy to each target server and pass the path via
-softwareLocationat deploy time. Fastest install, most disk.
Repository locations are specified when you install or upgrade DC, and managed afterwards under the maintenance settings.
Deployment flow
- Start and log on to Deployment Center.
- Set up the software repository (unzipped, in
software). - Create or register an environment.
- Select software, options, applications, then configure components.
- Generate deployment scripts.
- Run them in diagnostic mode first.
- Run them for real on each target machine.
★ Diagnostic mode is the free safety net. It validates the configuration on each target and writes errors to the log while making no changes to the machine. The documented best practice is to loop: run diagnostic, fix in DC, regenerate scripts, repeat until clean. Given how expensive a bad Teamcenter install is to unwind, treat diagnostic mode as mandatory, not optional.
Passwords entered for components are encrypted in the generated scripts, or you can supply an already-encrypted password.
Quick deploy
A lighter path that manages configuration and generates scripts through a utility rather than the full UI walk.
dc_quick_deployis the utility.- It is driven by an XML configuration file, with an optional XML override file layered on top.
- Quick Deploy can share components across environments.
Useful when the same environment shape gets rebuilt repeatedly, which is exactly the demo-VM pattern.
Utilities
| Utility | Role |
|---|---|
send_configuration_to_dc |
Push a configuration into Deployment Center |
deploy |
Run a generated deployment |
dc_quick_deploy |
Quick deployment path |
All three honor HTTPS once it is configured.
HTTPS and secrets
Two settings that are coupled, and getting them wrong produces confusing browser errors rather than clear failures.
useHttpsCommunicationdefaults tofalse. Setting ittrueenables HTTPS and disables HTTP entirely: DC then refuses all HTTP connections. An unexplained browser error after enabling it usually means the URL is stillhttp://.onlineSecretsDistributioncontrols where deploy scripts get secrets.true(online) means scripts fetch from the DC Vault at deploy time.false(offline) means secrets are encrypted into the generated packages.- ★ If
onlineSecretsDistributionistrue,useHttpsCommunicationMUST betrue, otherwise secrets cross the wire unencrypted.
Certificates can come from the DC Vault acting as a local CA, a real CA, or a self-signed cert. Self-signed needs an associated TrustStore and password.
Registering an existing TEM environment
This VM was built with TEM, not DC, so this is the likely entry path.
- Scan configurations from TEM.
- Generate a validation report.
- Review and perform the actions it lists.
- Complete environment registration (including importing Java EE web apps).
Note the standing division of labor: actions DC cannot yet perform are done by running TEM on the target server. DC does not fully replace TEM.
Naming collision worth flagging
Deployment Center has its own environment snapshots (create, review, restore, delete) for backing up and restoring an environment's configuration.
★ These are not Hyper-V checkpoints. A DC snapshot captures environment
configuration; a Hyper-V checkpoint captures the whole virtual machine. When
someone says "take a snapshot before this install", establish which one they
mean. On this VM the machine-level protection is the Hyper-V checkpoint: see
tc-vm-operations.
Documentation map
The 2606 collection is PL20251212545240207; 2506 is PL20241125556497283. The
folder IDs are opaque, so confirm by the PDF title page, which states the
software version explicitly.
D:\Siemens\Help Server\collections\documentation\external\PL20251212545240207\en-US\tc_help\
Deployment Center Usage.pdf <- concepts, install, maintain, quick deploy
Teamcenter Installation Using Deployment Center.pdf
Teamcenter Update Using Deployment Center.pdf
Teamcenter Environment Manager Usage.pdf <- TEM, still needed alongside DC
Teamcenter Installation on Windows Using TEM.pdf
Deployment Center - Usage chapter map: 1 Introduction, 2 Installing DC,
3 Quick Start registering a TEM environment, 4 Maintaining DC (backup, password
encryption, maintenance parameters, managing repositories), 5 Deploying software
(UI, scripts, process bar, quick deployment), 6 Managing environments (verify,
create, move/uninstall, snapshots, clone, migrate JRE), 7 Registering TEM
environments, A Troubleshooting, B Uninstalling.
The Teamcenter Deployment Reference Architecture package is referenced by the
docs for worked deployment examples and sample deploy scripts, but it ships
separately from Support Center Downloads and is not on this machine.
State on the TC2606 VM
★★ Deployment Center IS installed here, at C:\apps\DC. Its services are
Manual start type and Stopped by default, and they must be started before
Deployment Center will run at all. Chrome on the guest has a bookmark for it.
Do not read "DC is not in the running-services list" as "DC is not installed".
An earlier pass in this workspace concluded exactly that, because the service
query filtered on a Teamcenter|FSC name pattern that DC's services do not
match. Enumerate services without a name filter, or filter on state, before
concluding anything is absent.
VERIFIED 2026-08-05 by enumerating every service on the guest. The four DC
services, all Manual and all Stopped:
| Service | |
|---|---|
DC_Service |
the Deployment Center web application |
DC_RepoService |
the repository service (scans for software kits) |
DC_RepoService_Publisher |
repository publisher |
DC_Vault_Service |
the DC Vault, the CA and secrets store |
★ There is a dedicated start script, and the general Teamcenter start script does not include DC:
C:\apps\PLM\Misc\Startup\Services\Start\Start_DC_Services.bat
C:\apps\PLM\Misc\Startup\Services\Stop\Stop_DC_Services.bat
Run it the same way as the Teamcenter start script: over WinRM, stdin from
NUL, bounded wait, then judge by service state and listening ports rather than
by the script exiting. See tc-vm-operations for the exact invocation.
Note DC_RepoService being a service in its own right: the repository scan that
makes kits visible is that service's job, so "my kit does not appear" has two
possible causes, the layout rule above and the repo service simply not
running.
- The environment was built with TEM, via the
Vanilla_Tc2606_July2026overlay on a Windows Server 2022 base image. - TC2606 installation media is staged on a second virtual disk,
Deployment Center Software.vhdxat SCSI 0:2, about 16.4GB unpacked. - Checkpoint
TC2606 baseline + DC media disk (offline, pre-Deployment-Center) 2026-08-05is the clean pre-install restore point, taken with the VM Off.
Running it (VERIFIED 2026-08-05)
http://siemensdc:8070/deploymentcenter/#!/login
http://192.168.222.100:8070/deploymentcenter/ (from the Hyper-V host)
Plain HTTP on 8070, so useHttpsCommunication is false here. DC also
listens on 8072, 8073, 8074/8075 and 8200/8208. A host-scoped firewall rule for
8070 was added on the guest (the guest blocks everything by default).
Start with Start_DC_Services.bat as described above. All four services came up
and, unlike the Teamcenter start script, this one exits cleanly.
★ < NUL breaks the Windows timeout command. The DC script emitted
ERROR: Input redirection is not supported, exiting the process immediately at
each timeout, which simply skipped its built-in waits. Services still started,
because net start is synchronous. But be aware that redirecting stdin
neutralizes pause and defeats deliberate waits, so a script that depends on
a pause between steps may race.
Driving DC over REST (VERIFIED 2026-08-06)
DC has a JSON REST API on the same port as the UI, and it behaves a lot like Teamcenter's. Three rules, all learned the hard way:
- HTTP Basic auth works.
GET rest/isLoggedInwith anAuthorization: Basicheader returns{"authenticated":true,"authorities":[{"authority":"ROLE_USER"}]}and setsJSESSIONID. This means DC can be driven entirely from PowerShell with a DPAPIPSCredential, so the password never has to be typed into a browser or pasted into a transcript. - POSTs 403 until the session holds an
XSRF-TOKENcookie. Do a GET first to mint one, then echo it in anX-XSRF-TOKENheader. Same bootstrap shape Teamcenter's JSON REST surface needs. - ★★ DC returns HTTP 200 on faults, exactly like Teamcenter. A wrong path
returns
200withheader.status = "008"andDCXXE000 : Severe error occurred : No endpoint .... Success isheader.status = "000". Never treat 200 as success here. This also makes blind endpoint guessing useless: everything "works" and nothing is real.
Calls are POST rest/<area>/<operation> with a JSON body carrying a context
object, not REST-noun GETs. The 24 endpoints referenced by the UI:
rest/isLoggedIn rest/activeUser rest/activeProfile
rest/user/getUserId rest/metadata/getCommands
rest/gateway/getGatewayTiles rest/configuration/getDisplayLayoutConfiguration
rest/configuration/getDCSettingsConfiguration
rest/components/getPrimaryDetails rest/components/getSecondaryDetails
rest/components/updateSecondaryDetails rest/components/updateBreadCrumbDetails
rest/components/createApplication rest/components/removeApplication
rest/copy/copyEnvironment rest/quickdeploy/exportEnvironment
rest/envRegistration/envRegistrationComplete
rest/reports/generateSoftwareReport rest/reports/downloadSoftwareReport
rest/validationreports/generateValidationEnvironmentReport
rest/validationreports/downloadValidationReport
rest/analytics/getAnalyticsDetails rest/analytics/saveAnalyticsEventFromUI
rest/analytics/updateProcessedEvents
Working entry point, returns the four tiles (environment, software,
machine, plus Getting Started):
POST rest/gateway/getGatewayTiles body: {"token":"gateway"}
Layout for a tile, which yields body.subLocations[].id and .tabs[].id
(note the plural field names):
POST rest/configuration/getDisplayLayoutConfiguration
body: {"context":{"locationId":"environment"}}
-> subLocations[0].id = "all"; tabs = "deploySoftware", "overview"
★★ Reading a DC view: the call that actually works
getPrimaryDetails is the wrong operation for these pages. The UI reads
Environments and Software Repositories through getSecondaryDetails. Chasing
getPrimaryDetails costs hours and always ends in DCVF0008.
The verified request, recovered from dc_services.log:
POST rest/components/getSecondaryDetails
{
"context": {
"locationId": "environment", // or "software", "machine"
"subLocationId": "all",
"tabId": "overview", // or "deploySoftware"
"keyPair": null,
"mode": "view",
"secondaryDetailsPage": false,
"displayType": "secondary" // ← lowercase literal, NOT a *_DISPLAY_TYPE token
},
"objectId": 1, // ← on the REQUEST, not inside context. Mandatory.
"deselectedComponentIds": null
}
Three traps in that one payload, each of which produced a misleading error:
- ★
displayTypeis the lowercase string"secondary". The nineteen*_DISPLAY_TYPEconstants in the jars are a red herring for this call; every one of them fails. The error names thedisplayTypefield either way. - ★
objectIdsits on the request, not in the context, and omitting it givesDCTE001 : ... mandatory parameters are missing, which does not say which one. - ★ The context needs
keyPairandsecondaryDetailsPage, fields the client JS never mentions. Full-struct rule, same as Teamcenter.
Response shape: body.tabs[].viewSections[], where a section carries
components[] of {dataId, dataName, subtitles[]} and each subtitle is
{id, label, value} (deploymentStatus, software.version,
machine.hostName). refObject names the section: installedSoftware,
packaze (yes, spelled that way, matching the PACKAGE:"Packaze" typo in
DCConstants.js), or environment.
★★ The oracle that ended this: C:\apps\DC\webserver\logs\dc_services.log.
It logs every request at DEBUG with the fully deserialized struct. Have someone
click the page once in the UI, then read the exact call back out of the log. That
is faster and cheaper than any amount of payload guessing, and it needs no
browser automation. Reach for it first, not last.
Historical: the getPrimaryDetails dead end
rest/components/getPrimaryDetails always returns
DCVF0008 : Unable to find View Formatter for given Display Type.
The 19 valid tokens were recovered, by decompressing the server jars (class
strings live in the constant pool, and jar entries are deflated, so a raw grep of
the .jar finds nothing):
BASIC_TABLE BREADCRUMB COLLAPSIBLE COMPONENT COMPONENT_HEADER
COMPONENT_PROPERTY COMPONENT_UNINSTALL DEPLOY_INSTRUCTION EDIT_BUTTON
GRAPH INFORMATION INVALID NAVIGATION_BUTTON PROPERTY SOFTWARE_OPTION
SOFTWARE_OPTIONS TABLE TREE WARNING (each suffixed _DISPLAY_TYPE)
★★ All ten plausible ones fail identically, so displayType is not the
discriminator. Something else in the request shape is wrong, and the error
message points at the wrong field. Do not burn more time permuting this value.
The displaySection.value names from the layout
(OverviewTabPropertyGroups, OverviewTabApplicationGroups, ...) are the more
likely missing input.
★ Read C:\apps\DC\webserver\logs\dc_services.log instead of guessing. It
logs every request at DEBUG with the full deserialized struct, which is how the
real DCContext shape was recovered:
DCContext [locationId=software, subLocationId=null, tabId=null, keyPair=null,
mode=null, secondaryDetailsPage=false, displayType=null]
So the struct has keyPair and secondaryDetailsPage, which the client
JS never reveals. Sending the complete struct still does not fix
getPrimaryDetails, but this log is the single best oracle on this server: it
shows exactly what arrived, and dc_server_error.log shows why it failed.
The way in is to capture the UI's own network calls: see tc-capture-awc-calls,
same technique and same reason. Note the in-app browser pane cannot reach the
VM's private address (navigation ... was denied or failed), so that capture
has to happen from a browser that can, such as Chrome inside the guest. ★ The
cheapest version of this: have someone load the DC UI once and click into
Environments, then read the successful call straight out of dc_services.log
and replicate it. No browser automation needed. This is exactly how the
working call above was finally recovered.
SOFTWARE_OPTION(S)_DISPLAY_TYPE is the token to reach for when the options
screen is eventually driven, since options are what an MBSE-style add-on is
selected through.
The quick-deploy CLI is NOT installed here
The docs describe dc_quick_deploy, deploy and send_configuration_to_dc.
On this VM only C:\apps\DC\repository\system\deployer\deploy.bat exists (the
runner for already-generated deploy scripts). There is no dc_quick_deploy
binary. If it is needed, look inside the kit's
install\DeploymentCenter\deployment_center.zip or download it separately.
Do not assume the documented utility set is present.
★★ Not every application is visible, and the list will lie to you
Each application has a registry entry in the kit at
<kit>\dc_contributions\application_registry\<appId>_config.json:
{
"appId": "mdw0magicdraw",
"attributes": { "defaultSelection": false, "visible": false },
"requireDependencies": [ "uml0umlsysml", "behaviormodeling" ],
"requiredByDependencies": [ "ase1systemsmodeling" ]
}
★ "visible": false means the application never appears in the DC
Applications list, in the UI or over REST. It is installed implicitly, pulled
in by whatever requiredByDependencies names. mdw0magicdraw is a real example:
its display name is "Integration for Cameo", it is genuinely installed on the
VM, and it is absent from all 184 rows the API returns.
So "it is not in the applications list" does not mean it is not installed. Confirm against the tier itself:
Get-ChildItem C:\apps\PLM\tc_root\install | Where-Object Name -match 'mdw0|uml0|ase1'
An installed template leaves a directory there with its *_config.json and
*_dependency.xml. That check is authoritative where the DC list is not.
Two more mapping traps worth knowing:
- Display names live apart from appIds.
<appId>DCBundle_en_US.xmlunderdc_contributionsholds thedisplayName, one file per locale. Grepping the English value is how "Integration for Cameo" was tied back tomdw0magicdraw. Neither the appId nor the feature XML contains the product name. - The product name may not be the vendor name. Cameo is MagicDraw-based, so
the appId is
mdw0magicdrawand the string "cameo" appears nowhere in the media manifest. Searching for the marketing name alone finds nothing.
Worked chain from this VM: System Lifecycle Management
(ase1systemsmodeling, visible, installed) implicitly pulls in Integration for
Cameo (mdw0magicdraw, hidden), which in turn requires uml0umlsysml and
behaviormodeling. All four are present in tc_root\install.
Quick Deploy config format
C:\apps\DC\repository\quick_deploy_config\<name>\<name>_quick_deploy_configuration_<ver>.xml
is the documented, UI-free automation path, and its shape is simple:
<quickDeployConfig configName="MechatronicsQuickDeploy" version="1.0">
<deploymentTier value="2" />
<quickDeploySoftware>
<software id="Foundation" version="12.2" />
<software id="ActiveWorkspace" version="4.2" />
</quickDeploySoftware>
<quickDeployApplications>
<package id="smd0systemmodeling" displayName="System Modeling" />
<package id="systemsengineering" displayName="Systems Engineering" />
</quickDeployApplications>
</quickDeployConfig>
This is where added options are declared, by package id. The shipped
mechatronics sample is the best available reference for real package ids.
Install layout (VERIFIED)
C:\apps\DC holds webserver and repository. The repository is the canonical
DC structure, and its README.txt documents each folder:
| Folder | Role |
|---|---|
software |
The DC-compatible kits. DC scans it automatically and registers what it finds |
dc_contributions |
DC files for installing earlier TC/AW releases. Do not alter |
deploy_scripts |
Generated deploy scripts, per environment, in timestamped folders |
snapshot |
Quick-deploy files backing environment snapshots |
quick_deploy_config |
Quick Deploy configurations |
system |
DC's own state. Do not alter |
https_config, tls_configuration |
Certificate material |
This VM already has DC state: deploy_scripts\Vanilla_Env\install,
snapshot\Vanilla_Env_1, quick_deploy_config\mechatronics, and
https_config\{SIEMENSDC, Vanilla_Overlay_4TClient}. So an environment named
Vanilla_Env is already registered. Do not assume you are starting clean.
★★ The media disk is mounted INTO the repository, by volume GUID
C:\apps\DC\repository\software is not a real directory. It is a Junction
whose target is Volume{7154c769-7361-4f11-800f-5e5769cd9b02}\, the second
virtual disk. That is why the media disk has **no drive letter** and why
Get-Volume in the guest shows only C:.
Do not "fix" the missing drive letter. The disk is deliberately surfaced as
the DC software folder rather than as a lettered volume. (I assigned E: while
investigating, then removed it once the junction explained everything. Assigning
a letter is harmless but pointless, and it hides the actual design.)
Check the wiring with:
(Get-Item C:\apps\DC\repository\software -Force) | Select-Object LinkType,Target
★★ Open issue: the kit sits one level too deep
The media unpacked with a doubled directory, the same SFX artifact that bit the overlay extraction during the build:
software\tc2606_wntx64\tc2606_wntx64\{tem.bat, dc_contributions\, install\DeploymentCenter, bmide\, tc\, ...}
The repository README says to "unzip it and copy the unzipped folder into the
software directory", i.e. DC expects software\<kitFolder>\ to be the kit
root. Here software\tc2606_wntx64\ contains only another tc2606_wntx64, so
the kit root is one level below where DC scans.
★ Evidence says DC copes: the scanner recurses. Its scan cache lists both levels:
C:\apps\DC\webserver\repotool\lastScannedMedia.json
...\software\tc2606_wntx64 (the doubled outer folder)
...\software\tc2606_wntx64\tc2606_wntx64 (the real kit root)
That file is a path to last-scanned-epoch-millis map, not a status report, so it proves the scanner walked the real kit root; it does not by itself prove the software registered as selectable. Confirm on the Software Repositories page before moving anything. Do not relocate the folder on the strength of the doubled path alone.
The scan log also reports two harmless failures, because the junction targets the volume root, so DC sees the volume's hidden folders:
software\System Volume Information -> Error: Unsupported File
software\$RECYCLE.BIN -> Error: Unsupported File
Expect those every scan. They are noise, not a broken repository.
★★ Stop_all_tc_services_sessions.bat also stops the DC services. Despite
the name suggesting Teamcenter only, a Teamcenter restart cycle leaves DC down.
Re-run Start_DC_Services.bat afterwards, and check Get-Service DC_* rather
than assuming DC survived.
The kit itself is DC-compatible: it ships dc_contributions\ (with
deployablecomponents and quickdeploy), install\DeploymentCenter, and
bmide\, alongside tem.bat for the TEM path.
- Whether the repository will live on the media disk or be copied into the guest.
- Which port DC serves on here, and whether it needs its own host-scoped
firewall rule (the guest blocks everything by default: see
tc-vm-operations). - Whether
useHttpsCommunicationshould be enabled for a single-machine demo VM, given it disables HTTP outright.
★★★ The real shape of a translator package.xml, read off the TC2606 media itself
Verified 2026-08-06 by directly enumerating C:\Users\chris\Downloads\tc2606_wntx64.zip
(15.97 GB, 29,990 entries) and extracting dc_contributions\packages\ asyncservice_translator_package.xml — Siemens' own real, shipped package for a
stock Dispatcher translator. This is a stronger source than the DC Usage guide,
which does not cover BMIDE package internals at all (confirmed by reading the
actual help topic behind xid1161106 — it's the "Getting started" concepts page;
the guide's only mention of custom packages is one bullet naming BMIDE as the
authoring tool, no schema).
★ This zip's media is NOT doubled the way the VM's already-extracted copy is.
tc2606_wntx64.zip's root is tc2606_wntx64/ with dc_contributions\, bmide\,
install\DeploymentCenter\, tem.bat directly one level below — the doubled
software\tc2606_wntx64\tc2606_wntx64\... structure documented above for the VM
is an artifact of how that copy was self-extracted, not a property of the shipped
zip itself. Don't assume every future media drop needs the doubled-directory
workaround.
The translator package shape, directly reusable for any new Dispatcher translator:
<package packageId="asyncServiceSel_translator" guid="...">
<textBundle value="${packageId}Bundle.xml"/>
<contentType value="translatorContent"/>
<group value="translators"/>
<dependencies><dependency type="requires" id="dispatcher"/></dependencies>
<artifact artifactType="fnd0_translatorArtifact" guid="...">
<deploymentActions>
<install>
<target name="fileProcessing">
<unzip src="${Teamcenter_Foundation_path}/additional_applications/asynctranslator.zip"
dest="${fnd0_dispatcherRootDirectory}/Module/Translators/asyncservice" overwrite="true"/>
</target>
<target name="preDatabaseUpdate">
<chmod dir=".../Translators/asyncservice" perm="754" includes="**/*"/>
</target>
<target name="databaseUpdate">
<xmlReplace fileName="${dispatcherRootDirectoryModified}/Module/conf/translator.xml">
<replace xPath="/Translators/AsyncService[@service='asyncservice']/@isactive" withText="true"/>
</xmlReplace>
</target>
<target name="postDatabaseUpdate"/>
</install>
<update> <!-- same 4 targets --> </update>
<maintenance><target name="maintenanceUpdate"/></maintenance>
</deploymentActions>
</artifact>
</package>
Key confirmed facts this gives you, none of which are documented in the Usage guide:
contentType="translatorContent"/group="translators"is how a translator package identifies itself to DC.- Dependency on the
dispatcherpackage is declared, not assumed. translator.xml's root element is literally<Translators>, and each translator is a CHILD ELEMENT NAMED AFTER THE TRANSLATOR (<AsyncService service="asyncservice" isactive="..."/>, not a generic<Translator name="...">) — confirmed by thexmlReplacexPath itself, which is real, server-validated Ant task syntax, not documentation prose that could be stale.xmlReplaceis the Ant task for idempotent XML config patches duringdatabaseUpdate— the DC-native alternative to hand-rolling a script-based XML patch, when the change is simple (a single attribute flip). For a change too complex for onexPath/withTextpair (multiple new child elements, an<Options>block, conditional removal of a stale entry), delegating to atcexecPowerShell script frompostDatabaseUpdateinstead (see the DSS pattern below) is the more tractable route — don't fightxmlReplacefor a multi-node edit.dispatcher_module_package.xml(the parent) isdeprecated="true" visible="false"on this release — a hint at API churn worth checking before modeling a NEW package on it specifically;asyncservice's own package is not deprecated and is the safer template to copy.- Translator packages are not separately-selectable top-level DC "Applications"
— no standalone
application_registry\<name>_config.jsonexists forasyncservice/nxtopvdirect/etc. They attach asfnd0_translatorArtifact-typed artifacts under the parentfnd0_dispatcherModulecomponent, confirmed via that component's ownfnd0_dispatcher_module_DCC.xml:<supportedType value="fnd0_translatorArtifact" conditionId="enableTranslatorArtifactCondition"/>. So a new translator's own package needs noapplication_registryentry at all.
bmide\ on this media is thin at the top level — just
compressed_files\{bmide.zip, bmide_aw.zip, bmide_edaserver.zip}. The actual
BMIDE tool and any shipped extension-project samples live inside those nested
zips, not unpacked in the media tree itself.
★★ Combining the two known-good package.xml patterns
Two genuinely different, both-confirmed patterns now exist in this workspace, and a new package should pick the one that fits each piece of what it deploys rather than forcing everything through one:
asyncservice_translator_package.xml (Siemens, shipped) |
dss4decisionsupport_package.xml (this workspace, decision_support) |
|
|---|---|---|
| Best for | Simple file staging + a single-attribute config flip | Complex logic: preference imports, multi-step, arbitrary scripts |
| Mechanism | unzip / chmod / xmlReplace Ant tasks, inline |
getTcAdminPasswordFile + tcexec calling preferences_manager directly, then a second tcexec calling an arbitrary PowerShell script |
| Where the real logic lives | In the package.xml itself | In postinstall/*.ps1 scripts the package.xml just invokes |
For anything beyond a trivial translator (multiple config files, a generated
per-tier JSON, machine environment variables, workflow template handlers) the DSS
pattern — thin package.xml, real logic in an idempotent PowerShell orchestrator —
is far more maintainable and testable in isolation than trying to express it all
in Ant XML. A full worked example combining both (translator content following
asyncservice's shape, postDatabaseUpdate delegating to a PowerShell orchestrator
following DSS's shape) is in Capital_TC_Integration/deploy/bmide/ — built
2026-08-06 for the CapitalForward Dispatcher translator + its workflow trigger +
subscription daemons, not yet deployed against a real instance. Read its own
README before cloning it.
★ decision_support has two install trees; only one is current.
deploy/bmide/ is canonical (BMIDE "Generate Software Package" output plus
hand-authored files assembled by tools/finish-software-package.ps1, the
"3-file config" pattern — package.xml itself, rtserver.zip/aws2stage.zip,
and the final install zip are NOT fully BMIDE-generated, only the template/media/
partial application_registry are). deploy/deployment-center/ is explicitly
marked deprecated (DEPRECATED.md: "Deployment packaging has moved to
deploy/bmide/ (single path)") — don't clone it.
★ Workflow templates as BMIDE ops-data, in case a future package wants to try
this route instead of SOA-scripting the handlers post-creation: decision_support
ships deploy/bmide/workflows/Dss4DecisionApproval.xml, a simplified declarative
<WorkflowTemplate>/<Task>/<Handler>/<Participant>/<Action> format (NOT raw EPM
XML), deployed via BMIDE's native workflows/ ops-data folder
(enableOpsDataDeploy="true" in dependency.xml). Unverified as an alternative
route to the same result the paragraph below now covers over SOA directly.
★★★ RETRACTED 2026-08-08: createOrUpdateTemplate-only-makes-a-task-template
is wrong, and so is "a template's stage is a GUI-only state transition." Both
were measured against a real tier at the time and both were genuinely true of
the payload shapes tried, but neither is true of the operation itself. Captured
live by reading what Active Workspace's own Workflow Designer actually posts,
then reproducing the same calls over SOA, verified end to end on TC2606
including create → publish → delete on a disposable throwaway template before
touching a real one:
createOrUpdateTemplatecreates a genuine PROCESS template (template_classification= 0) whenadditionalDatacarries{"stage":["1"]}on the create call. Every earlier attempt omitted this key and got a Task template every time: the operation was never incapable of it, the payload was incomplete.- Publishing ("Set Stage to Available") is a SECOND
createOrUpdateTemplatecall,templateToUpdate= the template's uid,additionalDatacarrying{"template_stage":["2"]}. Note the key name difference from the read-back property:setPropertiesonstagereally is a documented silent no-op (that half of the original finding holds), butstageis not the write key either:template_stageis, and it only exists on this secondcreateOrUpdateTemplatecall, not as a generic settable property at all.
So the CapitalForward package's own "genuinely unscriptable, manual GUI step"
conclusion (referenced throughout this skill and in that package's own docs)
is retracted too, see Capital_TC_Integration/deploy/bmide/workflows/ CSAIntCapitalForwardTranslate-MANUAL-STEP.md for the package-level writeup,
and tc-workflow-authoring for the full derivation including the exact
payload shapes. The BMIDE ops-data route above remains untested and is now
moot for this specific problem: the direct SOA route is proven, so there's no
remaining reason to reach for the untested alternative.
★★★ First live test on TC2606, four confirmed bugs, all in the postinstall scripts not the package.xml design
Verified 2026-08-06: a colleague session with WinRM access to the siemensdc VM
ran the CapitalForward package's postinstall orchestrator directly (bypassing DC's
own deploy mechanism, which is still unproven on write — see below) against real
paths on TC2606. Nothing was written to the tier — every step that would have
modified TC failed or skipped cleanly before doing so, which is exactly the
behavior a first real run against a new tier should have. Four bugs found, all
fixed same-day:
- A path-computation bug lost one directory segment, computing an
artifacts-lookup path two levels above a script instead of one — the classic
off-by-one from double-applying
Split-Path -Parentwhen only one hop was needed. Caught immediately by the actual error message naming the wrong path. - ★★
preferences_manager.exe(and by extension any TC utility) needs its environment SOURCED IN THE SAME PROCESS, and the site's own env-setup script name is NOT constant across tiers. Confirmed by direct empirical test on this VM:preferences_manager -hwith no environment → exit-1073741515(0xC0000135, STATUS_DLL_NOT_FOUND) and zero output; the identical binary aftercall tc_root\tc_menu\tc_Vanilla_Env.bat→ exit0, full usage text. This VM's environment-setup script lives attc_root\tc_menu\tc_<EnvName>_Env.bat(e.g.tc_Vanilla_Env.bat), not%TC_DATA%\tc_profilevars.batas seen on the cloud/saber2506 tier this integration was originally built against. A script that hardcodes one convention will silently fail on a site using the other. Fix: auto-detect both candidates, accept an explicit override, and run the actual utility throughcmd.exe /c "call <envscript> && <utility> <args>"as ONE process — PowerShell invoking a.batand then an.exeas two separate calls does NOT carry the sourced environment across, because a batch file'scall-sourced variables only persist within that same cmd.exe process. - ★★★ The most important bug: an orchestrator step that skips its own work
(
exit 2, a documented "non-fatal, missing prerequisite" signal used throughout this package) does NOT throw a terminating exception in PowerShell — it just sets$LASTEXITCODEand returns normally. A naivetry { & $Body } catch { ... }wrapper around each step therefore records a fully-skipped step asOK. Confirmed live: a workflow-handler-wiring step reportedOKin the summary table while having wired zero handlers, because its password-file lookup silently failed first. This is the exact "silence reads as success" failure class this whole package's own error messages warn about elsewhere in text — reproduced by the orchestrator wrapping those same scripts. Fix: any multi-step orchestrator wrapping sub-scripts that use exit codes (not exceptions) to signal "skipped" must explicitly check$LASTEXITCODEafter each call, not rely on try/catch alone. - A hardcoded assumption about which credential file already exists on a tier
was wrong, and the colleague session correctly refused to substitute a
look-alike file rather than guess: this VM's
tc_root\security\hadTCDB_infodba.pwf(a database-account** password file) but no TC-user.pwfforinfodbaat all, contradicting the assumption (true on every other tier touched by this integration) that one already ships from install. **Do not assume a.pwfexists just because the tier is "the same kind of install" as another one you've seen — verify the directory listing first, and if genuinely absent, find the real creation utility rather than trying a same-size, differently-prefixed file as a substitute.
The general lesson for any DC postinstall script, beyond this one package: a script correct in isolation can still be silently wrong in an orchestrator that (a) doesn't source the environment the same way the target tier does, and (b) treats "didn't throw" as the only failure signal. Both are easy to get wrong and both produce a false "deployed successfully" report — test the orchestrator's own error-propagation, not just each step's happy path, on a genuinely fresh tier before trusting a summary table.
★★★ Fifth bug, found the same night: getWorkflowTemplates (2013-05) needs a fully-specified payload, or it looks exactly like the deprecated-route symptom
provision-workflow-handlers.ps1's Find-TcWorkflowTemplateUid helper called
getWorkflowTemplates with an empty body and read .templates/.output off
the response. Both were wrong, and both failures produced the same misleading
symptom: "no candidate templates," which reads exactly like the deprecated
2008-06-route trap already known to this workspace (see tc-workflow-authoring),
sending the first debugging pass toward the wrong hypothesis. Live testing
against TC2606 (170 real EPMTaskTemplate objects on that tier) established the
real cause and fix, documented in full in tc-workflow-authoring's
"...but the 2013-05 route ALSO returns an empty-looking result" section: the
request needs four required attributes (clientId, includeUnderConstruction,
getFiltered, group) under a member named input (not inputs), and the
response's real payload lives at templatesOutput[0].workflowTemplates, not
.templates or .output. Fixed in tc-soa-lib.ps1's Find-TcWorkflowTemplateUid
and verified against the corrected shape.
The debugging discipline worth repeating: when a "returns nothing" symptom
matches a known trap exactly, verify the payload and response-parsing before
accepting the known trap as the cause. Two different bugs producing an
identical-looking empty result is not a coincidence to be surprised by; it's the
default outcome when TC returns 200 OK for both "wrong operation" and
"well-formed but underspecified request" alike.
★★★ Sixth bug: tc_profilevars.bat is a SUPPLEMENT, not a standalone environment script, and that's the root cause of the earlier preferences_manager exit-1
Confirmed live on TC2606, 2026-08-07, and this is the resolution of the
"preferences_manager exit 1 (post-env-fix)" item that was open at the end of the
fourth-bug pass above. It looked like a NEW, different failure from the earlier
0xC0000135 DLL-not-found bug (same symptom class, a TC CLI utility failing,
but a different exit code), and turned out to be the same root cause wearing a
different mask.
%TC_DATA%\tc_profilevars.bat assumes TC_ROOT/TC_DATA are ALREADY set by
whatever calls it; it does not establish them itself. Called on its own (bare,
in a fresh process), it leaves both unset: confirmed by inspecting the resulting
environment, where TC_BIN ends up literally \bin, i.e. %TC_ROOT%\bin expanded
with TC_ROOT empty. preferences_manager then fails with exit 1,
"Login with -pf option failed! Error code = 26052 (POM_internal_error)", a
message shaped exactly like a bad credential or bad .pwf file, which is a red
herring. Proof it's the environment, not the credential: Siemens' own
smart_discovery_helper reproduces the identical 26052 under the same broken
env; a plaintext -p= password fails the same way; a read-only -mode=export
fails the same way. A whole family of "this specific option/credential must be
wrong" hypotheses can be ruled out at once by checking whether TC_ROOT/TC_DATA
actually resolved, before touching any of them individually.
tc_root\tc_menu\tc_<EnvName>_Env.bat (the script already identified as this
VM's convention in bug 2 above) IS self-sufficient: it sets TC_ROOT/TC_DATA
itself before internally calling tc_profilevars.bat, which is exactly why it
worked when tried directly, and exactly why any auto-detection logic that tries
tc_profilevars.bat FIRST (reasonable-looking, since it's the convention on
other tiers this integration was built against) silently picks the broken
half-environment and never reaches the working fallback. Fix: try the
self-sufficient tc_menu\tc_*_Env.bat FIRST, and set TC_ROOT/TC_DATA
explicitly in the invoking cmd.exe command line as a second layer of defense,
so that even a tc_profilevars.bat-only fallback (a tier with no tc_menu
folder at all) works instead of reproducing the same bug by a different path.
Two secondary findings from the same investigation, worth carrying into any similar script:
- The utility's report file is NOT under
TC_DATA\logs. It lands in%TEMP%(e.g.C:\Temp\preferences_manager_<stamp>.log), and the utility prints the exact path to stdout on success: point error messages there, or parse stdout for the real path, rather than a guessed conventional location. - ★★ Preference reads are session-cached. A
getPreferencescall on a session that was opened before an import completed can report a just-imported preference as absent, and this genuinely reversed itself after a fresh login: it is not a propagation-delay issue, it is the specific session's cache. A verification step must open a brand-new session (not reuse one opened earlier in the same script or process) after the import, or it can report a false failure on a successful import.
★★★ Three more findings from a third live test, run 3
Verified 2026-08-07, same colleague session, same package. Two real bugs and one false alarm that's worth documenting as a process lesson on its own.
The false alarm: a "still failing" report was actually a stale file
Run 3 reported the exact parse error from the very first attempt at fixing bug
6's error message (an invalid \"-escaped string, since backslash is not an
escape character inside a PowerShell double-quoted string: use a backtick, or
double the quote). That first attempt had already been caught and fixed in the
same edit pass, before ever being reported as ready. The file on the authoring
side parsed clean and had zero remaining \" occurrences, with a save
timestamp clearly after the fix. The run had executed a copy of the file
staged before the fix landed, not the current one. Lesson for any workflow
that separates the machine authoring a script from the machine running it:
when a report describes an error that's already been fixed and verified,
check file provenance (timestamp, a hash, anything) before assuming the fix
regressed. A stale copy is at least as likely as a real regression, and costs
nothing to rule out first.
Bug: SOA call timeouts on a cold session pool, misread as a payload problem
A 60-second Invoke-RestMethod timeout was too tight for two compounding
factors: a cold Teamcenter session pool can take 15-25 seconds just to assign
a tcserver process (confirmed independently: a colleague's own tc_connect
against the same tier timed out twice in a row on a cold pool, then succeeded
on the third try with the tier fully healthy, 21 services / 24 tcserver
processes up), and a heavier call on top of that (getWorkflowTemplates with
getFiltered:false against a 170-template site) returned a 74KB payload that
takes real time to generate server-side. The combination pushed a cold first
call past 60s with nothing to show for it but a bare HTTP client timeout: no
TC-side error, no partial response, nothing to diagnose the actual payload
against. Fix: raise the timeout (120s) and add a small retry-with-backoff
specifically for timeout exceptions (not for real HTTP errors or SOA
faults; those mean the request completed and failed for a substantive
reason, and retrying them just wastes time or masks a real bug). Applied to
both the login call (where the cold-pool assignment actually happens) and
every subsequent operation call. The general lesson: a bare timeout on the
first call against a freshly-started or freshly-restarted Teamcenter tier is
not by itself evidence the payload or route is wrong. Rule out a cold pool
first, especially on a call whose response is large.
Bug: a scheduled-task daemon script can silently double a process that predates it
setup-subscription-daemons.ps1's own header claimed "starting an
already-running daemon is a no-op": this was wrong, and a live run proved it.
After the script ran, TWO subscriptionmgrd and TWO actionmgrd processes were
running against the same database, one pair pre-existing (started by hand
during earlier manual work on the tier, Administrator-owned) and one pair
freshly started by the new scheduled task (SYSTEM-owned). Start-ScheduledTask
always launches a fresh process for its own task instance; MultipleInstances = IgnoreNew only prevents the SAME scheduled task from double-firing. It does
nothing to detect an unrelated process of the same executable already running
outside that task. For any daemon/service-like executable with no built-in
single-instance lock, a deployment script that unconditionally calls
Start-ScheduledTask/Start-Process/equivalent WILL create a duplicate if one
is already running by some other means (manual start, an earlier tool, a
different install path), and this is exactly the class of thing that never
shows up on a genuinely clean VM: it only bites a real, previously-touched
environment, which is precisely the kind DC packages actually deploy to.
Fix: check for an already-running instance (by process name, any owner)
before starting a new one. Registering the scheduled task for future-boot
survival can still happen unconditionally (harmless, idempotent); only the
immediate start should be conditional. If duplicates are found ALREADY
existing (not caused by this run), warn loudly and require manual cleanup
rather than guessing which instance to kill: a script that picks a pid to
terminate on someone else's tier is a destructive action past what an
idempotent deploy step should decide on its own.
★★★ Run 4: -pf= needs the ENCRYPTED file for every TC CLI utility, not just the daemons; the earlier docs had this backwards
Verified 2026-08-07, same package, same colleague session. This closes the
preferences_manager story for good: run 3's "exit 1 / 26052" was the missing
environment (bug 6 above); run 4's NEW "exit 1 / 515143
(POM_invalid_account_values)" was a second, independent bug once the
environment was fixed, and the direct A/B is unambiguous:
-pf=TCDB_infodba.pwf (encrypted) -> exit 0, "7 preferences imported successfully"
-pf=TC_DATA\password_file (plaintext) -> exit 1, 515143 (POM_invalid_account_values)
Same env script, same account, only the file format differs. -pf= on
preferences_manager means the standard encrypted .pwf format, the same
one subscriptionmgrd/actionmgrd use, not a plaintext password file: this
package's earlier design (and its own README) had it backwards, assuming
preferences_manager needed the plaintext file used for scripted SOA login
elsewhere in the package and that the encrypted .pwf was what caused
515143. The opposite was true. Fixed by giving
register-capitalforward-preferences.ps1 its own encrypted-.pwf discovery
(same TCDB_<user>.pwf / config1_<user>.pwf convention already used for the
daemons), reserving the plaintext-password chain for what it's actually needed
for: the script's own optional post-import SOA verification login, which sends
a literal password string in a JSON body and genuinely cannot use an encrypted
file. The general rule, worth checking on any new TC CLI utility wrapper:
-pf= (or any file-based credential flag on a compiled TC utility) almost
certainly wants the encrypted format; a plaintext password only ever belongs
on a scripted SOA/REST login, never on a CLI utility's credential flag, even
when both authenticate as the same account.
★★ A second lesson from the same investigation: don't diagnose an exit code, diagnose the actual output text. preferences_manager returned the identical exit code (1) for two unrelated root causes across run 3 and run 4 (missing environment vs. wrong -pf= file format). An error-handling block that branches purely on $LASTEXITCODE will confidently misdiagnose whichever cause it wasn't written for first: exactly what happened here, run 4 hit the new 515143 case and got served the old 26052 diagnosis, which sent debugging effort toward the environment when the environment was already fine. Fix: capture the utility's actual stdout/stderr text ($output = & cmd.exe /c $cmdLine 2>&1, not a bare & that lets it stream past uncaptured) and branch error messages on substrings of that text, falling back to a generic "check the captured output above" message for anything not specifically recognized. This generalizes to any wrapped CLI utility whose distinct failure modes share an exit code.
★★★ Run 5: a genuinely successful import was misreported as FAILED, by PowerShell itself, not by TC
Verified 2026-08-07, same package. The -pf= fix above worked (report logs
confirmed "A total of 1 preferences has been imported successfully!", exit
code 0), but the orchestrator still printed Register preferences FAILED:
with an empty message. Root cause is a documented Windows PowerShell 5.1
defect, not a Teamcenter problem or a logic bug in the diagnosis code added in
the previous fix:
Redirecting a native executable's stderr AT THE POWERSHELL CALL SITE
(& cmd.exe /c $cmdLine 2>&1) wraps every stderr line the exe writes in a
NativeCommandError, and sets $? to $false, even when the exe exits 0.
preferences_manager writes a single blank line to stderr on a completely
successful run. That blank line was enough to trigger the defect: PowerShell
manufactured an ErrorRecord whose text was the blank line, which is exactly
why the failure message came through empty, and why the substring-branching
diagnosis added for bugs 6/7 above couldn't classify it: there was no real
26052 or 515143 text to match, because there was no real error at all.
Fix: keep the redirect INSIDE the string handed to cmd.exe, not as a
PowerShell-level redirect on the & call itself. $cmdLine already ends
with the command to run; append 2>&1 to that same string (cmd.exe merges
stdout/stderr in its own process before PowerShell ever sees the output) and
invoke it as & cmd.exe /c $cmdLine with no PowerShell-side redirect at all.
This preserves the single-string-argument invocation shape already proven to
work correctly across four prior live runs; do not additionally wrap the
whole $cmdLine in a second layer of quotes to "do the redirect outside" (a
tempting-looking alternative) since cmd.exe /c argument parsing has its own
quote-stripping behavior that a second wrapping layer can collide with when
the inner string already contains embedded quotes, which this one does.
The general rule for any PowerShell script wrapping a native executable:
2>&1 (or any stderr redirect) belongs on the native side of a cmd.exe /c "..." string, never bolted onto the PowerShell & call itself, unless you
have specifically confirmed the target executable never writes anything to
stderr on success. A wrapper that looks correct and has passed every prior
test can still misreport the moment the wrapped executable emits so much as
one stderr byte on an otherwise-successful run: this is a silent,
intermittent-looking failure mode (it depends on the exe's own stderr
behavior on that specific invocation) that will not show up until exactly the
right exe/argument combination triggers it. Worth auditing every script in a
package that shells out to a native utility for the same pattern, not just
the one that happened to trip it first: the audit in this package's other
five scripts found no other occurrence (none of the other native-executable
invocations combine a PowerShell-level 2>&1 with the & call operator),
but that is a fact worth re-checking after any future edit, not something to
assume stays true.
★★★ Design gap found after the fact: subscriptionmgrd/actionmgrd already exist as OOTB Windows services, and this package's scheduled task is a SECOND, competing mechanism
Discovered live 2026-08-07, after the duplicate-daemon bug above was already
fixed and tested. This isn't a new bug so much as a finding that the earlier
fix, correct as far as it went, was papering over a structural design choice
worth reconsidering: TC2606 ships subscripmgrd and actionmgrd as genuine
Windows services (StartType Manual), separate from and racing for the same
ImanEvent queue as any package (like this one) that instead runs the
executables directly under a boot-triggered scheduled task. On a genuinely
clean tier, those OOTB services exist, Manual and Stopped, exactly like this
VM started.
This reframes the duplicate-consumer problem (bug 6, "silently double a process that predates it") as structural, not incidental. The already-running-instance guard added there correctly stops the SAME script from adding a third consumer, but it cannot stop the OOTB service mechanism from racing the scheduled-task mechanism: nothing in Teamcenter's own tooling (a future run of a service-startup script, or an admin manually starting the Manual-type service) knows this package's scheduled task exists, so the double-consumer situation can silently reappear after a clean deploy, at any later point, with no warning from anything this package wrote.
Two real design options, deliberately not decided inside this package: (a)
keep the scheduled task (explicit SYSTEM ownership, unlimited execution time,
which is presumably why it was chosen originally) and document the OOTB
service conflict clearly enough that nobody re-enables it by accident, or (b)
switch the package to configure and start the OOTB Windows services instead
(closer to out-of-the-box behavior, inherits whatever service config the tier
already has, removes the whole duplicate class by construction rather than
guarding against it). The general lesson for any DC package that stands up
a background process: check whether Teamcenter already ships that process
as a Windows service before reaching for a scheduled task, Start-Process, or
any other custom persistence mechanism. A custom mechanism can be made
correct in isolation (idempotent, guarded against self-duplication) and still
collide with an OOTB mechanism neither package author nor operator thought to
check for, because the collision only becomes visible when both are actually
running side by side against the same real backend, which a unit-test-style
check of the script alone will never surface.
★ A live side effect worth knowing about, not just the lesson: cleaning up what looked like a stray duplicate daemon pair on a real tier turned out, on reflection, to have stopped the underlying OOTB Windows services themselves (the processes killed were the services' own child processes, not unrelated strays as first assumed), so the services now show Stopped on that tier as a result. The functional end state (exactly one consumer of the queue, reboot-survivable) was still correct, but which mechanism owns the daemons on that specific tier changed as a side effect of the cleanup, which is worth knowing before drawing conclusions from that tier's current service state later.
★★★ Run 6: the third instance of the same forwarding-gap bug, plus a stale hardcoded port
Verified 2026-08-07, same package. The stderr-redirect fix above worked
(Register preferences now genuinely reports OK), but its optional
verification sub-step silently failed to run: WARNING: Could not verify over SOA (Unable to connect to the remote server).
Two compounding causes, and the first is a pattern worth naming on its own:
A hand-built per-step
$argshashtable in the orchestrator forgot to forward-TcHostto the preferences step. This is the THIRD time this exact bug shape hit this package:-TcDatamissing from workflow handlers (an earlier run),-InfodbaEncryptedPwfmissing from preferences (bug 7 above), now-TcHostmissing from preferences. Each time, the orchestrator held the right value in a variable and simply forgot to include it in one step's hand-written argument list. Fix, generalizable beyond this package: when an orchestrator forwards a growing set of shared values to several sub-scripts with different parameter surfaces, build the values once in a single hashtable and select each step's subset through a shared helper (a "which of these common values does this step declare" map), rather than hand-writing a fresh$args = @{...}per step. A repeated manual pattern that has already produced the identical bug three times is strong evidence the pattern itself, not the individual instances, is the thing to fix. This package's orchestrator now builds one$commonhashtable plus aNew-StepArgs -Map @{ CommonKey = 'TargetParamName' }helper; adding or wiring a new value in the future is a one-line change to one step's map, not a hunt through every step's argument block.The verification step's default web-tier port was hardcoded to 7001, a habit inherited from a different tier this integration was originally built against, and nothing listens on 7001 on TC2606 (the real port is 3000). A wrong default port produces "Unable to connect to the remote server," which reads exactly like a real connectivity problem and gives no hint that the actual cause is a bad guess baked into the script. Fix: remove the hardcoded fallback entirely. No port is universally correct across the tiers this package has touched (cloud/saber2506 vs. TC2606 genuinely differ), so guessing one is guaranteed to be wrong on some tier. Fail toward "skip this optional step with a clear, specific reason" (or, for a step where the connection is the whole point rather than optional, fail loudly with
exit 2before attempting anything) rather than toward silently trying a plausible-looking value. The general rule: a configuration default that was right on the tier a script was originally developed against is not a safe default for a script meant to run anywhere else: if there's no single correct value, don't pick one and hope; require it explicitly or refuse cleanly.
Neither cause was a functional failure of the import itself (which had already succeeded and correctly reported so). This was exactly the "we think it's verified and it isn't" shape this whole testing effort had been guarding against elsewhere, now caught in the one place that had been missed.
Also worth knowing: what looked like a regression was a stale file, not a code fault
A "still failing, identical error" report against an already-fixed script turned out to be the runner executing a copy staged before the fix landed, not a real regression. See the "Three more findings from a third live test, run 3" section above for the full writeup: this is a recurring risk in any workflow where the machine authoring a script and the machine running it are different, and it's cheap to rule out (check a timestamp or hash) before assuming a fix didn't actually work.
★ DC's write path (create Environment / register Software / actually deploy) is still UNPROVEN over REST
Everything in the "Driving DC over REST" section above was exercised as a
read. createApplication / removeApplication and the actual deploy-script
generation and execution have never been called — those endpoint names came from
harvesting string literals out of the DC client JS, not from a successful call.
Whether DC's deploy path can be driven headlessly over REST at all is an open
question, not a proven capability this skill can claim. The proven oracle
technique for cracking it when the time comes is unchanged from the read path:
have someone perform the action once in the real DC UI, then read the exact
request out of C:\apps\DC\webserver\logs\dc_services.log (DEBUG, logs every
request with the fully deserialized struct) and replicate it — that is how the
read path was cracked after hours of failed payload guessing, and it is almost
certainly the fastest way into the write path too.
★★ Headless BMIDE package generation, however, IS confirmed to exist:
C:\apps\PLM\tc_root\bin\bmide_generate_package.bat (shells to
com.teamcenter.bmide.foundation.core.internal.install.PackageProjectMain),
needs the TC environment sourced first (see the env-script trap above). Verbatim
usage:
-projectLocation=<Location of the project which is to be packaged>
[-packageLocation=<Location where the package is to be created>] default: <project_loc>\output
-dependencyTemplateFolder=<Location of the dependency templates>
[-codeGenerationFolder=<Location of the project's compiled generated code>]
-softwareVersion=<Software Version> patterns: 1 | 1.1 | 1.1.1 | 1.1.1.2
[-buildVersion=<Internal Build Version>]
[-allPlatform] [-addTimeStamp] [-skipFoundationVersion] [-log=<Log file>] [-h]
-allPlatform is described as generating a package deployable to a TC business
logic server on ANY platform, "useful when your software package only has a
BMIDE template and no platform specific files such as compiled libraries" — the
right flag for a package like CapitalForward's (data-model + hooks + PowerShell +
a payload zip, no compiled libs), though note the same help text says rt_server.zip
will NOT be built under -allPlatform if the template project is configured to
build one. Unresolved: the help text also says "The project must be loaded
in BMIDE client without errors for validation before packaging" — whether that
is a hard runtime-enforced requirement, a one-time-only precondition, or purely
advisory has not been tested. Cheapest way to find out is to just run the utility
against a real project and read what it actually does, rather than reason about
the sentence further.
Related skills
tc-vm-operations (the VM this runs on, and its checkpoint discipline),
tc-verify-and-cleanup (do not claim an install landed without checking),
tc-soa-session, tc-workflow-authoring (process/task template creation and
publishing over SOA, including the retraction of the earlier "GUI-only"
conclusion),
tc-dispatcher-requests (translator.xml / Service.properties mechanics this
skill's package examples configure).
Generated from skills/tc-deployment-center/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.