How to add Environment in script Step

Hello there,

I’m working on a case in which:

  1. have to add a “script step” to all projects that already have a step “Deploy to IIS”. Script step should be after “Deploy to IIS”
    This part I’m not sure how to perform…
  2. “Script step” should be added to “Webserver” role and only to be applied in “Stage” environments
    Wrote something but can’t find a way to create a step for the particular environment. I tied with Properties.Add, also tied with Id and Name…

Octopus vesrion: 2018.7.11

   Add-Type -Path 'Octopus.Client.dll' 
   $apikey = 'API-******' 
   $octopusURI = 'http://*****.com' 

   $projectId = "Projects-321" # Get this from /api/projects
   $stepName = "Run Website Inspection" # The name of the step
   $role = "Webserver" # The machine role to run this step on
   $environment = "STAGE"
   $scriptBody = "Write-Host 'Script body'" # The script to run

   $endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURI,$apikey 
   $repository = New-Object Octopus.Client.OctopusRepository $endpoint

   $project = $repository.Projects.Get($projectId)
   $process = $repository.DeploymentProcesses.Get($project.DeploymentProcessId)

   $environmentToAdd = $repository.Environments.FindByName($environment) 

   $step = New-Object Octopus.Client.Model.DeploymentStepResource
   $step.Name = $stepName
   $step.Condition = [Octopus.Client.Model.DeploymentStepCondition]::Success
   $step.Properties.Add("Octopus.Action.TargetRoles", $role)
   $step.Environment.Name = $environment1

   $step.Octopus.Environment.Id($environmentToAdd.Id)  # -> here I have trouble to understand how to stick environment 

   $scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
   $scriptAction.ActionType = "Octopus.Script"
   $scriptAction.Name = $stepName
   $scriptAction.Properties.Add("Octopus.Action.Script.ScriptBody", $scriptBody)

   $step.Actions.Add($scriptAction)
   $process.Steps.Add($step)
   $repository.DeploymentProcesses.Modify($process)

Any help is welcome… Thank you in advance.
B

Hi,

Thanks for getting in touch! I have just done some testing on my VM and managed to get this working.

You were quite close, the only things I really changed were specifying Id when first creating the $environmentToAdd variable and trying to set this value under $scriptAction instead of $step.

See the comments in my script:

Add-Type -Path 'Octopus.Client.dll' 

$apikey = 'API-***********' # Get this from your profile
$octopusURI = 'http://*******' # Your server address

$projectId = "Projects-321" # Get this from /api/projects
$stepName = "Run Website Inspection" # The name of the step
$role = "Webserver" # The machine role to run this step on
$environment = "Dev" #  The name of the Environment to scope step to
$scriptBody = "Write-Host 'Hello world'" # The script to run


$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURI,$apikey 
$repository = New-Object Octopus.Client.OctopusRepository $endpoint

$project = $repository.Projects.Get($projectId)
$process = $repository.DeploymentProcesses.Get($project.DeploymentProcessId)

# Same line you have but I added the .Id in the initial variable.
$environmentToAdd = $repository.Environments.FindByName($environment).Id

# I removed the "$step.Environment.Name = $environment1" (We set this below with $scriptAction
$step = New-Object Octopus.Client.Model.DeploymentStepResource
$step.Name = $stepName
$step.Condition = [Octopus.Client.Model.DeploymentStepCondition]::Success
$step.Properties.Add("Octopus.Action.TargetRoles", $role)

# Added "$scriptAction.Environments.ReplaceAll($environmentToAdd)" at the end here.
$scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
$scriptAction.ActionType = "Octopus.Script"
$scriptAction.Name = $stepName
$scriptAction.Properties.Add("Octopus.Action.Script.ScriptBody", $scriptBody)
$scriptAction.Environments.ReplaceAll($environmentToAdd)

$step.Actions.Add($scriptAction)

$process.Steps.Add($step)

$repository.DeploymentProcesses.Modify($process)

Let me know if this helps, or if you are still running into issues.

Best regards,
Daniel

Hello @Daniel_Fischer,

