Skip to content

Instantly share code, notes, and snippets.

@gitfvb
Last active January 22, 2020 08:33
Show Gist options
  • Save gitfvb/5b053eb23639c31c1df0a5d3dbd12bf0 to your computer and use it in GitHub Desktop.
Save gitfvb/5b053eb23639c31c1df0a5d3dbd12bf0 to your computer and use it in GitHub Desktop.
use profitbricks/IONOS API through powershell
# settings files
ionos.json
# encryption files
aes.key

Requirements

  • No external dependencies yet except of Windows and PowerShell 5.1 at minimum. And there is Invoke-WebRequest used instead of Invoke-Restmethod, which means you need a present installation of Internet Explorer. I use this to get the http status code back. But there is a parameter where you can change that behaviour.

Getting Started

  • Just download the script, execute it.
  • At the first execution you will be asked for your default settings and credentials.
  • All settings will be saved in the same directory in a json file. The password is double encrypted.

Hints

  • The aes.key and the ionos.json is not portable as the encryption is system-specific. If you copy everything to another computer, you will get a message like this: 2019-12-23 11_58_27-demonstration apteco-faststats de - Remotedesktopverbindung
################################################
#
# NOTES
#
################################################
################################################
#
# SCRIPT ROOT
#
################################################
# Load scriptpath
if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript") {
$scriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
} else {
$scriptPath = Split-Path -Parent -Path ([Environment]::GetCommandLineArgs()[0])
}
Set-Location -Path $scriptPath
################################################
#
# SETTINGS
#
################################################
# General settings
$settingsFilename = "ionos.json"
# Request type for calling the api
$requestType = [ordered]@{
"WebRequest"="(Default) Using WebRequest to get the status code back of last API call, requires Internet Explorer Engine installed (normally installed by default)."
"RestMethod"="Using RestMethod as more modern way of API calls. This does not deliver the status code back, only from PowerShell Core 6 upwards."
}
################################################
#
# LOAD FUNCTIONS
#
################################################
Function Get-PlaintextToSecure {
param(
[Parameter(Mandatory=$true)][String]$String
)
# generate salt
Create-KeyFile -keyfilename "$( $scriptPath )\aes.key" -byteLength 32
$salt = Get-Content -Path "$( $scriptPath )\aes.key" -Encoding UTF8
# convert
$stringSecure = ConvertTo-secureString -String $String -asplaintext -force
$return = ConvertFrom-SecureString $stringSecure -Key $salt
# return
$return
}
Function Get-SecureToPlaintext {
param(
[Parameter(Mandatory=$true)][String]$String
)
# generate salt
$salt = Get-Content -Path "$( $scriptPath )\aes.key" -Encoding UTF8
#convert
$stringSecure = ConvertTo-SecureString -String $String -Key $salt
$return = (New-Object PSCredential "dummy",$stringSecure).GetNetworkCredential().Password
#return
$return
}
Function Create-KeyFile {
param(
[Parameter(Mandatory=$false)][string]$keyfilename = "$( $scriptPath )\aes.key"
,[Parameter(Mandatory=$false)][int]$byteLength = 32
)
#$keyfile = ".\$( $keyfilename )"
# file does not exist -> create one
if ( (Test-Path -Path $keyfilename) -eq $false ) {
$Key = New-Object Byte[] $byteLength # You can use 16, 24, or 32 for AES
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
$Key | Set-Content -Encoding UTF8 -Path $keyfilename
}
}
Function Invoke-IONOS {
param(
[Parameter(Mandatory=$true)][string]$url
,[Parameter(Mandatory=$true)][Hashtable]$headers
,[Parameter(Mandatory=$false)][string]$method = "GET" # TODO [ ] maybe use the method type as an enum
)
# TODO [ ] maybe use the request type as an enum
if ( $settings.requestTypeDefault -eq "RestMethod" ) {
$res = Invoke-RestMethod -Headers $headers -Uri $url -Verbose -Method $method # In Powershell 6 we can directly use Invoke-Restmethod to get the headers
$statusString = "Call is done: $( $res )"
} else {
$res = Invoke-WebRequest -Headers $headers -Uri $url -Verbose -Method $method # In Powershell 6 we can directly use Invoke-Restmethod to get the headers
$statusString = "Status $( $res.StatusCode ) with url $( $res.Headers.Location )"
}
return $statusString
}
################################################
#
# MANAGE CREDENTIALS
#
################################################
if ( (Test-Path -Path $settingsFilename) -eq $false ) {
# Ask for default values
$baseDefault = "https://api.ionos.com/cloudapi/v4/"
$base = read-host "Using default API adress? Just press enter for [$( $baseDefault )]"
if ( $null -eq $base ) {
$base = $baseDefault
}
# Using WebRequest or RestMethod at last API call?
$requestTypeDefault = $requestType | Out-GridView -PassThru
# Ask for credentials
$username = read-host "Please enter your username"
$password = read-host "Please enter the password" -assecurestring
# Create credentials object
$cred = @{
username=$username
password= ( Get-PlaintextToSecure ( $password | convertfrom-securestring ))
}
# Create settings
$settings = @{
base = $base
requestTypeDefault = $requestTypeDefault.Name
credentials = $cred
}
# create json object
$json = $settings | ConvertTo-Json -Depth 8 # -compress
# print settings to console
$json
# save settings to file
$json | Set-Content -path "$( $scriptPath )\$( $settingsFilename )" -Encoding UTF8
# destroy credentials cache
$username = ""
$password = ""
}
################################################
#
# PREPARE API CALLS
#
################################################
#-----------------------------------------------
# LOAD SETTINGS
#-----------------------------------------------
# Load settings
$settings = Get-Content -Path "$( $scriptPath )\$( $settingsFilename )" -Encoding UTF8 -Raw | ConvertFrom-Json
$base = $settings.base
#-----------------------------------------------
# LOAD CREDENTIALS
#-----------------------------------------------
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $settings.credentials.username,(( Get-SecureToPlaintext $settings.credentials.password ) | ConvertTo-SecureString)
#-----------------------------------------------
# PREPARE HEADER
#-----------------------------------------------
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f ( $credentials.GetNetworkCredential().Username ),($credentials.GetNetworkCredential().password))))
$headers = @{
Authorization=("Basic {0}" -f $base64AuthInfo)
}
################################################
#
# HANDLE IONOS API
#
################################################
#-----------------------------------------------
# WHAT TO DO?
#-----------------------------------------------
# operations
$operations = [ordered]@{
"list"="list all servers"
"start"="start a server"
"stop"="stop a server / end provisioning"
"reboot"="do a hard restart"
"rdp"="remotedesktop"
"nothing"="nothing"
}
$operation = $operations | Out-GridView -PassThru
#-----------------------------------------------
# LOAD DATACENTERS FIRST
#-----------------------------------------------
# get all datacenters
$datacenters = Invoke-RestMethod -Headers $headers -Uri "$( $base )datacenters?pretty=true&depth=0" -Verbose -Method Get -ContentType "application/json" #| Select -expand companies
# get details for all datacenters
$datacentersDetail = @()
$datacenters.items | ForEach {
$url = $_.href
$dc = Invoke-RestMethod -Headers $headers -Uri "$( $url )?pretty=true&depth=4" -Verbose -Method Get -ContentType "application/json"
$datacentersDetail += $dc
}
#-----------------------------------------------
# BUILD SERVER LIST
#-----------------------------------------------
# build custom list for all servers
$servers = @()
$nics = @()
$datacentersDetail | ForEach {
$dataCenterId = $_.id # = New-Object -TypeName PSCustomObject
$dataCenterName = $_.properties.name
$_.entities.servers.items | ForEach {
$server = $_.properties.PSObject.Copy()
$server | Add-Member -Type NoteProperty -Name "datacenterId" -Value $dataCenterId
$server | Add-Member -Type NoteProperty -Name "datacenterName" -Value $dataCenterName
$server | Add-Member -Type NoteProperty -Name "serverId" -Value $_.id
$server | Add-Member -Type NoteProperty -Name "serverHref" -Value $_.href
$server | Add-Member -Type NoteProperty -Name "serverState" -Value $_.metadata.state
$servers += $server
# build custom list for NICs
$_.entities.nics.items | ForEach {
$nic = $_.properties.PSObject.Copy()
$nic | Add-Member -Type NoteProperty -Name "datacenterName" -Value $dataCenterName
$nic | Add-Member -Type NoteProperty -Name "serverName" -Value $server.name
$nics += $nic
}
}
}
#-----------------------------------------------
# DO THE OPERATION
#-----------------------------------------------
switch ( $operation.Name ) {
"list" {
$servers | Select datacenterName, name, cores, ram, vmState, serverState, cpuFamily, datacenterId, serverId, serverHref | Out-GridView
}
"start" {
$servers | Select datacenterName, name, cores, ram, vmState, serverState, cpuFamily, datacenterId, serverId, serverHref | where { $_.serverState -eq "INACTIVE" } | Out-GridView -PassThru | ForEach {
$url = "$( $_.serverHref )/$( $operation.Name )"
$res = Invoke-IONOS -url $url -headers $headers -method "POST"
"$( $res )"
}
}
"reboot" {
$servers | Select datacenterName, name, cores, ram, vmState, serverState, cpuFamily, datacenterId, serverId, serverHref | where { $_.serverState -eq "AVAILABLE" } | Out-GridView -PassThru | ForEach {
$url = "$( $_.serverHref )/$( $operation.Name )"
$res = Invoke-IONOS -url $url -headers $headers -method "POST"
"$( $res )"
}
}
"stop" {
$servers | Select datacenterName, name, cores, ram, vmState, serverState, cpuFamily, datacenterId, serverId, serverHref | where { $_.serverState -eq "AVAILABLE" } | Out-GridView -PassThru | ForEach {
$url = "$( $_.serverHref )/$( $operation.Name )"
$res = Invoke-IONOS -url $url -headers $headers -method "POST"
"$( $res )"
}
}
"rdp" {
$nics | Select datacenterName, serverName, name, mac, ips | where { $_.ips -ne "{}" } | Out-GridView -PassThru | ForEach {
mstsc /v:$($_.ips)
}
}
}
################################################
#
# FINISHING
#
################################################
# Wait until key pressed
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment