Scripting refers to a particular type of programming in which you use s scripting language such as batch, PowerShell, or a Linux command shell to automate a task.

Unattended Install of EMA OrCAD 16.6

This post will teach you how to create an unattended installation of EMA OrCAD 16.6. OrCAD’s setup program is built on InstallShield.  Normally an InstallShield installation can be automated by recording a manual install to a script file (setup.exe /r /f1 “setup.iss”) and playing it back (setup.exe /s /f1 “setup.iss”), but EMA does things a little bit differently.

Configuring the Installation

If you glance through the Install Guide (which you can download with your OrCAD software) you’ll see that the installation is configured by using a silent installation file called silentinstall-SPB.ini. You can tell setup.exe to use the file by running the command setup.exe !quiet=path_to_silentinstall-SPB.ini. Rather than messing with multiple copies of the file, I just edit the one provided and reference it in my install script. Below is a copy of my own silentinstall-SPB.ini file.

[State] ; Ignore the fact that there is far to much junk on my drive IKNOWBEST=YES ; ; defines the install location ; TargetDir=C:\Cadence\SPB_16.6-silent ; ; Reboots if required at conclusion of installation ; RebootIfReqd=N ; ;File Server Location ; ;FileServerLocationDir=G:\SPB_16.6 ; ; identifies desired maintenance behavior ; possible values are: REMOVEALL, MODIFY, REPAIR ; OnMaintenance=REMOVEALL ; ; If REMOVEALL is selected for maintenance, this flag ; determines if rollback (if applicable) should ; be performed ; ;Rollback=N ;Do you want to backup the files updated in ISR install ;Y indicates yes ;IsrBackup=Y ;Allusers identifies the alluesr vs Cuurent user installation ; A value of NO means current user and YES meas for All Users ALLUSERS=YES ;First time installation MODE ;You can CHOOSE COMPLETE too MODE=COMPLETE ;Crystal reports should be updated ;Y- yes ;N - skipped UPDATE_CRSTAL=Y ;.NET Framework should be updated ;Y- yes ;N - skipped UPDATE_NET_FRAMWRK =Y ;VC Redistribution package should be updated ;Y- yes ;N - skipped UPDATE_VC_REDIST=Y ;Fire wall entries to be done ;Y- yes ;N - skipped UPDATE_FIREWALL=Y ;Should setup overwrite the existing Installation ;Y- yes ;N - no ALWAYS_OVERWRITE=Y ;This option determines whether a disk space check is required ; Possible values are YES and NO. Yes means dont check NO means check IKNOWBEST=YES ;Should Setup Overwrite the HOME Environment variable ;Y - yes ;N - no OVERWRITE_HOME=Y [Features] ; ; Identifies feature list that should be manipulated. ; If the features is not listed here it is NOT ; selected for installation. ; Feature0=ALL OrCAD 166 Products ;Feature1=ALL OrCAD 166 Products\OrCAD_Capture_CIS ;Feature2=ALL OrCAD 166 Products\OrCAD_EE_Designer ;Feature3=ALL OrCAD 166 Products\OrCAD_FPGA_System_Planner ;Feature4=ALL OrCAD 166 Products\OrCAD_PCB_Designer ;Feature5=ALL OrCAD 166 Products\OrCAD_PCB_SI ;Feature6=ALL OrCAD 166 Products\PSpice Feature7=ALL SPB 166 Products ;Feature8=ALL SPB 166 Products\Allegro_AMS_Simulator ;Feature9=ALL SPB 166 Products\Allegro_Design_Authoring ;Feature10=ALL SPB 166 Products\Allegro_Design_Authoring_CIS ;Feature11=ALL SPB 166 Products\Allegro_Design_Authoring_Multi_Style ;Feature12=ALL SPB 166 Products\Allegro_Editor_Router ;Feature13=ALL SPB 166 Products\Allegro_FPGA_System_Planner ;Feature14=ALL SPB 166 Products\Allegro_PCB_Librarian ;Feature15=ALL SPB 166 Products\Allegro_PCB_Router ;Feature16=ALL SPB 166 Products\Allegro_PCB_SI ;Feature17=ALL SPB 166 Products\Allegro_Package_Designer_Cadence_SiP ;Feature18=ALL SPB 166 Products\Allegro_Physical_Viewer Feature19=ALL Documentation [Data] ; ; The data section stores any other properties required by ; the installation. This list varies based on product. ; ; ;The working Directory ; WorkingDir=C:\SPB_Data-Silent ; ; ;For setting FOOTPRINT Path ; ;FootprintPath=D:\Cadence\SPB_16.6-silent\share\pcb\pcb_lib\symbols ; ;SETTING LICENSE FILE ; ;Specifying the port and host information for the license server(s) sets the CDS_LIC_FILE environment variable used by Cadence applications to access the license server(s). ; ;You can get this information from the Cadence license file as follows: ; ; SERVER host1 83065c2 5280 ; ;Where, host name = host1 port = 5280 ; ; ;If you have multiple license servers and you want to access all of them, you can specify the port and host information for each of the license server ;delimited by a semi-colon as follows: port1@host1;port1@host2;port1@host3 For example: 5280@sunny;5280@blue;5280@sky ; ;If your license server has a fault-tolerant server configuration, you can specify the port and host information for each server separated by a ;comma as follows: port@host1,port@host2,port@host3 For example: 5280@sunny,5280@blue,5280@sky ; ; ;You can get the host and port information for a fault-tolerant server from the Cadence license file as follows, ; ; SERVER host1 83065c2 5280 ; ; SERVER host2 83055b2 5280 ; ; SERVER host3 82045c4 5280 ; ;If you uncomment the following line, your CDS_LIC_FILE will be set to 5280@flexserver ;overwrite-lic-path=5280@flexserver ; ;If you uncomment the following line 5280@flexserver1 will be appended to current value of CDS_LIC_FILE variable. ;append-lic-path=5280@flexserver1

 Scripting a Silent Installation