Thank you for your help, this is useful. In meantime, I tried to move forward and include adding a new step into all projects (in my first post point 1) but I’m getting the following:

Cannot convert argument "newItems", with value: "Environments-82", for "ReplaceAll" to type 
"System.Collections.Generic.IEnumerable`1[System.String]": "Cannot convert the "Environments-82" 
value of type "System.String" to type 
"System.Collections.Generic.IEnumerable`1[System.String]"."
At line:34 char:1
+ $scriptAction.Environments.ReplaceAll($environmentToAdd)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

 Cannot find an overload for "Modify" and the argument count: "1".
 At line:41 char:5
+     $repository.DeploymentProcesses.Modify($a)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest

Code (first part is the same):

$allProjects = $repository.Projects.GetAll()

$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURI,$apikey 
$repository = New-Object Octopus.Client.OctopusRepository $endpoint

$project = $repository.Projects.Get($projectId)
$process = $repository.DeploymentProcesses.Get($project.DeploymentProcessId)
$environmentToAdd = $repository.Environments.FindByName($environment).Id

$step = New-Object Octopus.Client.Model.DeploymentStepResource
$step.Name = $stepName
$step.Condition = [Octopus.Client.Model.DeploymentStepCondition]::Success
$step.Properties.Add("Octopus.Action.TargetRoles", $role)

$scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
$scriptAction.ActionType = "Octopus.Script"
$scriptAction.Name = $stepName
$scriptAction.Properties.Add("Octopus.Action.Script.ScriptBody", $scriptBody)
$scriptAction.Environments.ReplaceAll($environmentToAdd)

$process = $repository.DeploymentProcesses.Get($allProjects.DeploymentProcessId)

foreach ($a in $allProjects) {
    $step.Actions.Add($scriptAction)
    $process.Steps.Add($step)
    $repository.DeploymentProcesses.Modify($a)
}

EDIT

I think there should be Add instead ReplaceAll.
$scriptAction.Environments.Add($environmentToAdd)
After change, I’m not getting Cannot convert argument "newItems", with value: "Environments-82", ...

Hi @bdjurkic,

Thanks for the update here! Interesting, during my testing I was not able to get the Environments.Add working, my script only started working after changing this to Environments.ReplaceAll However the Add seems to work fine for me this morning. ¯\_(ツ)_/¯. Perhaps it was restarting my machine, or having fresh coffee. (Or both. :coffee: )

Anyway, it sounds like this is working for you now. If you have any other questions or issues here, please don’t hesitate to get in touch. :slight_smile:

Best regards,
Daniel

Hi @Daniel_Fischer,

I was trying to add step to all projects but unsuccessfully, can you take a look where I’m getting wrong? You didn’t catch in my previous post :slight_smile:

$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURI,$apikey 
$repository = New-Object Octopus.Client.OctopusRepository $endpoint

$environmentToAdd = $repository.Environments.FindByName($environment).Id

$step = New-Object Octopus.Client.Model.DeploymentStepResource
$step.Condition = [Octopus.Client.Model.DeploymentStepCondition]::Success
$step.Properties.Add("Octopus.Action.TargetRoles", $role)

$scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
$scriptAction.ActionType = "Octopus.Script"
$scriptAction.Properties.Add("Octopus.Action.Script.ScriptBody", $scriptBody)
$scriptAction.Environments.Add($environmentToAdd)

$allProjects = $repository.Projects.GetAll()
foreach ($a in $allProjects) {

    $step.Actions.Add($scriptAction)
    $repository.Deployments.Modify($a)
    }

My second question, how to add a step to the project after a specific step which already exists? For instance, I would like to add this new step after step called " Send Notification" or step called “Deploy website”, etc

Thank you in advance,
B

Hi @bdjurkic,

Sorry for the delay here. I ran this past the team as I was having trouble getting the iteration working for every project. However, I was able to get some great help here and have a resolution.

Below I have pasted the entire script with the working iteration to add this step to each project. I’ll have to make sure I add this one to our repository for anyone else looking for a solution in the future. (I’ll write some better commenting on the one I post on GitHub :wink:)

