Using Windows PowerShell to connect to a SAS Workspace server

23

This post is another in my series about creating apps with SAS Integration Technologies, a topic that I'm preparing for SAS Global Forum 2013.

In this article, I'll describe how to use Windows PowerShell to connect to a remote SAS Workspace, run a SAS program, and retrieve the results. This can be a useful method to implement a batch-style SAS job that you initiate from a client machine -- a common requirement in many SAS shops. The complete code example is on GitHub as SasWorkspaceExample.ps1.

Like my previous example with the SAS Metadata Server, this example begins with using SAS Object Manager. Use SAS Object Manager to create a ServerDef object that you use to connect to a SAS Workspace. This time we'll use the Workspace server port (by default, 8591) and the ClassIdentifier value for a SAS Workspace object. Remember, I used PROC IOMOPERATE to remind me which value that is, since I've never been any good at memorizing a 32-character hexadecimal value.

$objFactory = New-Object -ComObject SASObjectManager.ObjectFactoryMulti2
$objServerDef = New-Object -ComObject SASObjectManager.ServerDef 
$objServerDef.MachineDNSName = "server.mycompany.com" # SAS Workspace node
$objServerDef.Port           = 8591  # workspace server port
$objServerDef.Protocol       = 2     # 2 = IOM protocol
# Class Identifier for SAS Workspace
$objServerDef.ClassIdentifier = "440196d4-90f0-11d0-9f41-00a024bb830c"

Connecting to a SAS Workspace

With an instance of an ObjectFactory class and the ServerDef class, you can now establish a connection to the server:

# create and connect to the SAS session 
$objSAS = $objFactory.CreateObjectByServer(
                "SASApp",      # server name
                $true, 
                $objServerDef, # used server definition for Workspace
                "sasdemo",     # user ID
                "Password1"    # password
                )

