AzureDevOpsPS.psm1

# Azure DevOps PS module
function Test-AzDevOpsSession {
    if ($null -eq $Global:headers) {
        Write-Warning "You are not connected to Azure DevOps use Add-AzDevOpsAccount -accesstoken '<PAT>' to connect."
        break
    }
}
function Add-AzDevOpsAccount {
    
    # Add-AzDevOpsAccount -accesstoken "000000000000000000000"
    
    param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $accesstoken
    )

    $encodedAzureDevOpsPatToken = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$accesstoken"))
    $Global:Headers = @{Authorization = ("Basic {0}" -f $encodedAzureDevOpsPatToken) }

    return $Headers
}

function Get-AzDevOpsServiceEndpoint {
    <#
    .Synopsis
       For now the function gets a Azure DevOps service endpoint ID.
    .DESCRIPTION
       Later it will be expanded to do more stuff - see TODO
    .EXAMPLE
       Get-AzDevOpsServiceEndpointId -organization "devopsglobal" -project "Azure-Samples" -Headers $headers -endpointName "<endpoint>"
    .OUTPUTS
       e.g. 1232362eb-4fd8-4cbc-a671-88f2eb23dcaz
    #>


    # TODO - add switch and expand function to get more properties such as Name, ID etc.
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $project,
        [Parameter(Mandatory = $true)]
        $endpointName,
        [Parameter(Mandatory = $false)]
        $headers
    )

    Test-AzDevOpsSession

    $requestParameters = @{
        
        Uri     = "https://dev.azure.com/$organization/$project/_apis/serviceendpoint/endpoints?api-version=5.0-preview.2"
        Method  = "GET"
        Headers = $global:Headers

    }

    (((Invoke-RestMethod @requestParameters -UseBasicParsing).value) | Where-Object { $_.name -eq $endpointName })
}
function New-AzDevOpsServiceEndpoint {
    
<# New-AzDevOpsServiceEndpoint -Organization "<organization>" `
                        -Project "<project>" `
                        -tenantid "<tenantId>" `
                        -servicePrincipalId "<service principal Id>" `
                        -appPassword "<password>" `
                        -subscriptionName "<subscription name>" `
                        -subscriptionId "<subscription id>" `
                        -AzureDevOpsServiceName "<new name>" `
                        -headers $headers #>


            param(
            [CmdletBinding]
            [Parameter(Mandatory = $true)]
            $tenantid,
            [Parameter(Mandatory = $true)]
            $servicePrincipalId,
            [Parameter(Mandatory = $true)]
            $Organization,
            [Parameter(Mandatory = $true)]
            $Project,
            [Parameter(Mandatory = $true)]
            $appPassword, # TODO - Secure this
            [Parameter(Mandatory = $true)]
            $subscriptionName,
            [Parameter(Mandatory = $true)]
            $subscriptionId,
            [Parameter(Mandatory = $true)]
            $AzureDevOpsServiceName,
            [Parameter(Mandatory = $false)]
            $headers
        )

        Test-AzDevOpsSession

             $requestBody = @{
              "authorization"= @{
                "parameters"= @{
                  "tenantid"= "$tenantId"
                  "serviceprincipalid"= "$servicePrincipalId"
                  "authenticationType"= "spnKey"
                  "serviceprincipalkey"= "$appPassword"
                }
            "scheme"= "ServicePrincipal"
          }
              "data"= @{
                "subscriptionId"= "$subscriptionId"
                "subscriptionName"= "$subscriptionName"
                "environment"= "AzureCloud"
                "scopeLevel"= "Subscription"
                "creationMode"= "Manual"
              }
              "name"= "$AzureDevOpsServiceName"
              "type"= "azurerm"
              "url"= "https=//management.azure.com/"
        
        } | ConvertTo-Json


        $requestParameters = @{
        
            Uri = "https://dev.azure.com/$Organization/$Project/_apis/serviceendpoint/endpoints?api-version=5.1-preview.2"
            Method = 'POST' 
            ContentType = "application/json"
            Headers = $global:headers
            Body = $requestBody
        
        }
        
        Invoke-RestMethod @requestParameters -UseBasicParsing
}2
function Add-AzDevOpsEndpointRole {

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $groupId,
        [Parameter(Mandatory = $true)]
        $endpintId,
        [Parameter(Mandatory = $true)]
        $projectId,
        [Parameter(Mandatory = $true)]
        $organizationName,
        [Parameter(Mandatory = $false)]
        $headers
    )
        Test-AzDevOpsSession

        $requestBody = "[{roleName: `"User`", userId: `"$groupId`"}]"

        $requestParameters = @{

            Uri = "https://$organizationName.visualstudio.com/_apis/securityroles/scopes/distributedtask.serviceendpointrole/roleassignments/resources/$projectId`_$endpintId`?api-version=5.0-preview.1"
            Method = "PUT"
            Headers = $global:headers
            Body = $requestBody
            ContentType = "application/json";
        
        }

        Invoke-RestMethod @requestParameters -UseBasicParsing
}

function Get-AzDevOpsSecurityGroup {
    
    # Get-AzDevOpsSecurityGroup -securitygroup "[Buzzer]\Buzzer Team" -organization "devopsglobal" -Headers $headers
    # TODO - add switch and expand function to get properties as well e.g ID.
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $securitygroup,
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

    $requestParameters = @{
        
        Uri     = "https://vssps.dev.azure.com/$organization/_apis/graph/groups?api-version=5.0-preview.1"
        Method  = "GET"
        Headers = $Global:headers        

    }

    ((Invoke-RestMethod @requestParameters -UseBasicParsing).value | Where-Object { $_.principalName -eq $securitygroup })

}

function Get-AzDevOpsProject {

    # Get-AzDevOpsProject -organization "devopsglobal" -project "Azure" -Headers $headers
    
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $project,
        [Parameter(Mandatory = $false)]
        $Headers
    ) 
    
    Test-AzDevOpsSession

    $requestParameters = @{
        
        Uri     = "https://dev.azure.com/$organization/_apis/projects?api-version=5.0"
        Method  = "GET"
        Headers = $global:headers

    }
    
    ((Invoke-RestMethod @requestParameters -UseBasicParsing).value | Where-Object { $_.Name -eq $project })

}

function Get-AzDevOpsVarGroup {
    
    # Get-AzDevOpsVarGroup -organization "devopsglobal" -project "Azure-Samples" -varGroup "var2" -headers $headers

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $project,
        [Parameter(Mandatory = $true)]
        $varGroup,
        [Parameter(Mandatory = $false)]
        $headers
    )

    Test-AzDevOpsSession

    $requestProperties = @{
    
        Method  = "Get"
        Uri     = "https://dev.azure.com/$organization/$project/_apis/distributedtask/variablegroups?groupName=$varGroup&queryOrder=IdDescending&api-version=5.0-preview.1"
        Headers = $Global:headers
    }

    Invoke-RestMethod @requestProperties
}

function Get-AzDevOpsVarGroupSecurity {
    
    # Get-VariableGroupSecurity -Headers $headers -organization devopsglobal -varGroupId 2 -groupId 8213a116-e7d5-47ae-a24a-b8324d8cd45f -projectid 3214bf44-6448-4c39-95c7-0159d4ff2b7e

    param (
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $varGroupId,
        [Parameter(Mandatory = $true)]
        $groupId,
        [Parameter(Mandatory = $true)]
        $projectid,
        [Parameter(Mandatory = $false)]
        $Headers
    )
    
    Test-AzDevOpsSession

    $encodedValue = [System.Web.HttpUtility]::UrlEncode('$' + $varGroupId) 
    $Uri = "https://dev.azure.com/$organization/_apis/securityroles/scopes/distributedtask.variablegroup/roleassignments/resources/$projectId$encodedValue"
    
    $requestParameters = @{
        Uri     = $Uri
        Method  = "GET"
        Headers = $Global:headers
    }

    Write-output $requestParameters.uri

    $requestParameters

    (Invoke-RestMethod @requestParameters).value
}

function Add-AzDevOpsVarGroupRole {

    # Add-VariableGroupRole -Headers $headers -organization devopsglobal -varGroupId 2 -projectid c1c4bf44-6448-4c39-95c7-0159d4ff2b7e -groupId "86b8a116-e7d5-47ae-a24a-b8324d8cd45f" -role "Administrator"

    param (
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $projectid,
        [Parameter(Mandatory = $true)]
        $varGroupId,
        [Parameter(Mandatory = $true)]
        $groupId,
        [Parameter(Mandatory = $true)]
        [ValidateSet("Reader", "User", "Administrator")]
        $role,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

    $encodedValue = [System.Web.HttpUtility]::UrlEncode('$' + $varGroupId) 
    $Uri = "https://dev.azure.com/$organization/_apis/securityroles/scopes/distributedtask.variablegroup/roleassignments/resources/$projectId$encodedValue" + "?api-version=5.0-preview.1"
    
    $requestBody = "[{roleName: `"$role`", userId: `"$groupId`" }]"

    $requestParameters = @{
        Uri         = $Uri
        Method      = "PUT"
        Headers     = $Global:headers
        Body        = $requestBody
        ContentType = "application/json; api-version=5.0-preview.1"
    }

    Write-output $requestParameters.uri

    $requestParameters

    (Invoke-RestMethod @requestParameters)
}

# Boards

function Add-AzDevOpsCliLogin {

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $AccessToken
    )

    Write-Host "Logging in to $Organization" 
    Write-Output $AccessToken | az devops login --organization "https://dev.azure.com/$Organization/"

}

function Find-AzDevOpsWorkItem {

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $organization,
        [Parameter(Mandatory = $true)]
        $query,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

    $Headers = $Global:headers

    $Headers.Remove('Content-Type') 
    $Headers.Add("Content-Type", "application/json")
    
    $body = @{
  
       "query" = $query
    
    } | ConvertTo-Json

    $requestParameters = @{
        
        Uri     = "https://dev.azure.com/$organization/_apis/wit/wiql?api-version=5.1"
        Method  = "POST"
        Headers = $headers
        Body = $body

    }

    (Invoke-RestMethod @requestParameters -UseBasicParsing)
}

function Get-AzDevOpsWorkItem {
    
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $id,
        [Parameter(Mandatory = $false)]
        $Headers
    )
    
    Test-AzDevOpsSession

    $requestParameters = @{
    
        Uri = "https://dev.azure.com/$Organization/_apis/wit/workitems/$id`?`$expand=all&api-version=5.1"
        Method = 'GET'
        Headers = $Global:headers
    
    }
    
    (Invoke-RestMethod @requestParameters -UseBasicParsing)

}

function New-AzDevOpsWorkItem {
    
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $Project,
        [Parameter(Mandatory = $true)]
        $WorkItemType,
        [Parameter(Mandatory = $true)]
        $WorkItemName,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

    $Headers = $Global:headers

    $Headers.Remove('Content-Type') 
    $Headers.Add('Content-Type', 'application/json-patch+json')

    $Body = @()

    class RequestBody {
        [string]$op = "add"
        [string]$path = "/fields/System.Title"
        [string]$from = $null
        [string]$value = $WorkItemName
        }

    $row = [RequestBody]::New() 
    $Body += $row
    $BodyJson = ConvertTo-Json -InputObject $Body
    $BodyJson

    
    $requestParameters = @{
    
        Uri = "https://dev.azure.com/$Organization/$Project/_apis/wit/workitems/`$$WorkItemType`?api-version=5.1"
        Method = 'POST'
        Headers = $Headers
        Body = $BodyJson
    
    }
    
    (Invoke-RestMethod @requestParameters -UseBasicParsing)

}

function Update-AzDevOpsWorkItem {
    
  # Update-AzDevOpsWorkItem -Organization 'devopsglobal' -Project 'Azure-Samples' -releaseWorkItemId 1 -workitemId 6 -Headers $header
   
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $workitemId,
        [Parameter(Mandatory = $true)]
        $workitemRev,
        [Parameter(Mandatory = $true)]
        $releaseWorkItemId,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

    $Headers = $Global:headers

    $Headers.Remove('Content-Type') 
    $Headers.Add('Content-Type', 'application/json-patch+json')

$Body = @(
    @{
         'op' = "Test"
         'path' = '/rev'
         'from' = $null
         'value' = $workitemRev
     }
    @{ 
        "op"= "add"
        "path"= "/relations/-"
        "value"= @{
             "rel"= "System.LinkTypes.Related"
             "url"= "https://dev.azure.com/$Organization/_apis/wit/workItems/$workitemId"
             }
    }
) | ConvertTo-Json

    $requestParameters = @{
    
        Uri = "https://dev.azure.com/$Organization/_apis/wit/workitems/$releaseWorkItemId`?api-version=5.1"
        Method = 'PATCH'
        Headers = $Headers
        Body = $Body
    
    }
    
    (Invoke-RestMethod @requestParameters -UseBasicParsing)

}

function Get-AzDevOpsDeploymentGroup {
    
    # Get-AzDevOpsDeploymentGroup -Organization 'devopsglobal' -Project 'Azure-Samples' -deploymentGroupName "Test2" -Headers $headers

        param(
        [CmdletBinding]
    [Parameter(Mandatory = $true)]
    $Organization,
    [Parameter(Mandatory = $true)]
    $Project,
    [Parameter(Mandatory = $true)]
    $deploymentGroupName,
    [Parameter(Mandatory = $false)]
    $Headers    
    )

    Test-AzDevOpsSession

    $check = @{
    
    Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/deploymentgroups?api-version=5.0-preview.1"
    Method = 'GET'
    Headers = $global:headers
  
  }

    (Invoke-RestMethod @check -UseBasicParsing).value | Where-Object {$_.Name -eq $deploymentGroupName}
}

function New-AzDevOpsDeploymentGroup {

    # New-AzDevOpsDeploymentGroup -Organization 'devopsglobal' -Project 'Azure-Samples' -deploymentGroupName "Test3" -Headers $headers
    
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $Project,
        [Parameter(Mandatory = $true)]
        $deploymentGroupName,
        [Parameter(Mandatory = $false)]
        $headers
    )

    Test-AzDevOpsSession

    $body = @{
            name = "$deploymentGroupName"
            description = "Deployment group created during automatic deployment"
        } | ConvertTo-Json

    $create = @{
        Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/deploymentgroups?api-version=5.0-preview.1"
        Method = 'POST'
        Headers = $Global:headers
        Body = $body
        ContentType = "application/json"
    }

    (Invoke-RestMethod @create -UseBasicParsing)

}

function Add-AzDevOpsDeploymentGroupRole {

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $groupId,
        [Parameter(Mandatory = $true)]
        $deploymentGroupId,
        [Parameter(Mandatory = $true)]
        $projectId,
        [Parameter(Mandatory = $true)]
        $organizationName,
        [Parameter(Mandatory = $false)]
        $Headers
    )

    Test-AzDevOpsSession

        $requestBody = "[{roleName: `"User`", userId: `"$groupId`"}]"

        $requestParameters = @{

            Uri = "https://$organizationName.visualstudio.com/_apis/securityroles/scopes/distributedtask.machinegrouprole/roleassignments/resources/$projectId`_$deploymentGroupId`?api-version=5.1-preview.1"
            Method = "PUT"
            Headers = $Global:headers
            Body = $requestBody
            ContentType = "application/json"

    }
    Invoke-RestMethod @requestParameters -UseBasicParsing
}

# Variable group operations

function Get-AzDevOpsVariableGroup {

    # Get-AzDevOpsVariableGroup -Organization 'devopsglobal' -Project 'Azure-Samples' -AzureDevOpsVarGroupName "test2" -headers $headers
    
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $Project,
        [Parameter(Mandatory = $true)]
        $AzureDevOpsVarGroupName,
        [Parameter(Mandatory = $false)]
        $headers
    )

    Test-AzDevOpsSession

    $requestParameters = @{
    
        Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=4.1-preview.1"
        Method = 'GET'
        Headers = $Global:headers
        
    }

    (Invoke-RestMethod @requestParameters -UseBasicParsing).value | Where-Object {$_.name -eq $AzureDevOpsVarGroupName}
}

function New-AzDevOpsVariableGroup {

    # TODO - Document how the variable object works
    # New-AzDevOpsVariableGroup -Organization 'devopsglobal' -Project 'Azure-Samples' -AzureDevOpsVarGroupName 'Test' -azureDevOpsEndpointId '125b66eb-4fd8-4cbc-a671-88f2eb23dcac' -variables $variables -headers $headers

        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $Project,
        [Parameter(Mandatory = $true)]
        $AzureDevOpsVarGroupName,
        [Parameter(Mandatory = $true)]
        $azureDevOpsEndpointId,
        [Parameter(Mandatory = $true)]
        $variables,
        [Parameter(Mandatory = $false)]
        $headers
    )

    Test-AzDevOpsSession

    $requestBody = @{
          "type" = "AzureKeyVault"
          "name" = "$AzureDevOpsVarGroupName"
          "providerData" = @{
            "serviceEndpointId"= "$azureDevOpsEndpointId"
            "vault" = "$keyVaultName"
          }
          "variables" = "$variables"
} | ConvertTo-Json

    $requestParameters = @{
        
        Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups?api-version=4.1-preview.1"
        Method = "POST"
        Headers = $Global:headers
        Body = $requestBody
        ContentType = "application/json"

    }

    (Invoke-RestMethod @requestParameters -UseBasicParsing)

}

function Update-AzDevOpsVariableGroup {
    # TODO - Document how the variables object works
        param(
        [CmdletBinding]
        [Parameter(Mandatory = $true)]
        $Organization,
        [Parameter(Mandatory = $true)]
        $Project,
        [Parameter(Mandatory = $true)]
        $variableGroupId,
        [Parameter(Mandatory = $true)]
        $AzureDevOpsVarGroupName,
        [Parameter(Mandatory = $true)]
        $azureDevOpsEndpointId,
        [Parameter(Mandatory = $true)]
        $keyVaultName,
        [Parameter(Mandatory = $true)]
        $variables,
        [Parameter(Mandatory = $false)]
        $headers
    )
        Test-AzDevOpsSession

        $requestBody = @{
          "type" = "AzureKeyVault"
          "name" = "$AzureDevOpsVarGroupName"
          "providerData" = @{
            "serviceEndpointId"= "$azureDevOpsEndpointId"
            "vault" = "$keyVaultName"
          }
          "variables" = "$variables"
} | ConvertTo-Json

    $requestParameters = @{
        
        Uri = "https://dev.azure.com/$Organization/$Project/_apis/distributedtask/variablegroups/$variableGroupId`?api-version=4.1-preview.1"
        Method = "PUT"
        Headers = $Global:headers
        Body = $requestBody
        ContentType = "application/json"

    }

    (Invoke-RestMethod @requestParameters -UseBasicParsing)

}

function Remove-AzureDevOpsDeploymentGroup {

    param(
        $deploymentGroupId,
        $organization,
        $project,
        $Headers
    )
    
    $requestParameters = @{

    Uri     = "https://dev.azure.com/$organization/$project/_apis/distributedtask/deploymentgroups/$deploymentGroupId`?api-version=5.0-preview.1"
    Method  = "DELETE"
    Headers = $Headers
}
    
    Invoke-RestMethod @requestParameters -UseBasicParsing -ContentType "application/json; api-version=5.0-preview.1"
}

# Removes Service Connection from Azure DevOps
function Remove-AzDevOpsServiceConncetion {

    param(
        $organization,
        $project,
        $endpointName,
        $Headers
    )

    if (Get-Command Get-AzDevOpsServiceEndpoint) { 
    
    $endpointID = (Get-AzDevOpsServiceEndpoint -organization $organization -project $project -endpointName $endpointName -Headers $Headers).id

    $requestParameters = @{
        
        Uri     = "https://dev.azure.com/$organization/$project/_apis/serviceendpoint/endpoints/$endpointID`?api-version=5.1-preview.2"
        Method  = "DELETE"
        Headers = $Headers

    }

    (((Invoke-RestMethod @requestParameters -UseBasicParsing).value) | Where-Object { $_.name -eq $endpointName })
    
    }
    else {
        Write-Output "You need to import Get-AzDevOpsServiceEndpoint command for this function to work"
    }

}

function Remove-AzureDevOpsVariableGroup {

    param(
        $variableGroupId,
        $organization,
        $project,
        $Headers
    )
    
    $requestParameters = @{

    Uri     = "https://dev.azure.com/$organization/$project/_apis/distributedtask/variablegroups/$variableGroupId`?api-version=5.0-preview.1"
    Method  = "DELETE"
    Headers = $Headers
    ContentType = "application/json; api-version=5.0-preview.1"
}
    
    $requestParameters

    Invoke-RestMethod @requestParameters
}