Add-Type -Path 'Octopus.Client.dll' 

$apikey = 'API-***********' # Get this from your profile
$octopusURI = 'http://*******' # Your server address

$stepName = "Run Website Inspection" # The name of the step
$role = "Webserver" # The machine role to run this step on
$environment = "Dev" #  The name of the Environment to scope step to
$scriptBody = "Write-Host 'Hello world'" # The script to run

$endpoint = New-Object Octopus.Client.OctopusServerEndpoint $octopusURI,$apikey 
$repository = New-Object Octopus.Client.OctopusRepository $endpoint

$allProjects = $repository.Projects.GetAll()

$environmentToAdd = $repository.Environments.FindByName($environment).Id

$step = New-Object Octopus.Client.Model.DeploymentStepResource
$step.Name = $stepName
$step.Condition = [Octopus.Client.Model.DeploymentStepCondition]::Success
$step.Properties.Add("Octopus.Action.TargetRoles", $role)

$scriptAction = New-Object Octopus.Client.Model.DeploymentActionResource
$scriptAction.ActionType = "Octopus.Script"
$scriptAction.Name = $stepName
$scriptAction.Properties.Add("Octopus.Action.Script.ScriptBody", $scriptBody)
$scriptAction.Environments.Add($environmentToAdd)

$step.Actions.Add($scriptAction)

foreach ($x in $allProjects) {
    $process = $repository.DeploymentProcesses.Get($x.DeploymentProcessId)
    $process.Steps.Add($step)
    $repository.DeploymentProcesses.Modify($process)
}

Currently it will give you an error for each projects which already has this step added, you can ignore it or put some further logic to handle the errors.

Let me know how this goes, if you are still having issues I’m more than happy to help. :slight_smile:

Best regards,
Daniel

Hello @Daniel_Fischer,

This looks great :slight_smile:
I have one concern how to add a script with multiple lines? Let’s use similar code from above:

$scriptBody =  "$text = 'Hello World'
               Write-Host $text"

I’m getting:

= 'Hello World'
Write-Host 

I tried a various combination of quotes, but couldn’t make this work.

Kind Regards,
B

Hi @bdjurkic,

Sorry again for the delay in responding here. Hopefully the below answer is detailed enough for anyone else looking for information on this in the future.

I have chat to multiple developers about this and it looks like there are some options available.

The first thing I will note is that I was advised multiple times against adding steps via the API. Whilst it is completely possible as you have seen, it carries with it some issues and pains which make it not ideal to work with.

Script Body via API
One of these issues is adding the script body. When I add multi line script body via the UI I was able to check the development console in my browser and look at the post which is made to our API during this action. I can see that we add the script body like this:

Octopus.Action.Script.ScriptBody:"$test = 'hello world'/nWrite-Host $test/nWrite-Host 'The end'"

When Octopus adds multiple lines via the UI we do so with /n. However this can easily get very messy.

Script in a package via API
There was an alternative suggestion here which I believe could work. It involves creating your script as a regular .ps1 file, then packaging it. Octopus provides the option to specify a script source, the default option is Source code however, if you change this to Script file in a package then you are able to do the following:

  1. Directly upload the .ps1 as a package in the script step
  2. Let Octopus extract it just like a regular package step
  3. Perform any variable replacement
  4. Execute script on the target.

The general consistence is that the latter is the recommended approach for achieving this via the API

Now there is a final option!
Step Template
I do not know if you have considered this option. However, we have a step template feature designed for creating a template step which can be added to each project in Octopus, and managed remotely.

I believe our documentation would be the best to explain what you can do with this feature. From what I have seen above, it does not look like using a step template would restrict you.

I personally believe the Step Template options is the best (If it works in your situation). I am happy to answer any questions you have on this or help you resolve any issues.

Sorry again for my late responses, let me know what you think.

Best regards,
Daniel

Hello @Daniel_Fischer,

I adjust script with add Step Template as it’s the easiest way. I still didn’t determinate how to add this step after some other step (as I described in my previous posts).

I’ll share complete script when I sorted it out.

Kind Regards,
B

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.