OctopusVariables.psm1

#Install-Module PowershellOctopusClientPS
Import-Module PowershellOctopusClientPS
(Get-Module -ListAvailable PowershellOctopusClientPS).ModuleBase

try
{
    $octoclientpath = Join-Path -Path (Get-Module -ListAvailable PowershellOctopusClientPS).ModuleBase -ChildPath "lib\Octopus.Client.dll"
    $newtonsoftPath = Join-Path -Path (Get-Module -ListAvailable PowershellOctopusClientPS).ModuleBase -ChildPath "lib\Newtonsoft.Json.dll"
    Add-Type -Path $newtonsoftPath
    Add-Type -Path $octoclientpath
}
catch [System.Reflection.ReflectionTypeLoadException]
{    #system.attonations always throws
    #Write-Host "Message: $($_.Exception.Message)"
    #Write-Host "StackTrace: $($_.Exception.StackTrace)"
    #Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
}
<#
    .SYNOPSIS
    return a setup client
#>

function LoadOctopusClientAndDependancies {
[OutputType([Octopus.Client.OctopusClient])]
Param(
        [parameter(Mandatory=$true)][String] $url,
        [parameter(Mandatory=$true)][String] $apiKey
    )
    
    $client = [Octopus.Client.OctopusClient]::new([Octopus.Client.OctopusServerEndpoint]::new($url, $apiKey))
    $client
}
<#
    .Parameter octopusURL

    .Parameter octopusAPIKey

    .Parameter enviromentScope

    .Parameter variableName

    .Parameter variableValue

    .Parameter space

    .Parameter project
#>

function SetTenantVariableInEnviromentScope {
    Param(
        [parameter(Mandatory=$true)][String]  $octopusURL,
        [parameter(Mandatory=$true)][String]  $octopusAPIKey,
        [parameter(Mandatory=$true)][String]  $enviromentScope,
        [parameter(Mandatory=$true)][String]  $variableName,
        [parameter(Mandatory=$true)][String]  $variableValue,
        [parameter(Mandatory=$false)][String] $space = '',
        [parameter(Mandatory=$false)][String] $project = '',
        [parameter(Mandatory=$false)][String] $tenantName = '',
        [parameter(Mandatory=$false)][String] $tenantTag = '',
        [parameter(Mandatory=$false)][switch] $WhatIf 
    )
    if( -not ([string]::IsNullOrEmpty($tenantTag))){
        if(-not([string]::IsNullOrEmpty($tenantName))){
            Throw 'Please provide only a tenant tag or tenant name paramater, both were provided. Exiting!'
        }
    }
    if([string]::IsNullOrEmpty($tenantTag)){
        if([string]::IsNullOrEmpty($tenantName)){
            Throw 'Please provide a tenant tag or tenant name. Exiting!'
        }
    }

    $spaces = $null

    $client = LoadOctopusClientAndDependancies -url $octopusURL -apiKey $octopusAPIKey   
    $repository = $client.ForSystem()    
    
    if([string]::IsNullOrEmpty($space)) {        
        $spaces = $repository.Spaces.GetAll()        
    }else{
        $spaces = $repository.Spaces.FindByName($space)        
    }

    $spaces | foreach { 
        try {
            $projects = $null
            $tenants = [System.Collections.Generic.List[Octopus.Client.Model.TenantResource]]::new()
            $currSpace = $repository.Spaces.FindByName($_.Name)
            $repositoryForSpace = $client.ForSpace($currSpace)
            $theEnviromentResource = $repositoryForSpace.Environments.FindByName($enviromentScope)
            if([string]::IsNullOrEmpty($tenantName) -eq $true){                
                $tmpTenants = $repositoryForSpace.Tenants.GetAll()
                if([string]::IsNullOrEmpty($tenantTag) -eq $true){
                    $tenants = $tmpTenants
                }else {
                    $tmpTenants | foreach {
                        $currTmpTenant = $_
                        if($currTmpTenant.TenantTags.Contains($tenantTag) -eq $true) {
                            $tenants.Add($currTmpTenant)
                        }
                    }
                }
            }else {
                $tenants = $repositoryForSpace.Tenants.FindByName($tenantName)
            }
            if([string]::IsNullOrEmpty($project)) {        
                $projects = $repositoryForSpace.Projects.GetAll()       
            } else {
                $projects = $repositoryForSpace.Projects.FindByName($project)        
            }
            $projects | foreach {
                $currProject = $_
                $tenants | foreach {
                    $currTenant = $_                    
                    $repositoryForSpace.TenantVariables.GetAll().Where({$_.TenantName -eq $currTenant.Name}) | foreach {
                        $currTVar = $_               
                        $currTVar.ProjectVariables.Values.Where({$_.ProjectName -eq $currProject.Name}) | foreach {
                            $projVar = $_          
                            $projectVariableId = $null
                            $projVar.Templates | foreach{ if($_.Name -eq $variableName) { $projectVariableId = $_.Id} }
                            if($projVar.Variables.ContainsKey($theEnviromentResource.Id)){                                
                                $varResource = [Octopus.Client.Model.PropertyValueResource]::new($variableValue,$false)                           
                                $projVar.Variables[$theEnviromentResource.Id].Remove($projectVariableId) | Out-Null
                                $projVar.Variables[$theEnviromentResource.Id].Add($projectVariableId,$varResource)                                 
                            }
                            else {
                                $varResource = [Octopus.Client.Model.PropertyValueResource]::new($variableValue,$false)
                                $newDic = [System.Collections.Generic.Dictionary[String,Octopus.Client.Model.PropertyValueResource]]::new()                                
                                $newDic.Add($projectVariableId,$varResource)
                                $projVar.Variables.Add($theEnviromentResource.Id,$newDic)                                                            
                            }
                            if($WhatIf -eq $false) {
                                Write-Output "Saving $($currTVar)"
                                $repositoryForSpace.TenantVariables.Modify($currTVar)
                            }else {
                                Write-Output "WHATIF: $($currTVar)"
                            }
                        }
                    }
                }
            }
        }catch {
            Write-Error "Error in SetTenantVariableInEnviromentScope $($_)"
        }
    }
}

