Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

Adding functions in Otter



  • Hello, I want to start creating small functions of reusable code so I don't have to keep adding them to my plans. I use PSEnsure almost exclusively to maintain consistency across my roles. One of the things I would like to set up is a machine domain check, since we take care of machines in multiples domains and forests. I would basically like to call this function during my PSEnsure and have it output a variable called $DomainMembership for me to be able to utilize as need. For example:

        $GetDomain = (Get-CimInstance Win32_ComputerSystem).Domain
        
        if ($GetDomain -eq 'abc.local'){
            $DomainMembership  = 'abc.local'
        }
        elseif ($GetDomain -eq 'def.local'){
            $DomainMembership = 'def.local'
        }
    

    I am not sure if this should be a module or script, as it is not clear to me the difference between the two, or documentation on how to get it working.

    Product: Otter
    Version: 2.1.3



  • So just to update, I have been testing some powershell scripts out. I test them by scheduling a job, and have it write the output and I can see it in the logging. they seem to work when using an Orchestration / Execution plan or job. But I am not able to get it working with a configuration / PSEnsure job. I am not sure if I am missing something or it has a limitation? I would hope one would be able to use reusable code in a Configuration /PSEnsure plan. Ideas?



  • <crickets>

    Anyone? Am I not doing this right, or does it not work that way?


  • inedo-engineer

    I'm not 100% sure I understand, but when I see "small functions of reusable code", the first thing that comes to mind are Modules. These are bits of OtterScript that you can from OtterScript using the call statement.

    module Get-DomainMembership<$Param1, $OptionalParam2 = default, out $OutParam>
    {
       set $outParam = hello;
    }
    
    call Get-DomainMembership
    (
      Param1: some value,
      Param2 => $SomeVariable
    );
    

    And then there are also Script Assets, which are PowerShell scripts that you call using the pscall operation from within OtterScript.

    Is this helpful?



  • Hello, I have a simple test function as a script asset:
    function TestFunction (){
    Write-Warning 'TestFunction was successful'
    }

    And I have the psensure. I have tried to make this is simple as possible for clarity:

    ##AH:UseTextMode

    PSEnsure
    (
    Key: Test,
    Value: True,
    Collect: >>
    $false
    >>,
    Configure: >>
    pscall test;
    >>
    );

    This does not work. I have not been able to call any assets in a PSensure. I have made this example as simple as possible. As far as I can tell, assets don't work in PSEnsure configurations. How can I store PowerShell functions

    https://docs.microsoft.com/en-s/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-6

    equivalent in Otter so I can call them in a PSEnsure? Am I doing something wrong?



  • To use script assets, use the CollectScript and/or ConfigureScript properties, for example:

    # ensures the BuildMaster Agent service exists on the remote server, using a
    # PowerShell script asset to perform the configuration
    PSEnsure(
        Key: BuildMasterAgentInstalled,
        # returns the count of INEDOBMAGT services installed
        Collect: @(Get-Service | Where-Object {$_.Name -eq "INEDOBMAGT"}).Count,
        # expected value is 1
        Value: 1,
        # use script stored in InstallBmAgent asset
        ConfigureScript: InstallBmAgent,
        ConfigureScriptParams: %(
            AgentType: TCP,
            Port: 1000),
        Debug: true,
        Verbose: true
    );
    


  • So looking at your example, I tried the "ConfigureScript: test". I have a powershell script asset called test with this in it:

    function TestFunction (){
    Write-Warning 'TestFunction was successful'
    }
    TestFunction

    And this simple psensure:

    ##AH:UseTextMode

    PSEnsure
    (
    Key: Test,
    Value: True,
    Collect: >>
    $false

    >>,
    Configure: >>
    
    
    >>,
    ConfigureScript: Test
    

    );

    Won't run the asset as it says it can't find it, even though I see it fine under assets and when I look where my raft exists in bitbucket, it is there as well.

    Even if it did run it, I can only run one script as it does not appear to be an array? That does not meet my requirement of being able to run multiple scripts/functions/assets/ whatever they are called.

    So am I probably not explaining this very well. In my PSensure, I have pieces of code that I have to include many different times that I want to keep as a script asset/function, whatever. My intent is instead of duplicating this code 1000 times, I can change it in one place and just call the script from one location while doing my psensure. Make sense?



  • Update: so for whatever reason, after I left for lunch and came back, this particular gremlin decided to leave. I can call a single script, however, this does not meet my requirement of using separate multiple scripts for different functions, and I cannot run this with a configure block as well; it's one or the other.

    So back to square one. Can this functionality be added in to run a script asset in a psensure?


  • inedo-engineer

    Resolved via ticket:

    If you want to call PowerShell functions, you need to wrap them in a PowerShell Module, then import that module in your PowerShell script asset. That's the only way I can think of that this will consistently work. I've implemented something like this before:

    set $ModuleName = Hello-World;
    Get-Asset $ModuleName.ps1
    (
        Type: Script    
    );
    set $ModulePath = $PathCombine($WorkingDirectory, $ModuleName.ps1);
    
    PSEnsure
    (
        Key: Test,
        Value: True,
        Collect: "$false",
        Configure: >>
        Import-Module -Name "$ModulePath" -Verbose
        
        Hello-World "Testing 123"
        >>
    );
    

    which just writes a Hello-World script out and imports it as a PS module. You could probably figure out a better way than this e.g. creating a "PsModule" role that ensures the module is installed on any servers you're working with, then you'd only need the Import-Module command in your scripts to gain access to all your shared functions.



  • So, now that I have time to work on this again, to recap, I can create my PSEnsure as I usually do. I can create all of my shared functions as a PowerShell module, and store them as a PowerShell script asset be able to call them as needed in my various roles as needed, and more than just one? I will also not need to copy and manage each PowerShell Module/ Function as a traditional PowerShell module and copy them over to the file system of each server and manage that? If I can create a separate module for each function, store them as a PowerShell Script asset, and call them as needed, then I believe this will work for my PSEnsure roles, and I will be able to standardize the function by calling them from a separate location instead of including them each time with each PSEnsure, which is very cumbersome and costing me a great deal of time.



  • Hello Jonathan,
    We use Proget to deploy our PSModules.
    The deployement is driven with an Otter PSEnsure fonction- check if module exist with a specific version, if not deploy -
    Each new server is linked to an Otter role and after remediation, the module is installed.
    To update the module, we change version number in a otter variable. Otter remediation make the job

    We call PS function from local script or from Otter plan directly or from module.
    Don't know if this your need but it is working fine for us.


Log in to reply
 

Inedo Website HomeSupport HomeCode of ConductForums GuideDocumentation