The remote server returned an error: (405) Method Not Allowed error when trying to Put project level variable to library set from REST API

Hi,

High-level description:
Copy variables from project level to library set (copy only unmatched)

I can GET project-level variables for the project ‘KT’ using REST API calls below. And also able to GET variable set ID using below REST API call.
And created an object to capture project-level variable output to $results(), where I’m getting the result correctly. But while executing the “PUT” command to copy variables from project level to library set I’m getting 405 methods not allowed error.

Get project

$libSet = (Invoke-RestMethod -Method Get -Uri ($AppOctopusSrvPRODURI + “/api/projects/all”) -ContentType application/json -Headers $oBaseURIHdr) | Where-Object {$_.Name -eq ‘KT’}

Get project variables

$libSetDet = Invoke-RestMethod -Method Get -Uri ($AppOctopusSrvPRODURI + $project.links.Variables) -ContentType application/json -Headers $oBaseURIHdr

- get library set details

$librarySet = (Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/?partialName=$librarySetName”) -ContentType application/json -Headers $oBaseURIHdr -Method Get).items
$librarySetDet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + $librarySet.links.Variables) -ContentType application/json -Headers $oBaseURIHdr -Method Get

Here is the full script:
The script logic is working fine except “PUT” REST API call failure. Not sure what is missing. Please do the needful.

- functions

function DO-BuildList () {
param (
[Parameter(Mandatory=$true)][System.Object] $Userlist
)
[system.Object]$returnList = @()
foreach ($item in $userlist) {
$itemName = ($item.Split(’,’)).Trim()
$returnList = $returnList + $itemName
}
return $returnList
}

- input vars

$ProjectName = ‘KT’ # Ocotpus library set name
$librarySetName = ‘Sample.Test.Appway’
$outRoot = ‘C:\Users<user>\Desktop\Output’ # output root path - creates if it doesn’t exist
$outFldr = ‘LibSetExport’ # output folder name

- general vars

[System.Collections.ArrayList]$results = @()

- Octopus vars

$AppOctopusSrvPRODURI = ‘’
$AppOctopusSrvPRODAPIKey = ‘removed for security’
[Hashtable]$oBaseURIHdr = @{ “X-Octopus-ApiKey” = $AppOctopusSrvPRODAPIKey }

BEGIN

- get environments

$envs = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/environments/all”) -ContentType application/json -Headers $oBaseURIHdr -Method Get

Get projec

$libSet = (Invoke-RestMethod -Method Get -Uri ($AppOctopusSrvPRODURI + “/api/projects/all”) -ContentType application/json -Headers $oBaseURIHdr) | Where-Object {$_.Name -eq $ProjectName}

Get project variables

$libSetDet = Invoke-RestMethod -Method Get -Uri ($AppOctopusSrvPRODURI + $libSet.links.Variables) -ContentType application/json -Headers $oBaseURIHdr

- get library set details

$librarySet = (Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/?partialName=$librarySetName”) -ContentType application/json -Headers $oBaseURIHdr -Method Get).items
$librarySetDet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + $librarySet.links.Variables) -ContentType application/json -Headers $oBaseURIHdr -Method Get

if ($results.Count -gt 0) { $results.Clear() }
foreach ($var in $libSetDet.Variables) {

$scopeString = $null
$varobj = $null

# - handle scoped vs non-scoped variables differently
if ( $var.scope.psObject.Properties.Name -eq 'Environment' ) {
    
    # - build scope string
    $scopeList = DO-BuildList -Userlist $var.Scope.Environment

    foreach ($item in $scopeList ) {
        $scopeName = ($envs | ? {$_.Id -eq $item}).name + ', '
        $scopeString = $scopeString + $scopeName
    }
}
elseif ( $var.scope.psObject.Properties.Name -eq 'Role' ) {
    $scopeList = DO-BuildList -Userlist $var.Scope.Role
    
    foreach ($item in $scopeList ) {
        $scopeName = $item + ', '
        $scopeString = $scopeString + $scopeName
    }
}
else { $scopeString = 'NONE'}
$scopeString = $scopeString.TrimEnd(', ')

# - create custom obj
$varobj = [pscustomobject] @{
    Source = $libSetName
    Name = $var.Name
    Value = $var.Value
    Scope =  $scopeString
    IsSensitive = $var.IsSensitive
}

# - add to final results obj
$results.Add($varobj) | Out-Null

}

- export results to csv

$now = get-date -Format “yyMMdd_HHmm”
$outFile = $libSetName
$outPath = ($outRoot + ‘’ + $outFldr + ‘’ + $outFile + ‘_’ + $now +’.csv’)

- create target folder

if ( ! ( test-path ($outRoot + ‘’ + $outFldr))) { New-Item -Path ($outRoot + ‘’ + $outFldr) -ItemType Directory -Force | Out-Null }

- output file

if ( test-path $outPath ) { Remove-Item $outPath -Force | Out-Null }
$results | export-csv $outPath -NoTypeInformation -Force

Update the collection

#Invoke-RestMethod -Method Put -Uri “$octopusURL/api/variables/$libSetDet” -Headers $header -Body ($projectVariables | ConvertTo-Json -Depth 10)

$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/?partialName=$librarySetName”) -Headers $oBaseURIHdr -Method Put -Body ($results | ConvertTo-Json -Depth 10)
#$envs = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/environments/all”) -ContentType application/json -Headers $oBaseURIHdr -Method Get

Error stack trace:

Invoke-RestMethod : The remote server returned an error: (405) Method Not Allowed.
At line:99 char:30

  • … riableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + "/api/lib …
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    • FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Hi @anilkumar.pantham,

Thanks for reaching out and for the code snippets.

I think the problem we are running into here is this line:

$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/?partialName=$librarySetName”) -Headers $oBaseURIHdr -Method Put -Body ($results | ConvertTo-Json -Depth 10)

the URI will need to be a specific variableset ID after that last slash, you can’t do a search on partial name as a PUT, only a GET. Somewhere in your code you will need to store the variable set ID to a variable and put it after that last slash.

Please let me know if that gets you unstuck.

Best,
Jeremy

Thanks, @jeremy.miller for the quick response.

I defined a variable to store variable set ID. That is $libraryset.

PS C:\WINDOWS\system32> $librarySet.Id
LibraryVariableSets-4584

And I added $librarySet.Id variable to “PUT” rest method call, now getting 500 error. Any thoughts?
From the browser If I call the same URI, I’m able to get a response. But POST is failing.

$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/$libraryset.Id”) -Headers $oBaseURIHdr -Method PUT -Body ($results | ConvertTo-Json -Depth 10) -ContentType “application/json”

Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error.
At line:1 char:30

  • … riableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + "/api/lib …
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    • FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Hi @anilkumar.pantham,

On this line…


$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/$libraryset.Id”) -Headers $oBaseURIHdr -Method PUT -Body ($results | ConvertTo-Json -Depth 10) -ContentType “application/json”

You may need to encapsulate that new variable like this…

$($libraryset.Id)


$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/$($libraryset.Id)”) -Headers $oBaseURIHdr -Method PUT -Body ($results | ConvertTo-Json -Depth 10) -ContentType “application/json”

Can you please give that a try and let me know if it resolves it?

Best,
Jeremy

Hi @jeremy.miller

I tried as you suggested, but it’s still the same thing. I also enabled toggle breakpoint, but that doesn’t help.

$UpdatedLibraryVariableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + “/api/libraryvariablesets/$($libraryset.Id)”) -Headers $oBaseURIHdr -Method PUT -Body ($results | ConvertTo-Json -Depth 10) -ContentType “application/json”

PS C:\WINDOWS\system32> C:\Users<myuser>\Desktop\Variablecopy.ps1

Hit Line breakpoint on ‘C:\Users<myuser>\Desktop\Variablecopy.ps1:99’
[DBG]: PS C:\WINDOWS\system32>>
Invoke-RestMethod : The remote server returned an error: (500) Internal Server Error.
At C:\Users<myuser>\Desktop\Variablecopy.ps1:99 char:30

  • … riableSet = Invoke-RestMethod -Uri ($AppOctopusSrvPRODURI + "/api/lib …
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    • FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Hey @anilkumar.pantham,

I reviewed your script with a few colleagues, and we noticed you were creating a new object for the variables to transfer them to the library variable set. You should be able to avoid this by copying the variable objects from the project over to the library variable set. Also, I’m not quite sure what was causing the 500 error, but it may be related to this.

The following script should accomplish what you’re after, or at least you should be able to use it as an example to work from to modify your own.

As with any script we provide, please test prior to running and understand what it does. Fill in the details for your instance, the space, the project, and the variable set names:

# Define working variables
$octopusURL = "https://YOUR_OCTO_URL"
$octopusAPIKey = "API-#####"
$header = @{ "X-Octopus-ApiKey" = $octopusAPIKey }
$spaceName = "YOUR_SPACE_NAME"
$projectName = "YOUR_PROJECT_NAME"
$variableSetName = "YOUR_LIBRARY_VARIABLE_SET_NAME"

# Get space
$space = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/spaces/all" -Headers $header) | Where-Object {$_.Name -eq $spaceName}

# GET PROJECT VARIABLES
# Get project
$project = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/projects/all" -Headers $header) | Where-Object {$_.Name -eq $projectName}

# Get project variables
$projectVarSetLink = $project.Links.Variables
$projectVarSet = (Invoke-RestMethod -Method Get -Uri "$octopusURL/$($projectVarSetLink)" -Headers $header)

# GET VARIABLESET VARIABLES
# Get variableset from space
$variableSet = (Invoke-RestMethod -Method Get -Uri "$octopusURL/api/$($space.Id)/libraryvariablesets/all" -Headers $header) | Where-Object {$_.Name -eq $variableSetName}

# Get variableset variables
$variableSetVarLink = $variableSet.Links.Variables
$variableSetVars = (Invoke-RestMethod -Method Get -Uri "$octopusURL/$($variableSetVarLink)" -Headers $header)

# ADD PROJECT VARIABLES TO LIBRARY VARIABLE SET
# Create new variable set with project variables
[array]$projVarsToTransfer = $projectVarSet.Variables

# Add library variables to new variable set
foreach($varsetVar in $variableSetVars.Variables){
    $projVarsToTransfer += $varsetVar
}

# UPDATE LIBRARY VARIABLE SET
# Update the variables in the library variable set
$variableSetVars.Variables = $projVarsToTransfer

# Put/Update variable set to commit changes
Invoke-RestMethod -Method Put -Uri "$octopusURL/$($variableSetVarLink)" -Headers $header -Body ($variableSetVars | ConvertTo-Json -Depth 10)

I’ve tested this on my end and it works as expected, so I hope that’s helpful but let us know if you have any questions.

Best,
Patrick

Thank you @patrick.smergut

I will test it and let you know the outcome.

1 Like