Unfortunately running OrCAD’s setup application doesn’t quite get the job done.  There are a couple of issues I had to work out along the way create a successful, unattended install.

Use the Setup.exe Under the \Disk1 Folder

The main setup.exe opens the interactive menu.  When you script your silent installation, you need to call the setup.exe from within the \Disk1 folder, which is actually responsible for installing the applications.

Copy silentinstall-SPB.ini Somewhere “Simple”

When I passed a complicated path to the silentinstall-SPB.ini file, setup didn’t like it. I’m running the entire installation from a UNC path, and when I passed that network path as !quiet=”\\server\share\EMA OrCAD\Disk1\documents\silentinstall-SPB.ini” the installation would stop without error or warning, or any log that I could locate.  In my silent install script I got around this problem by copying the file to the system Temp folder (referenced by the %TEMP% environment variable), then referencing it from there.

Make Setup.exe Wait Until the Installation Completes Before it Exits

OrCAD’s setup.exe was created with InstallShield.  InstallShield works by using it’s own interface to configure the installation, then spawning a bunch of msiexec processes to actually install the software.  The problem is that setup.exe quits before the MSI installations complete, so unless you account for this behavior your silent install script will exit before the installation is actually completed.  In most situations this isn’t the behavior your going to want.  OrCAD may be one of several applications you want to install in a series.  If your OrCAD install script exist before the installation is completed, your next install will probably fail.  I account for this behavior by running setup.exe from a new command shell (using cmd /c) and specifying the InstallShield switches /w /clone_wait. The /w switch is supposed to make setup.exe wait until installation is completed, but it doesn’t seem to get the job done itself.  The /clone_wait switch tells setup.exe to wait until all of it’s cloned processes (several instances of msiexec) complete.  The combination of the two switches finally made my installation work the way it was supposed to.

Configure Licensing

EMA’s own instructions for unattended installation state that you must manually configure licensing after the installation is completed.  That’s, like, the exact opposite of what unattended means.  Running the License Configuration Utility just creates an environment variable called CDS_LIC_FILE that points to the license server you should already have in place.  We’ll account for this by setting the variable ourselves.

The Final Script