When this statement completes successfully the $objSAS variable contains a connection to the SAS Workspace. With this connection, you can run SAS programs, read SAS data, and transfer files between the SAS Workspace and your application. (I'll cover how to transfer files in a future post.)

Running a SAS program with LanguageService

The SAS Workspace provides an API called LanguageService, which allows you to run SAS programs and retrieve the text-based results, including the SAS log and the SAS listing output. Here is a simple example using the $objSAS variable from the previous example:

$program = "ods listing; proc means data=sashelp.cars; run;"

# run the program
$objSAS.LanguageService.Submit($program);

These statements will run the program, but they won't retrieve your results. To see the contents of the SAS log, use the FlushLog method as in this example:

# flush the log - could redirect to external file
Write-Output "LOG:"
$log = ""
do
{
  $log = $objSAS.LanguageService.FlushLog(1000)
  Write-Output $log
} while ($log.Length -gt 0)

The FlushLog method requires that you specify the number of bytes that you want to retrieve in a single call. In the above example, we used 1000. But what if your SAS log is greater than 1000 characters? That's the reason for the do..while loop. In this example, the PowerShell statements will continue to retrieve and emit the SAS log content until the FlushLog method no longer returns any content (that is, while the length of the $log variable is greater than 0).

You can retrieve the SAS listing output in a similar way by using the FlushList method:

# flush the output - could redirect to external file
Write-Output "Output:"
$list = ""
do
{
 $list = $objSAS.LanguageService.FlushList(1000)
 Write-Output $list
} while ($list.Length -gt 0)

This picture shows the Windows PowerShell ISE (scripting environment) with the script output:

Closing the SAS Session

It's important to manage the lifetime of the SAS session in a responsible way. If you create a SAS Workspace connection and use it within your application, you should call the Close method on the SAS.Workspace object when your work is complete. This is especially true if your application is designed to stay running for a long time, even when the SAS session is not in use.

To close the SAS Workspace session, use the Close() method:

# end the SAS session
$objSAS.Close()

Related links

Share

About Author

Chris Hemedinger

Senior Manager, SAS Online Communities

+Chris Hemedinger is the manager of SAS Online Communities. Since 1993, Chris has worked for SAS as an author, a software developer, an R&D manager and a consultant. Inexplicably, Chris is still coasting on the limited fame he earned as an author of SAS For Dummies.  He also hosts the SAS Tech Talk webcasts each year from SAS Global Forum, connecting viewers with smart people from SAS R&D and the impressive work that they do.

23 Comments

  1. Pingback: Using PowerShell to download a file from a SAS Workspace

  2. Pingback: Build your own SAS client app with Microsoft .NET - The SAS Dummy

  3. If I don't have access to use Proc iomoperate, how can I find out the class identifier of the workspace server?

    Also just double checking, setting protocol = 2, tells it to use IOM Protocol?

    • Chris Hemedinger
      Chris Hemedinger on

      Yes, protocol=2 is IOM. The Workspace Server class ID is always the same: 440196d4-90f0-11d0-9f41-00a024bb830c. PROC IOMOPERATE simply provides a convenient reference.

  4. Chris, one of the comments in the code published on github is "# could be read from external file". Can you give an example of how you could run a .sas program in powershell? Thanks.

    • Chris Hemedinger
      Chris Hemedinger on

      Erik,

      Yes. Simply replace that line with a command to read in the program file that you want. The PowerShell Get-Content cmdlet will read an external file. For example:

      $program =  Get-Content "U:\SGF2013\PowerShell\sample.sas"  
      

      If you want to pass the file name in as a command argument, you could add processing like:

      # check for an input file
      if ($args.Count -eq 1)
      {
        $fileToProcess = $args[0] 
      }
      else
      {
        Write-Host "EXAMPLE Usage: SASWorkspaceExample.ps1 path-and-name-of-SAS-program"
        Exit -1
      }
      
      # check that the input file exists
      if (-not (Get-Item $fileToProcess).Exists)
      {
        Write-Host "$fileToProcess does not exist."
        Exit -1
      }
      
      $program =  Get-Content $fileToProcess  
      

  5. Would it be possible to connect to the work space server using the metadata server profile(configuration61.xml) like EG does it, if so, how ?

    Thanks!

    • Chris Hemedinger
      Chris Hemedinger on

      Laurent,

      EG uses this file to store information about your SAS Metadata connection. Once you connect with EG, a metadata query is issued to find the available SAS Workspace Servers.

      You can do the same work via PowerShell, but you have to parse/query items yourself. You can use standard XML parsing techniques to find all of the required values for host, port, and user ID. But you'll need your own method of prompting/retrieving the password. However, this connects you to the SAS Metadata Server, and not the SAS Workspace. Then you would need to query SAS metadata for the available workspace servers.

      If you have a simple single-machine server environment, perhaps the SAS Metadata Server and SAS Workspace Server are the same host machine, and only the port numbers differ. If that's true, you might be able to use the configuration file to find some values, and then hardcode the rest based on assumptions in your configuration.

      You could also use EG automation to discover the profile information, as in this sample script. (See the full article here on sasCommunity.org.)

  6. Chris how can authenticate the connection using a metadata identity (login authenticaed via ldap) instead of machine authentication?

    "sasdemo", # user ID
    "Password1" # password
    thank you

    • Chris Hemedinger
      Chris Hemedinger on

      If using integrated windows auth (kerberos), you can leave the userid/password blank, and add a value to the objServerDef:
      $obServerDef.BridgeSecurityPackage = "Negotiate"

      For SAS token authentication, once connected to metadata, you can obtain credentials for one-time use to connect to other resources (such as a SAS workspace). You might be interested in the Cmdlets4SAS project, which encapsulates this feature within its library.

  7. Michael Goulding on

    When I run this code by submitting a SAS program file, using the Get-Content cmdlet, the log does not show me the expected sequence of source statements for each step followed by the log messages related to that step. Instead, all the source statements appear at the top of the log – with no line feeds – and then the log messages for each step appear one after another, with no source code. And the line breaks appear at random positions of the text, not at statement boundaries. I tried doing options source source2; but this doesn't affect the log content.

    %let domain = HO; data ho_hos (keep=subjid studyid usubjid
    1 ! hoterm hostdtc hoendtc coval EXICUYN visit) ho_icu (keep=subjid studyid usubjid h
    oterm hostdtc hoendtc visit coval);
    1 ! length subjid $20 studyid $11 usubjid $19 ; set raw_cdms.extrt(DROP=studyid); subjid = subject; studyid=project;

    NOTE: DATA statement used (Total process time):
    real time 0.01 seconds
    cpu time 0.00 seconds

    NOTE: DATA statement used (Total process time):
    real time 0.02 seconds
    cpu time 0.03 seconds

    Any idea what is causing this result and why the log text isn't ordered in the normal way ?

    • Chris Hemedinger
      Chris Hemedinger on

      I don't know what would cause the log content to be returned in the wrong order. You didn't change the Async property on the LanguageService by any chance, did you? Async is False by default, but if it were set to True then the Log fetching code could be processed before the program has completed running.

      • Michael Goulding on

        Hi Chris, thanks for replying. No, I didn't change Async - was not even aware of that property. Just as an experiment, I went back to the original example code, and added 2 more simple steps to the proc means: proc sort and proc print. I observe the same effect as before. Below I've pasted what is written to the console. Note the source statements for all 3 steps appear as one continuous block of text; then that is followed by the messages for each step, absent the source statements.

        Is it possible that different defaults might somehow be set on the server side? I'd be really curious what result you are seeing with your log; I can't tell just from the screen shot above, as only the first line of the log is visible.

        NOTE: The autoexec file, E:\SAS\Config\Lev1\SASApp\WorkspaceServer\autoexec.sas, was executed at server initial
        ization.
        1 options formchar='|----|+|---+=|-/\*';ods listing; proc means data=sashelp.cars mean mode min max;
        run;proc sort
        1 ! data=sashelp.cars out=work.cars; by origin type; run;proc print data=work.cars; where make='Volv
        o'; var make model msrp
        1 ! invoice; run;

        NOTE: There were 428 observations read from the data set SASHELP.CARS.
        NOTE: The PROCEDURE MEANS printed page 1.
        NOTE: PROCEDURE MEANS used (Total process time):
        real time 0.01 seconds
        cpu time 0.01 seconds

        NOTE: There were 428 observations read from the data set SASHELP.CARS.
        NOTE: The data set WORK.CARS has 428 observations and 15 variables.
        NOTE: PROCEDURE SORT used (Total process time):
        real time 0.00 seconds
        cpu time 0.00 seconds

        NOTE: There were 12 observations read from the data set WORK.CARS.
        WHERE make='Volvo';
        NOTE: The PROCEDURE PRINT printed page 2.
        NOTE: PROCEDURE PRINT used (Total process time):
        real time 0.00 seconds
        cpu time 0.00 seconds

        • Chris Hemedinger
          Chris Hemedinger on

          Not sure what's happening here -- I'm not seeing these results. You could also try the SASHarness application (written about here) and see if you get the same thing. The logic is very similar, just using .NET instead of PowerShell.

  8. Nelly Khalioullina on

    Hi Chris,
    I have install Cmdlets4Sas, but I can not import module.

    Import-Module : Could not load file or assembly 'file:///C:\Program Files\WindowsPowerShell\Modules\Cmdlets4Sas\Interop.SAS.dll' or one of its dependencies. Operation is not supported. (Excep
    tion from HRESULT: 0x80131515)
    At line:1 char:1
    + Import-Module Cmdlets4Sas
    + ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [Import-Module], FileLoadException
    + FullyQualifiedErrorId : FormatXmlUpdateException,Microsoft.PowerShell.Commands.ImportModuleCommand

    Any idea what is causing this error?
    Thank you.

    • Chris Hemedinger
      Chris Hemedinger on

      You'll find this file (and other required files) in the SAS Integration Technologies client folder. On my machine these are in:
      C:\Program Files\SASHome\Integration Technologies
      OR
      C:\Program Files\SASHome\x86\Integration Technologies
      depending on whether you're using 64-bit or 32-bit PowerShell. You should add this to your system search PATH environment variable.

  9. Pingback: Build your own SAS client app with Microsoft .NET - The SAS Dummy

  10. Pingback: Using PowerShell to download a file from a SAS Workspace

  11. Hey Chris,

    The information that you have provided above for executing a SAS program from PowerShell really helps. The same way, can we use the Get-Content cmdlet to execute the SAS EG project and read the output? Something like below -

    $program = Get-Content "U:\SGF2013\PowerShell\sample.egp"

    Please let me know.

    Thanks!

Leave A Reply

Back to Top