function SetProjectVariableInEnviromentScope {
    Param(
        [parameter(Mandatory=$true)][String]  $octopusURL,
        [parameter(Mandatory=$true)][String]  $octopusAPIKey,
        [parameter(Mandatory=$true)][String]  $enviromentScope,
        [parameter(Mandatory=$true)][String]  $variableName,
        [parameter(Mandatory=$true)][String]  $variableValue,
        [parameter(Mandatory=$true)][String]  $space,
        [parameter(Mandatory=$true)][String]  $project,
        [parameter(Mandatory=$false)][switch] $WhatIf 
    )
    $spaces = $null

    $client = LoadOctopusClientAndDependancies -url $octopusURL -apiKey $octopusAPIKey   
    $repository = $client.ForSystem()    
    
    if([string]::IsNullOrEmpty($space)) {        
        $spaces = $repository.Spaces.GetAll()        
    }else{
        $spaces = $repository.Spaces.FindByName($space)        
    }

    $spaces | foreach { 
        try {
            $projects = $null
            $currSpace = $repository.Spaces.FindByName($_.Name)
            $repositoryForSpace = $client.ForSpace($currSpace)
            $theEnviromentResource = $repositoryForSpace.Environments.FindByName($enviromentScope)

            if([string]::IsNullOrEmpty($project)) {        
                $projects = $repositoryForSpace.Projects.GetAll()       
            } else {
                $projects = $repositoryForSpace.Projects.FindByName($project)        
            }

            $projects | foreach {
                $currProject = $_
                $vars = $repositoryForSpace.VariableSets.Get($currProject.VariableSetId)
                $scope = [Octopus.Client.Model.ScopeSpecification]::new()
                $scope.Add("Environment",[Octopus.Client.Model.ScopeValue]::new($theEnviromentResource.Id))
                $varSet = $vars.AddOrUpdateVariableValue($variableName,$variableValue,$scope)

                if($WhatIf -eq $false){
                    $repositoryForSpace.VariableSets.Modify($varSet)
                }
                write-output "Added variable $variableName to project $($currProject.Name) with enviroment scope $($enviromentScope)."
            }
        }catch {
            Write-Error "Error in SetProjectVariableInEnviromentScope $($_)"
        }
    }
}

<#
    .parameter
#>

function CreateVariableInSetWithValueAndEnviromentScope
{
    Param(
        [parameter(Mandatory=$true)][String] $octopusURL,
        [parameter(Mandatory=$true)][String] $octopusAPIKey,
        [parameter(Mandatory=$true)][String] $enviromentScope,
        [parameter(Mandatory=$true)][String] $space,
        [parameter(Mandatory=$true)][String] $variableSet,
        [parameter(Mandatory=$true)][String] $variableName,
        [parameter(Mandatory=$true)][String] $variableValue,
        [parameter(Mandatory=$false)][switch] $WhatIf
    )

    $client = LoadOctopusClientAndDependancies -url $octopusURL -apiKey $octopusAPIKey   
    $repository = $client.ForSystem()    
    $spaceResource = $repository.Spaces.FindByName($space)
    $repositoryForSpace = $client.ForSpace($spaceResource)
    $theEnviromentResource = $repositoryForSpace.Environments.FindByName($enviromentScope)
    $vSetResource = $repositoryForSpace.LibraryVariableSets.CreateOrModify($variableSet)

    $alreadyExists = $false
    try {
        $vSetResource.Variables.Instance.Variables | foreach {
            $currVar = $_
            if($currVar.Name -eq $variableName){
                if($currVar.Scope.ContainsKey('Environment')) {
                    if($_.Scope['Environment'].Contains($theEnviromentResource.Id) -eq $true) { 
                        $alreadyExists = $true
                    } 
                }
            }
        }
    }
    catch {
        Write-Output "Failed to detect if variable already exists in scope $($theEnviromentResource.Name)"
    }
    
    if($alreadyExists -eq $true) { 
        Write-Output "The variable $variableName already exists with an enviroment scope in $($theEnviromentResource.Name). This process will NOT update the value."
    }else {
        try {
            $scopeSpec = [Octopus.Client.Model.ScopeSpecification]::new()
            $scopeSpec.Add("Environment",[Octopus.Client.Model.ScopeValue]::new($theEnviromentResource.Id))
            $newVar = [Octopus.Client.Model.VariableResource]::new()
            $newVar.Description = 'Created by CreateVariableInSetWithValueAndEnviromentScope automation.'
            $newVar.Scope = $scopeSpec
            $newVar.Name  = $variableName
            $newVar.Value = $variableValue
            $newVar.IsSensitive = $false        
            $vSetResource.Variables.Instance.Variables.Add($newVar)
            if($WhatIf -eq $false) {
                $vSetResource.Variables.Save() | Out-Null   
                Write-Output "Created new value in VariableSet: $($vSetResource.Instance.Name) for Variable: $variableName with Value: $variableValue Scoped to Enviroment: $($theEnviromentResource.Name)"
            }else {
                Write-Output "WHATIF: Created new value in VariableSet: $($vSetResource.Instance.Name) for Variable: $variableName with Value: $variableValue Scoped to Enviroment: $($theEnviromentResource.Name)"
            }
        }catch{
            write-error $($_)
        }
    }    
}