Below is the completed unattended installation script that I’ve created.  It accounts for all of the quirky behaviors discussed above, and returned an error code of “0” so it can be used in a chain of installations through products like Microsoft Deployment Toolkit.

@REM ************************************************************************** @REM ** install.zip ** @REM ** ** @REM ** Silently installs EMA OrCAD 16.6. ** @REM ** ** @REM ** Save this file to the root of your OrCAD installation files (at the ** @REM ** same level as setup.exe, Disk1, Disk2, etc). Modify the variable ** @REM ** below called "ORCAD_LICENSING" to reflect the port and name of your ** @REM ** EMA OrCAD licensing server. Open the file called ** @REM ** silentinstall-SPB.ini under Disk1\documents\ and modify it with the ** @REM ** appropriate settings for your environment as per the EMA's install ** @REM ** guide, or base it off my own file (http://goo.gl/gbng84). The ** @REM ** installer will copy the file to your local temp folder, run the ** @REM ** setup unattended (not silent!), and wait for installation to ** @REM ** Once the install completes the temporary copy of the silent install ** @REM ** file is removed, and licensing is configured via the @ECHO OFF @REM Change this to reflect your licensing environment SET ORCAD_LICENSING=5280@orcad @REM CP holds the directory where you ran install.zip from. SET CP=%~DP0 ECHO Installing OrCAD @REM Copy the silent install file locally so setup doesn't choke on path. COPY "%CP%Disk1\documents\silentinstall-SPB.ini" %TEMP% @REM The following line invokes setup.exe from a new command shell and instructs @REM InstallShield to wait (/w) for all cloned processes to complete (/clone_wait) @REM It also tells InstallShield to use the silent install file. @REM See http://helpnet.installshield.com/installshield18helplib/ihelpsetup_execmdline.htm cmd /c "%CP%\Disk1\Setup.exe" /w /clone_wait !quiet=%TEMP%/silentinstall-SPB.ini @REM Set the machine-level environment variable for the license server. SETX CDS_LIC_FILE "%ORCAD_LICENSING%" /M @REM Clean up, clean up, everybody, everywhere... DEL %TEMP%\silentinstall-SPB.ini EXIT 0

Updating Windows Deployment Services Images From WSUS

Sometimes you’ll run into a situation where two pieces of Microsoft software don’t play well together, or in a way that would be convenient from the user’s perspective.  Such is the case with Windows Deployment Services and Windows Server Update Services.  One piece of technology stores and deploys operating system images.  The other piece of technology updates Windows operating systems.  It seems like there ought to be some glue that brings the two together because, otherwise, you need to deploy the OS image, update it manually, then recreate it.

The bad news is, no such glue exists out of the box. The good news is, it’s not difficult to create it with PowerShell (provided WDS is running on Server 2012).  Below is my project, hosted on GitHub. It’s a single, short PowerShell script which, when run on a Server 2012 WDS server, will cycle through all of your Install Images and update them using your WSUS Repository from the WsusContent share. All you need to do is run the script from the WDS server. It will ask for a scratch location where it can extract the WIM images from Windows Deployment Services, and the location of your WSUS Repository.  I recommend running it over a weekend, because, depending on the number of images stored in WDS and the number of updates in WSUS, it can take a day or longer.

The script requires interactive input, but with just a little modification you can easily hard-code the information it needs about your environment, and run it as a scheduled task on your WDS Server.

Retrieving Recursive Group Memberships with PowerShell

This is a problem that has plagued me for a long time. I’ve seen solutions implemented in VBScript, and I’ve seen PowerShell solutions that relied on third-party tools to get the job done.  Well I’ve finally implemented a solution to finding all of an account’s group memberships with PowerShell.

The following script provides a function I call Get-ADPrincipalGroupMembershipRecursive, which I named after the built-in function ADPrincipalGroupMembership.  The function is called by passing the distinguished name of the account.  It will determine all of the group’s memberships using the memberOf attribute, then recursively check those groups, their subgroups, etc. until a comprehensive list of the account’s memberships has been built.

<# .SYNOPSIS Gets all the Active Directory groups that have a specified user, computer, group, or service account. .DESCRIPTION Gets all the Active Directory groups that have a specified user, computer, group, or service account. Unlike the built-in Get-ADPrincipalGroupMembership cmtlet, the function I've provided below will perform a recursive search that will return all of the groups that the account is a member of through membership inheritance. This function required the Active Directory module and thus must be run on a domain controller or workstation with Remote Server Administration Tools. .PARAMETER dsn The distinguished name (dsn) of the user, computer, group, or service account. .PARAMETER groups An array of ADObject instances for each group in which the user, computer, group, or service account is a member. This parameter can be ignored and in fact should never be specified by the caller. The groups parameter is used internally to track groups that have already been added to the list during recursive function calls. .NOTES Author : Brian Reich <breich@reich-consulting.net .LINK http://www.reich-consulting.net #> function Get-ADPrincipalGroupMembershipRecursive( ) { Param( [string] $dsn, [array]$groups = @() ) # Get an ADObject for the account and retrieve memberOf attribute. $obj = Get-ADObject $dsn -Properties memberOf # Iterate through each of the groups in the memberOf attribute. foreach( $groupDsn in $obj.memberOf ) { # Get an ADObject for the current group. $tmpGrp = Get-ADObject $groupDsn -Properties memberOf # Check if the group is already in $groups. if( ($groups | where { $_.DistinguishedName -eq $groupDsn }).Count -eq 0 ) { $groups += $tmpGrp # Go a little deeper by searching this group for more groups. $groups = Get-ADPrincipalGroupMembershipRecursive $groupDsn $groups } } return $groups } # Simple Example of how to use the function $username = Read-Host -Prompt "Enter a username" $groups = Get-ADPrincipalGroupMembershipRecursive (Get-ADUser $username).DistinguishedName $groups | Sort-Object -Property name | Format-Table

A Better Way to View Shell Command Output

I’m a command prompt guy.  If there is a way to do something with the keyboard, then by golly, that’s how I’m going to do it.  But sometimes you’ll want to use a command whose options and formatting aren’t obvious, and when you type the command to view the command’s usage (usually by typing command /?, command -h, or command –help), black and white letters go flying by like a newspaper in a Blendtec ad.

Linux users can read command documentation via the man command which provides detailed usage instructions in a way that’s easy to read and navigate.  We Windows admins don’t have such a luxury, but you can mimic it.

Piping Output to More

One way to view the output of a command when it’s longer than a single screen is by piping the output to the more command like this:

command | more

For example if I want to view the help page for the icacls command:

icacls.exe /? | more

This method works, but what if you want to save the command’s output for use later?

Redirecting Output to a File

Another option is to send the command’s output to a file. This can be done using the redirection operator “>”

command > output.txt

Using the same command from above, we’ll save the command usage page for icacls.exe to a file called icacls_help.txt:

icacls /? > icacles_help.txt

If we want to be really fancy, we can combine the ideas of redirection and piping and view the help page immediately in notepad:

icacls /? > icacls_help.txt | notepad icacls_help.txt

KiXtart Fails to Delete Network Drives

I recently ran into an issue with a logon script written in KiXtart failing to disconnect network drives. I can only guess from the dozens of forum posts I found online that this is a common problem that scripters run into.  Luckily there is a simple solution.

The Problem

When you issue a Use “<drive>” /Delete comment from KiXtart or a NET USE <drive> /DELETE batch file command, the drive letter in question isn’t actually deleted from My Computer. Instead, the drive will be listed as a Disconnected Network Drive and when opened it will automatically reconnect.  This happens when the drive was initially connected using by mapping the drive manually and selecting “Reconnect at logon” or by executing one of the following commands:

Use [drive] /Persistent
NET USE [drive] /PERSISTENT:YES

The Solution

Deleting persistent drives may require that you use the /Persistent switch when you execute the delete command. You can delete a single network drive using one of the following commands:

Use [drive] /Delete /Persistent
NET USE [drive] /DELETE /PERSISTENT:YES

Or alternatively, you can delete all network drives at the beginning of your script using one of the following:

Use * /Delete /Persistent
NET USE * /DELETE /PERSISTENT:YES