# Add field for device opt-out - Passowrd Rotation Opt Out # pause instead of wait 7 seconds to let the user generate a new tokein in FireFox # fix for currentfolder to be returned to if Get-TokenfromFirefox fails and throws an exception # Version 2.68 - Updated Get-TokenFromFirefox to parse ## Info ## ## __| __ __| | / __| ## ( | . < ( _ \ _| -_) ## \___| _| _|\_\ \___| \___/ _| \___| ## ## Author: Nielsen Pierce ## Updated by Martin Howlett and Mark Wichall ## Description: PSv3 module for Querying Core Device(s) from cmd line ## Requires Powershell v3 # this function is being reorganised to be merged back with the other CTKCore version # if you add a function it must meet the following guidelines: # - start with Get-CoreTicket* Get-CoreDEvice* etc # - Have full help, with at least 1 example and parameter descriptions # - Use convertto-json to create json, not herestring # - mandatory params - no null values please # - not include validate-Token - this is done in get-corequery # - optimise your json # - minimum sub attributes needed to function - keep query small # - use switches to add attributes, dont just get everything by default ################################## ## Connection and auth functions ################################## #Functions used by all the other functions to get/save the CORE token and query CORE Function Get-Token { Param( [Parameter()]$SSOPass ) $count = 0 While (-NOT $Response) { #The -erroraction param doesnt work on Invoke-RestMethod so we have to temporarily turn it off for the whole script $OldEAP = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' IF ([Environment]::GetEnvironmentVariable("CoreUser", "User")) { $Username = [Environment]::GetEnvironmentVariable("CoreUser", "User") } ELSE { #Prompt for user name $Username = Read-Host "Please enter your username to login to CORE" } if(-NOT $SSOPass){ $SSOPass = $([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($(Read-Host -Prompt "Please enter your SSO Password to login to CORE" -AsSecureString)))) } #Build the JSON for the request $json = @" { "auth": { "RAX-AUTH:domain": { "name": "Rackspace" }, "passwordCredentials": { "username": "$username", "password": "$SSOPass" } } } "@ #Getting the site $uri = "https://identity-internal.api.rackspacecloud.com/v2.0/tokens" #Send request to Identity and ask for the token $Response = $(Invoke-RestMethod -Uri $uri -Body "$json" -ContentType "application/json" -Method "Post").access.token.id #Enable the error action again $ErrorActionPreference = $OldEAP #If we have not had a response from the invoke-restmethod then creds are probably wrong, so inform the user IF (-NOT $Response) { $count ++ If ($count -eq 3) { #After 3 tries break, else we will be here all day $CoreFailed = $true break } write-host "CORE Username or password incorrect, please retype credentials" -ForegroundColor Red $Token = $null } } if(-not $CoreFailed){ #Save our username [Environment]::SetEnvironmentVariable("CoreUser", $Username, "User") #Save our token to our variables Save-Token -Token $Response Return $Response }else{ write-host "CORE credentials failed 3 times. Giving up!" -ForegroundColor Red break } } Function Save-Token { Param( [Parameter(Mandatory=$true)]$Token ) #Save the token and username to our Environment Variables [Environment]::SetEnvironmentVariable("Token", $Token) $Global:Token = $Token } Function Validate-TokenRequest { Param( [Parameter(Mandatory=$true)]$Token ) #The -erroraction param doesnt work on Invoke-RestMethod so we have to temporarily turn it off for the whole script $OldEAP = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' $URL = "https://identity-internal.api.rackspacecloud.com/v2.0/tokens/$Token" $Headers = @{ "X-Auth-Token" = $Token } #I have seen a weird error where this command will return "The underlying connection was closed: An unexpected error occurred on a send." #As such if it comes back as blank we will try and webclient, which seems to work more reliably try { $tokenValid = $(Invoke-WebRequest -Uri $URL -Method Head -Headers $Headers).StatusCode -eq 200 } catch { Write-Verbose "Error on Invoke-WebRequest -Uri $URL -Method HEAD" Write-Verbose "$_" } IF (200 -eq $tokenValid) { $tokenValid = $true } else { $tokenValid = $false } IF (200 -ne $tokenValid) { #try downloading via the web client $wc = New-Object System.Net.WebClient #or $wc = [system.net.webclient]::new() #or $wc = [system.net.webclient]::create($url) $wc.Headers.Add("X-Auth-Token", $Token) try { $tokenValid = ($wc.DownloadString($url)) | ConvertFrom-Json #or $tokenValid = $wc.GetRespnse() } catch { Write-Verbose "Error on ($wc.DownloadString($url)) | ConvertFrom-Json" Write-Verbose "$_" } } #Enable the error action again $ErrorActionPreference = $OldEAP Return $tokenValid } Function Validate-Token{ Param( [Parameter()][switch]$ReturnBooleen ) #Test the $Global:Token Variable $Token = $null $Token = $Global:Token if ($Global:Token -eq $null){ $Valid = $false write-host "Global:Token is null" -ForegroundColor Red Write-Verbose "Global:Token is null" }else{ $Valid = Validate-TokenRequest -Token $Token write-host "Global:Token is $Valid after testing in CORE" -ForegroundColor Green Write-Verbose "Global:Token is $Valid after testing in CORE" } if($Valid){ Save-Token -Token $Token write-host "Saving existing Global:Token" -ForegroundColor Green Write-Verbose "Saving existing Global:Token" if($ReturnBooleen){ return $true $NoMoreoutput = $true } }else{ #Test the Environment Variable $Token = $null $Token = [Environment]::GetEnvironmentVariable("Token") write-host "ENviroment token = $Token" -ForegroundColor Yellow Write-Verbose "ENviroment token = $Token" if ($Token -eq $null){ $Valid = $false Write-Host "Enviroment token is null" -ForegroundColor Red Write-Verbose "Enviroment token is null" }else{ $Valid = Validate-TokenRequest -Token $Token Write-Host "Enviroment token is $Valid after testing in CORE" -ForegroundColor Green Write-Verbose "Enviroment token is $Valid after testing in CORE" } } if(-NOT $NoMoreoutput){ if($Valid){ Save-Token -Token $Token write-host "Saving existing enviroment token" -ForegroundColor Green Write-Verbose "Saving existing enviroment token" if($ReturnBooleen){ return $true } }else{ if($ReturnBooleen){ return $false }else{ $Token = Get-Token write-host "Getting new token $Token" -ForegroundColor Yellow Write-Verbose "Getting new token $Token" } } } } Function Get-CoreQuery{ Param( [Parameter(Mandatory=$true)]$JSON ) #Search each line of the JSON, if load_arg is blank or NULL then cancel ForEach ($line in $JSON) { IF (($line -like "*load_arg*`"`"*") -OR ($line -like "*load_arg*`"NULL`"*")) { write-host "Blank or null value detected in line $line in CTKCore module. Cancelling script" -ForegroundColor Red exit } } #Make sure our token is valid Validate-Token #Submit the CORE query Invoke-RestMethod -Uri "https://ws.core.rackspace.com/ctkapi/query" -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body $JSON -TimeoutSec 180 } <# .SYNOPSIS Pulls in your CORE admin token from your Firefox cookie database .DESCRIPTION Copies your Firefox cookie database to tmp and then uses Sqllite to extract The function will download Sqllite if not present on the server Requires Powershell 3 .EXAMPLE Get-TokenFromFirefox .OUTPUT Writes to screen on status .NOTES Written by Martin Howlett #> Function Get-TokenFromFirefox { #################### Helper functions - BEGIN #################### #region #function to extract zip function Expand-ZIPFile($file, $destination) { $shell = new-object -com shell.application $zip = $shell.NameSpace($file) foreach($item in $zip.items()) { $shell.Namespace($destination).copyhere($item) } } #endregion ##################### Helper functions - END ##################### #you can change this path, it is where we store sqlite so we dont have to download it every execution $SQLiteLocation = "C:\Temp\SQLite" #Check if we have the SQLite CLI downloaded, if not go get it and extract it if (-NOT (Test-Path "$SQLiteLocation")) { $null = mkdir C:\Temp\SQLite } #if we dont have it downloaded, get sqlite3.exe if(-not (Test-Path $SQLiteLocation\sqlite3.exe)) { # download Invoke-WebRequest "http://www.sqlite.org/2015/sqlite-shell-win32-x86-3090200.zip" -OutFile C:\Temp\SQLite\SQlite3.zip # extract Expand-ZIPFile $SQLiteLocation\SQlite3.zip $SQLiteLocation if(-not (Test-Path $SQLiteLocation\sqlite3.exe)) { Throw "Failed to download SqlLite3. Copy to C:\temp\sqlite and rerun" } } # Daniel: Check if active Chrome cookie so try and get Token from it $ChromeCookieDB = "C:\Users\$env:USERNAME\AppData\Local\Google\Chrome\User Data\Default\Cookies" if ((Get-ChildItem $ChromeCookieDB).LastWriteTime -gt (get-date).AddHours(-5)) { # was there a cookie write in the last 5 hours? # there is a fresh Chrome Cookie, let's go Write-Host "Trying Chrome Cookie" # Work around for cookie file being locked, just create a copy and work on that try { [IO.File]::OpenWrite($ChromeCookieDB).close() } catch { Write-Warning "Chrome Cookie file is locked, making a copy" $CookieCopy="$ENV:tmp\ChromeCookies" Copy-Item $ChromeCookieDB $CookieCopy -Force $ChromeCookieDB = $CookieCopy } # create the batch file with everything on it! $null = New-Item -ItemType File -Path "$ENV:tmp\sqllite2.bat" -Value ` "@echo .q | $SQLiteLocation\sqlite3.exe -cmd `"SELECT hex(encrypted_value) FROM cookies WHERE name='rackspace_admin_session';`" `"$ChromeCookieDB`" " -Force $crumb = & "$ENV:tmp\sqllite2.bat" if ($crumb) { # Convert the Hex String I get into its byte array, amazing oneliner I got here: # http://www.beefycode.com/post/Convert-FromHex-PowerShell-Filter.aspx $cookieAsEncryptedBytes = $crumb -replace '^0x', '' -split "(?<=\G\w{2})(?=\w{2})" | %{ [Convert]::ToByte( $_, 16 ) } # unencrypt Chrome's cookie Add-Type -AssemblyName System.Security $cookieAsBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($cookieAsEncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) $cookie = [System.Text.Encoding]::ASCII.GetString($cookieAsBytes) $Global:Token = $null $Global:Token = $cookie if(Get-Command Validate-Token -ErrorAction SilentlyContinue) { $TokenValid = Validate-Token -ReturnBooleen if($TokenValid) { write-host "Token from Chrome successful" return } } } Write-Host "Chrome Cookie didn't work out, let's try Firefox then" } # get firefox profile # Daniel change this to only get directories (mine was failing because there was a cookies file there! # $Profiles = Get-ChildItem "$env:APPDATA\Mozilla\Firefox\Profiles" | ?{ $_.PSIsContainer } # Daniel - change again to get just 1, hopefully the default $Profiles = Get-ChildItem "$env:APPDATA\Mozilla\Firefox\Profiles" | Where-Object Name -like "*default*" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 $FirefoxProfile = $Profiles.FullName # Work around for cookie file being locked, just create a copy and work on that $FFCookieDB = "$FirefoxProfile\cookies.sqlite" try { [IO.File]::OpenWrite($FFCookieDB).close() } catch { Write-Verbose "Firefox Cookie file is locked, making a copy" $CookieCopy="$ENV:tmp\FFCookies" Copy-Item $FFCookieDB $CookieCopy -Force $FFCookieDB = $CookieCopy } # create the batch file with everything on it! $null = New-Item -ItemType File -Path "$ENV:tmp\sqllite.bat" -Value ` "@echo .q | $SQLiteLocation\sqlite3.exe -cmd `"select value from moz_cookies where name = 'rackspace_admin_session';`" `"$FFCookieDB`" " -Force $crumb = & "$ENV:tmp\sqllite.bat" $Global:Token = $null $Global:Token = $crumb if(Get-Command Validate-Token -ErrorAction SilentlyContinue) { $TokenValid = Validate-Token -ReturnBooleen if(-not $TokenValid) { #refresh our core cookie by loading a Core page to get a new cookie write-host "Core token is not valid. Loading Core page to refresh your cookie and trying again" & "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" https://core.rackspace.com/account/1103359 pause $CoreCookie = & "$ENV:tmp\sqllite.bat" $TokenFound = $CORECookie -match '[a-z0-9]{32}' if($TokenFound) { $Global:Token = $TokenValid = $null $Global:Token = $CoreCookie $TokenValid = Validate-Token -ReturnBooleen if(-not $TokenValid) { Throw "Failed to get valid Core cookie. Are you logged into Core in Forefox?" } else { write-host "Core token is valid" -ForegroundColor Cyan } } else { Throw "Failed to parse Core cookie from Firefox" } } else { write-host "Core token is valid" -ForegroundColor Cyan } } else { write-host "WARNING: Core module is not loaded so did not validate token" -ForegroundColor Yellow } write-host "Token from Firefox successful" } ################################## ## Device info functions ################################## #Functions used to get device info from account, ticket or device #Need to be rewritten as they are a bit of a mess Function Get-ComputerAttributes { Param( [Parameter ()][switch]$RackPW, [Parameter ()][switch]$DRACPW ) #Create array of all params we want to get from devices in CORE $ComputerAttributes = @( "account.name", "number", "name", "primary_ip", "primary_nat_ip", "is_windows", "os_name", "nickname", "status.active", "status.name", "os_type", "platform_name", "platform_type", "is_online", "datacenter.name", "datacenter.symbol", "ports.display_name", "vcc", "dracnet_ip", "account", "attached_devices", "is_windows", "offline_date", "attached_firewalls", "failover_device", "date_online", "account_domain_info.domain_name", "account_domain_info.add_userid_to_domain", "account_domain_info.is_workgroup", "is_cluster", "aggexnet_vlans", "contract", "platform_model", "customer_aggexnet_vlans", "is_ice_complete", "opt_out" ) #add skus $Skus = @{ "attribute" = "parts"; "subattributes" = @{ "Description" = "ctk_product.description"; "sku" = "sku_number"; "label" = "display_label"; "modified"= "modified"; "part_attributes"= @{ "attribute" = "partributes"; "subattributes" = @( "value", "sku_attribute.description" ) } } } $ComputerAttributes = $ComputerAttributes + $Skus #adds password info if required If ($RackPW) { $ComputerAttributes += "root_password" $ComputerAttributes += "rack_password" $ComputerAttributes += "notes" } If ($DRACPW) { $ComputerAttributes += "drac_passwords.password" $ComputerAttributes += "drac_passwords.username" } Return $ComputerAttributes } Function Get-COREDevicePSobject { Param( [Parameter ()][switch]$RackPW, [Parameter ()][switch]$DRACPW, [Parameter()]$Computers, [Parameter()][switch]$NoDRAC, [Parameter()][switch]$GetIP, #get ips from IP commander (adds 3 seconds) [Parameter ()]$IPs ) IF ($GetIP) { write-host "Getting IP information from IP commander - this will take around $($Computers.count * 3) seconds" } $DeviceList = @() ForEach ($Computer in $Computers) { #Ignore inactive computers IF ($Computer."status.name" -like "*no longer active*") { continue } #Get DRAC IP IF ($NoDRAC) { $DRAC_IP = $null } Else { IF ($Computer.dracnet_ip.load_value) { $DRAC_IP = Get-CoreIP -ID $($Computer.dracnet_ip.load_value) } else { $DRAC_IP = $null } } #Get IPs from IP commander IF ($GetIP) { $IPs = Get-IPsfromIPCommander -Device $Computer.number } [string]$name = $Computer.name if($Computer.aggexnet_vlans) { $aggexnet_vlans = ($Computer.aggexnet_vlans).Name.Trim() -Join "," }else{ $aggexnet_vlans = $null } if($Computer.customer_aggexnet_vlans) { $customer_aggexnet_vlans = ($Computer.customer_aggexnet_vlans).Name.Trim() -Join "," }else{ $customer_aggexnet_vlans = $null } #Parse our skus $Skus = $null $Skus = $Computer.parts.subattributes $SKUarr = @() ForEach ($Sku in $SKUs) { $SKUarr += New-Object PSObject -Property @{ SKU = $Sku.Sku Value = $Sku.part_attributes.subattributes.value ValueLabel = $Sku.part_attributes.subattributes."sku_attribute.description" Label = $sku.label Description = $sku.Description Server = $Device } } $DevicePSO = [PSCustomObject]@{ Name = $Computer.name; Number = $Computer.number; IP = $Computer.primary_ip; PrivateIP=$Computer.primary_nat_ip; BackupIP=""; OSname=$Computer.os_name; NickName=$Computer.nickname; OStype=$Computer.os_type; PlatName=$Computer.platform_name; PlatType=$Computer.platform_type; PlatModel=$Computer.platform_model; Status=$Computer."status.name"; Contract=$Computer.contract.load_value; Datacenter=$Computer."datacenter.name"; DatacenterSymbol =$Computer."datacenter.symbol"; Ports=$Computer.ports.display_name; vCenter=$Computer.vcc; Offline=$Computer.offline_date; attached_firewalls = $Computer.attached_firewalls; FailoverDev = $Computer.failover_device.load_value; "Account Name" = $Computer."account.name"; dracnet_ip = $DRAC_IP; Backup_IP = ""; attached_devices = $Computer.attached_devices; Account = $Computer.account.load_value; ServerName = ($name.Split(".")[0]); is_windows = $Computer.is_windows; offline_date = $Computer.offline_date; date_online = $Computer.date_online; dateonline = $Computer.date_online; Domain = $Computer."account_domain_info.domain_name"; IntensiveAddDomain = $Computer."account_domain_info.add_userid_to_domain"; is_workgroup = $Computer."account_domain_info.is_workgroup"; aggexnet_vlans = $aggexnet_vlans customer_aggexnet_vlans = $customer_aggexnet_vlans is_cluster = $Computer."is_cluster"; is_ice_complete = $Computer."is_ice_complete"; ServiceNetGW = $IPs.ServiceNetGW ServiceNetIP = $IPs.ServiceNetIP ServiceNetNM = $IPs.ServiceNetNM PublicGW = $IPs.PublicGW PublicIP = $IPs.PublicIP PublicNM = $IPs.PublicNM PrivateGW = $IPs.PrivateGW PrivateNM = $IPs.PrivateNM PwdRotat_opt_out = $Computer.opt_out Skus = $SKUarr } If ($RackPW) { Add-Member -InputObject $DevicePSO -NotePropertyName RackPass -NotePropertyValue $Computer.rack_password; Add-Member -InputObject $DevicePSO -NotePropertyName AdminPass -NotePropertyValue $Computer.root_password; Add-Member -InputObject $DevicePSO -NotePropertyName PassNotes -NotePropertyValue $Computer.notes; } If ($DRACPW) { Add-Member -InputObject $DevicePSO -NotePropertyName DRACPass -NotePropertyValue $Computer."DRAC_passwords.password"; Add-Member -InputObject $DevicePSO -NotePropertyName DRACUser -NotePropertyValue $Computer."DRAC_passwords.username"; } $DeviceList += $DevicePSO } Return $DeviceList } function Get-CoreDevicesAccount { Param( [Parameter(mandatory=$true)]$Account, [Parameter()]$limit = 100, [Parameter()][switch]$NoDRAC, [Parameter()][switch]$RackPW, [Parameter()][switch]$DRACPW, #get DRAC password. [Parameter()][switch]$GetIP #get ips from IP commander (adds 3 seconds) ) $Params = $null $Params = @{ Account = $Account; Limit = $Limit; } #get our JSON If ($RackPW) { $Params.Add("RackPW", $true) } If ($DRACPW) { $Params.Add("DRACPW", $true) } $obj = $null $obj = Get-COREDeviceJSON @params if(-not $obj) { throw "unable to get JSON object" } $offset = 0 $Computers = @() do { #write to screen what we are doing write-host "Getting devices $offset to $($limit + $offset) from CORE..." #Update our offset in the JSON $obj.load_arg.offset = $offset #Convert it again $JSON = $obj | ConvertTo-Json -Depth 10 #Query CORE $QueryResult = "" $QueryResult = (Get-CoreQuery -JSON $JSON).Result #Add result to array $Computers += $QueryResult $oldoffset = $offset $offset = $offset + $limit #debug #write-host "limit $limit" -ForegroundColor Yellow #write-host "QueryResult.created $(($QueryResult.created).count)" -ForegroundColor Yellow } Until (($QueryResult.created).count -lt $limit) $Params = $null $Params = @{ Computers = $Computers; } #Add additional params from the script If ($NoDRAC) { $Params.Add("NoDRAC", $true) } If ($DRACPW) { $Params.Add("DRACPW", $true) } If ($RackPW) { $Params.Add("RackPW", $true) } If ($GetIP) { $Params.Add("GetIP", $true) } $ReturnedComputers = Get-COREDevicePSobject @params Return $ReturnedComputers } Function Get-COREDeviceJSON { Param( [Parameter()]$Device, [Parameter()]$Ticket, [Parameter()]$Account, [Parameter()]$Limit, [Parameter()][switch]$RackPW, [Parameter()][switch]$DRACPW ) $Params = @{} #get our JSON If ($RackPW) { $Params.Add("RackPW", $true) } If ($DRACPW) { $Params.Add("DRACPW", $true) } $ComputerAttributes = Get-ComputerAttributes @params If ($Ticket) { #Create JSON object for ticket $obj = @( @{ "load_arg" = $ticket; "class" = "Ticket.Ticket"; "attributes" = @( "id", @{ "id" = "number" "attribute" = "computers" "subattributes" = $ComputerAttributes } ) } ) #Convert it $JSON = $obj | ConvertTo-Json -Depth 10 } If ($Account) { #Create JSON object $json = @( @{ "class" = "Computer.Computer"; "attributes" = $ComputerAttributes "load_arg" = @{ "class" = "Computer.ComputerWhere" "values" = @("account","=","$Account") "offset" = 0 "limit" = $limit } } ) } If ($Device) { #Create JSON object $obj = @( @{ "class" = "Computer.Computer"; "load_arg" = $Device "attributes" = $ComputerAttributes } ) #Convert it $JSON = $obj | ConvertTo-Json -Depth 10 } return $JSON } function Get-CoreDevicesTicket { Param( [Parameter(mandatory=$true)]$Ticket, [Parameter()][switch]$NoDRAC, [Parameter()][switch]$RackPW, [Parameter()][switch]$GetIP, #get ips from IP commander (adds 3 seconds) [Parameter()][switch]$DRACPW #get DRAC password. ) $Params = $null $Params = @{ Ticket = $Ticket; } #get our JSON If ($RackPW){ $Params.Add("RackPW", $true) } If ($DRACPW){ $Params.Add("DRACPW", $true) } $JSON = Get-COREDeviceJSON @Params #Query CORE $QueryResult = (Get-CoreQuery -JSON $JSON) #As we query subattributes when going via ticket, we need to expand the properties out $Computers = @() $ComputerList = $QueryResult.result.computers.subattributes | gm -MemberType NoteProperty | Select-Object Name ForEach ($Name in $ComputerList) { $Computers += $QueryResult.result.computers.subattributes."$($Name.Name)" } #for some reason i never worked out, when returning the SKUs as a subattribute on a ticket it calls the property the json like below #so here we remove it from the psobject and add a new one called parts so it matches the other queries $SkuProperty = "{'subattributes': {'sku': 'sku_number', 'modified': 'modified', 'Description': 'ctk_product.description', 'part_attributes': {'subattributes': ['value', 'sku_attribute.description'], 'attribute': 'partributes'}, 'label': 'display_label'}, 'attribute': 'parts'}" foreach($computer in $computers) { $skus = $null $skus = $computer."$SkuProperty" Add-Member -InputObject $Computer -MemberType NoteProperty -Name parts -Value $skus -force $computer.PSObject.Properties.Remove("$SkuProperty") } $Params = @{ Computers = $Computers; } #Add additional params from the script If ($NoDRAC) { $Params.Add("NoDRAC", $true) } If ($RackPW) { $Params.Add("RackPW", $true) } If ($GetIP) { $Params.Add("GetIP", $true) } If ($DRACPW){ $Params.Add("DRACPW", $true) } $ReturnedComputers = Get-COREDevicePSobject @params Return $ReturnedComputers } function Get-IPsfromIPCommander { Param( [Parameter (Mandatory=$true)]$Device ) #Skip SSL errors [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} #Create the connection $WebService = New-WebServiceProxy -Uri "https://ipadmin.dcx.rackspace.com/ipadmin/ip_info/wsdl" -UseDefaultCredential #get IPs by device $IPs = $WebService.IpsByDevice("$device") #create psobject from results and return it $Output = New-Object PSObject #filter secondary IPs to get servicenet $servicenet = $IPs.secondary_ips | Where-Object {($_.descriptor -eq "servicesnet") -OR ($_.descriptor -eq "Managed Backup")} #servicenet Add-Member -InputObject $Output -MemberType NoteProperty -Name ServiceNetGW -Value $servicenet.gateway_ip Add-Member -InputObject $Output -MemberType NoteProperty -Name ServiceNetIP -Value $servicenet.ip_address Add-Member -InputObject $Output -MemberType NoteProperty -Name ServiceNetNM -Value $servicenet.netmask #public Add-Member -InputObject $Output -MemberType NoteProperty -Name PublicGW -Value $IPs.primary_ip[0].gateway_ip Add-Member -InputObject $Output -MemberType NoteProperty -Name PublicIP -Value $IPs.primary_ip[0].ip_address Add-Member -InputObject $Output -MemberType NoteProperty -Name PublicNM -Value $IPs.primary_ip[0].netmask #private Add-Member -InputObject $Output -MemberType NoteProperty -Name PrivateGW -Value $IPs.primary_ip[0].inside_local_gateway_ip Add-Member -InputObject $Output -MemberType NoteProperty -Name PrivateIP -Value $IPs.primary_ip[0].inside_local Add-Member -InputObject $Output -MemberType NoteProperty -Name PrivateNM -Value $IPs.primary_ip[0].inside_local_netmask return $output } function Get-CoreDevices{ Param( [Parameter ()][string] $Account, [Parameter ()][switch] $NoOutput, [Parameter ()][string] $Ticket, [Parameter ()][switch] $NoDRAC, [Parameter ()][switch] $RackPW, [Parameter()][switch]$DRACPW #get DRAC password. ) IF ($Account) { $Params = @{ Account = $Account } If ($NoDRAC){$Params.Add("NoDRAC", $true)} If ($RackPW){$Params.Add("RackPW", $true)} If ($RackPW){$Params.Add("DRACPW", $true)} Get-CoreDevicesAccount @Params } ELSEIF ($Ticket) { $Params = @{ Ticket = $Ticket } If ($NoDRAC){$Params.Add("NoDRAC", $true)} If ($RackPW){$Params.Add("RackPW", $true)} If ($RackPW){$Params.Add("DRACPW", $true)} Get-CoreDevicesTicket @Params } ELSE { break } <# .SYNOPSIS RETIRED -> Use Get-CoreDevicesAccount and Get-CoreDevicesTicket .DESCRIPTION RETIRED -> Use Get-CoreDevicesAccount and Get-CoreDevicesTicket .EXAMPLE RETIRED -> Use Get-CoreDevicesAccount and Get-CoreDevicesTicket .EXAMPLE RETIRED -> Use Get-CoreDevicesAccount and Get-CoreDevicesTicket #> } function Get-CoreDevice { Param( [Parameter (Position=1, Mandatory=$true)] [string] $Device, [Parameter()][switch]$NoDRAC, [Parameter()][switch]$RackPW, [Parameter()][switch]$GetIP, #get ips from IP commander (adds 3 seconds) [Parameter()][switch]$DRACPW #get DRAC password. ) #get our JSON $Params = $null $Params = @{ device = $device; } #get our JSON If ($RackPW){ $Params.Add("RackPW", $true) } If ($DRACPW){ $Params.Add("DRACPW", $true) } $JSON = Get-COREDeviceJSON @Params #Query CORE $Computers = (Get-CoreQuery -JSON $JSON).Result $Params = @{ Computers = $Computers; } #Add additional params from the script If ($NoDRAC) { $Params.Add("NoDRAC", $true) } If ($RackPW) { $Params.Add("RackPW", $true) } If ($GetIP) { $Params.Add("GetIP", $true) } If ($DRACPW) { $Params.Add("DRACPW", $true) } $ReturnedComputers = Get-COREDevicePSobject @params Return $ReturnedComputers <# .SYNOPSIS The Get-CoreDevice cmdlet retrieve details for specified device. The results will be output in format-list. The variable $Device will also be available at cmd line for use in scripts. .DESCRIPTION See synopsis. .EXAMPLE PS C:\Users\Administrator> Get-CoreDevice -Device 24645 This example shows how to retrieve device details #> } Function Get-CoreTicketDevices { Param ( [Parameter(Mandatory=$true)]$ticket = (Read-Host("Enter Ticket Number")) ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreTicketDevices function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---LEGACY FUNCTION IN USE!" -noconsole $DeviceList = Get-CoreDevices -Ticket $ticket return $DeviceList #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreTicketDevices function" -noconsole <# .SYNOPSIS Get-CoreTicketDevices is present to support legacy scripts Please use Get-CoreDevices -Ticket $ticket instead .DESCRIPTION See synopsis. .EXAMPLE Use Get-CoreDevices -Ticket $ticket instead #> } Function Get-CoreTicket { Param ( [Parameter(Mandatory=$true)]$ticket = (Read-Host("Enter Ticket Number")) ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreTicket function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---LEGACY FUNCTION IN USE!" -noconsole $DeviceList = Get-CoreDevices -Ticket $ticket return $DeviceList #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreTicket function" -noconsole <# .SYNOPSIS Get-CoreTicket is present to support legacy scripts Please use Get-CoreDevices -Ticket $ticket instead .DESCRIPTION See synopsis. .EXAMPLE Use Get-CoreDevices -Ticket $ticket instead #> } ################################## ## Ticket functions ################################## #Functions related to tickets Function Get-CoreTicketContents { Param ( [Parameter(Mandatory=$true)]$ticket = (Read-Host("Enter Ticket Number")) ) $obj = @( @{ "class" = "Ticket.Ticket"; "attributes" = @( "id", "account.name", "account.number", "subject", "status", "messages", "is_scheduled_service", "assignee.name", "queue", "difficulty", "created" ) } ) $obj[0]['load_arg'] = $ticket $JSON = $obj | ConvertTo-Json $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result } Function Get-CoreTicketMessage { Param ( [Parameter(Mandatory=$true)]$ID ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreTicketMessage function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "ID = $ID" -noconsole $obj = @( @{ "class" = "Ticket.Message"; "attributes" = @( "text", "is_visible", "is_customer", "date", "private", "contact", "source", "id", "contact" ) } ) $obj[0]['load_arg'] = $ID $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreTicketMessage function" -noconsole } Function Set-CoreTicketAssignee { Param ( [Parameter(Mandatory=$true)]$ID, [Parameter(Mandatory=$true)]$Ticket ) Invoke-RestMethod -Uri "https://ws.core.rackspace.com/ctkapi/attribute/Ticket/$ticket/assignee" -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body "$ID" | Out-Null } Function Set-CoreTicketQueue { Param ( [Parameter(Mandatory=$true)]$Ticket, [Parameter(Mandatory=$true)]$queue ) Invoke-RestMethod -Uri "https://ws.core.rackspace.com/ctkapi/attribute/Ticket/$ticket/queue" -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body "$queue" | Out-Null #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Set-CoreTicketQueue function" -noconsole } Function Get-CoreTicketDetails { Param ( [Parameter(Mandatory=$true)]$Ticket ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreTicketDetails function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "Ticket = $Ticket" -noconsole $obj = @( @{ "class" = "Ticket.Ticket"; "attributes" = @( "account", "subject", "status", "messages", "is_scheduled_service", "superticket" ) } ) $obj[0]['load_arg'] = $Ticket $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreTicketDetails function" -noconsole } Function Set-CoreTicketDifficulty { Param ( [Parameter(Mandatory=$true)]$Difficulty, [Parameter(Mandatory=$true)]$Ticket ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Set-CoreTicketDifficulty function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "Difficulty = $Difficulty" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "Ticket = $Ticket" -noconsole Invoke-RestMethod -Uri "https://ws.core.rackspace.com/ctkapi/attribute/Ticket/$ticket/difficulty" -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body "$Difficulty" | Out-Null #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Set-CoreTicketDifficulty function" -noconsole } <# .SYNOPSIS Creates a subticket with a supplied master ticket number .DESCRIPTION Creates a subticket with a supplied master ticket number, has various params for the subticket as below returns ticket number, account and status #Params as per addMessage method as documented in https://ws.core.rackspace.com/ctkapi/browse/Ticket/Ticket .PARAMETER MasterTicket Mandatory The number of the master ticket .PARAMETER subject Mandatory Ticket subject .PARAMETER FirstComment Mandatory First ticket update If you have BBCode dont forget to specity has_bbcode = 1 .PARAMETER Queue Mandatory See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Queue #683 INTL Windows .PARAMETER source See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Source 4 is Automation 3 is Racker (if private ticket CORE marks it as public then private) .PARAMETER subcategory The category of the ticket See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Subcategory 13376is "Windows - Other" Will be inherited from master ticket if not supplied .PARAMETER severity severity of the ticket https://ws.core.rackspace.com/ctkapi/browse/Ticket/Severity 1 Standard 2 Urgent 3 Emergency .PARAMETER computer_list Array fo deviceids to attach to ticket If blank will inherit master ticket .PARAMETER has_bbcode Set 1 for yes Set 0 for no .PARAMETER is_private_ticket Set 1 for private ticket Set 0 for public ticket If you set a private ticket but have a ticket source of Racker or Customer, your first ticket update will be public and made private .Notes Written by Martin Howlett - martin.howlett@rackspace.co.uk Used in IAW Desktop Scripts > ImpNewAccountTicketGenerator.ps1 #> Function New-CoreSubTicket{ Param( #Mandatory in Powershell as no default values [Parameter(Mandatory=$true)][string]$MasterTicket, [Parameter(Mandatory=$true)][string]$Subject, [Parameter(Mandatory=$true)][string]$FirstComment, #Mandatory (ticket wont create if these not supplied but has default) [Parameter()][Int]$Queue=26, [Parameter()][Int]$priority=1, [Parameter()][Int]$severity=1, [Parameter()][array]$computers, #optional ticket params [Parameter()][Int]$is_private_message=1, [Parameter()][Int]$is_private_ticket=1, [Parameter()][Int]$send_message_text=0, [Parameter()][Int]$has_bbcode=0, [Parameter()][Int]$status, [Parameter()][Int]$assignee ) #generate our default param list core needs $KeywordArgs = @{ subject = $Subject; comment = $FirstComment; queue = $Queue; computers = $computers; severity = $severity; priority = $priority; is_private_ticket = $is_private_ticket; is_private_message = $is_private_message; send_message_text= $send_message_text; has_bbcode=$has_bbcode } #add any additional user supplied ones if($status) { $KeywordArgs.Add("status",$status) } if($assignee) { $KeywordArgs.Add("assignee","$assignee") } $obj = @{ "class"= "Ticket.Ticket"; "load_arg"= "$MasterTicket"; "method"= "addSubTicket"; "args"= @(); "keyword_args" = $KeywordArgs "result_map" = @{ "status" = "status.name" "account"= "account.number" "number"= "number" } } $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $result = Get-CoreQuery -JSON $JSON return $result.result } Function Get-CoreTicketFullContents{ <# .SYNOPSIS Gets the details of a ticket and all ticket updates .DESCRIPTION Returns ticket details like assigning and queue name as well as all ticket updates, including text .PARAMETER ticket Ticket number .EXAMPLE Get-CoreTicketFullContents -ticket 123456-564978 .NOTES Written by Martin Howlett #> Param ( [Parameter(Mandatory=$true)][string]$ticket ) $obj = @( @{ "class" = "Ticket.Ticket"; "attributes" = @( "id", "assignee.name", "subject", "status.name", "is_scheduled_service", "queue.name", "difficulty", "created", @{ "id"="id" "attribute"="messages" "subattributes" = @("text","is_visible","is_customer","date","private","contact","source","contact") } ) } ) $obj[0]['load_arg'] = $ticket $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $ticketresult = Get-CoreQuery -JSON $JSON if(-not $ticketresult){ return "not found" } else { $MessageIDs = ($ticketresult.result.messages.subattributes | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"} ).Name $Messages = @() ForEach($MEssageID in $MessageIDs){ $Messages += $ticketresult.result.messages.subattributes."$MessageID" } $ticketresult.result.messages = $Messages Return $ticketresult.result } } Function Set-CoreTicketAssigneeByLogin { Param ( [Parameter(Mandatory=$true)]$Ticket ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Set-CoreTicketAssigneeByLogin function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "Ticket = $Ticket" -noconsole $JSON=@" [ { "class": "Ticket.Ticket", "load_arg": "$Ticket", "set_attribute": { "assignee": { "class": "Contact.Contact", "load_arg": { "class": "Contact.ContactWhere", "values": [ [ "employee_userid", "=", "$([Environment]::UserName)" ] ] } } } } ] "@ Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON #Return $result #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Set-CoreTicketAssigneeByLogin function" -noconsole } Function Add-COREDeviceToTicket { Param( [Parameter(Mandatory=$true)]$Device, [Parameter(Mandatory=$true)]$ticket ) $obj = @( @{ "class" = "Ticket.Ticket"; "method" = "addComputer"; "args" = @( "$Device" ) } ) $obj[0]['load_arg'] = $ticket $JSON = $obj | ConvertTo-Json -Depth 10 $response = Invoke-RestMethod -Uri "https://ws.core.rackspace.com/ctkapi/query" -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body $JSON Return $response.Success } <# .SYNOPSIS Updates a Core ticket .DESCRIPTION Adds a message to an existing Core ticket with various options .PARAMETER Ticket Mandatory Ticket number to update .PARAMETER Message Mandatory The text of the update .PARAMETER source Mandatory, set to 4 by default See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Source 4 is Automation 3 is Racker (if private ticket CORE marks it as public then private - no idea why) .PARAMETER has_bbcode Set 1 for bbcode Set 0 for no bbcode Will be force disabled if comment is split due to exceeding the comment size limit. .PARAMETER source_contact Who created the message .PARAMETER send_message_text Boolean determining if text of message should be included in e-mail. .PARAMETER send_email Send email to contact .PARAMETER VerboseOutput Return true/false if successful .EXAMPLE Minimum amount of info Update-CoreTicket -Ticket 170111-12345 -message "This is a test message" .OUTPUT None by default, true/false with VerboseOutput .NOTES Written by Martin Howlett #> Function Update-CoreTicket { Param( #Mandatory in Powershell as no default values [Parameter(Mandatory=$true)][string]$Ticket, [Parameter(Mandatory=$true)][string]$Message, [Parameter()][Int]$Source=4, #Optional [Parameter()][Int]$source_contact=0, [Parameter()][Int]$send_message_text=0, [Parameter()][Int]$send_email=0, [Parameter()][Int]$has_bbcode=0, [Parameter()][Int]$Private, [Parameter()][Switch]$VerboseOutput ) $KeyWordArgs = @{ text = $Message source = $Source } #Add optional params if($source_contact){$KeyWordArgs.Add("source_contact",$source_contact)} if($send_message_text){$KeyWordArgs.Add("send_message_text",$send_message_text)} if($send_email){$KeyWordArgs.Add("send_email",$send_email)} if($has_bbcode){$KeyWordArgs.Add("has_bbcode",$has_bbcode)} if($private){$KeyWordArgs.Add("private",$private)} #create JSON object $obj = @{ "class"= "Ticket.Ticket"; "load_arg"= "$Ticket"; "method"= "addMessage"; "args"= @(); "keyword_args" = $KeyWordArgs; } #generate json $JSON = $obj | ConvertTo-Json -Depth 10 #query core $result = Get-CoreQuery -JSON $JSON if($VerboseOutput) { return $result.success } } <# .SYNOPSIS Creates a new core ticket .DESCRIPTION Creates a new core ticket .PARAMETER Account Mandatory Account to create new ticket in .PARAMETER subject Mandatory Ticket subject .PARAMETER text Mandatory First ticket update .PARAMETER Queue Mandatory See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Queue #683 INTL Windows .PARAMETER subcategory The catagory of the ticket See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Subcategory 13376, #Windows - Other .PARAMETER source See here: https://ws.core.rackspace.com/ctkapi/browse/Ticket/Source 4 is Automation 3 is Racker (if private ticket CORE marks it as public then private - no idea why) .PARAMETER severity severity of the ticket https://ws.core.rackspace.com/ctkapi/browse/Ticket/Severity 1 Standard 2 Urgent 3 Emergency .PARAMETER computer_list Array fo deviceids to attach to ticket .PARAMETER has_bbcode Set 1 for bbcode Set 1 for no bbcode .PARAMETER is_private_ticket Set 1 for private ticket Set 0 for public ticket If you set a private ticket but have a ticket source of Racker or Customer, your first ticket update will be public and made private .PARAMETER assignee This is the contact ID of the person you want to assign the ticket too If you do not know the contact ID, you can get it using the function Get-CoreContactFromUsername Example: $Contact = Get-CoreContactFromUsername @("$ENV:username") .EXAMPLE Minimum amount of info $TicketNumber = New-CoreTicket -Account 1103359 -subject "This is a test ticket" -text "first ticket update" -Queue 510 .EXAMPLE All info $TicketNumber = New-CoreTicket -Account 1103359 -subject "This is a test ticket" -text "first ticket update" -Queue 510 -subcategory 123 -source 3 -severity 1 -computer_list @("560570","502664") -has_bbcode 1 -is_private_ticket 1 -status 1 -assignee mart6141 .OUTPUT Ticket number .NOTES Written by Martin Howlett #> Function New-CoreTicket { Param( #Mandatory in Powershell as no default values [Parameter(Mandatory=$true)][Int]$Account, [Parameter(Mandatory=$true)][String]$subject, [Parameter(Mandatory=$true)][String]$text, [Parameter(Mandatory=$true)]$Queue, #Mandatory (ticket wont create if these not supplied but has default) [Parameter()][Int]$subcategory=13376, #Windows - Other [Parameter()][Int]$source=4, #Automation [Parameter()][Int]$severity=1, #Standard [Parameter()][array]$computer_list=@(), #Optional [Parameter()][Int]$has_bbcode=0, [Parameter()][Int]$is_private_ticket=0, [Parameter()][Int]$status, [Parameter()][Int]$assignee #optional never used and not tested <# [Parameter()][array]$contact_list=@(), [Parameter()][Int]$source_contact, [Parameter()][Switch]$send_message_text, [Parameter()][Int]$status, [Parameter()][Int]$contact_email_type, [Parameter()][Int]$priority, [Parameter()][Int]$product_suite_name, [Parameter()][Int]$product_name, [Parameter()][Int]$action_name, [Parameter()][Int]$classification, [Parameter()][Int]$message_time, [Parameter()][Int]$required_receipients #> ) $KeyWordArgs = @{ queue = $queue subcategory = $subcategory source = $source severity = $severity subject = $subject text = $text computer_list = $computer_list } #Add optional params if($has_bbcode){$KeyWordArgs.Add("has_bbcode",$has_bbcode)} if($is_private_ticket){$KeyWordArgs.Add("is_private_ticket",$is_private_ticket)} if($status){$KeyWordArgs.Add("status",$status)} if($assignee){$KeyWordArgs.Add("assignee",$assignee)} $obj = @{ "class"= "Account.Account"; "load_arg"= "$Account"; "method"= "addTicket"; "args"= @(); "keyword_args" = $KeyWordArgs; } #generate jsopn $JSON = $obj | ConvertTo-Json -Depth 10 #query core $result = Get-CoreQuery -JSON $JSON #return ticket number return $result.result.load_value } Function Set-CORETicketStatusByName{ Param( [Parameter(Mandatory=$true)]$Ticket, [Parameter(Mandatory=$true)]$Status ) <# #CASE SENSITIVE https://ws.core.rackspace.com/ctkapi/browse/Ticket/Status 1 New New 2 Closed Closed 3 Unsolved Unsolved 4 REQUIRE Feedback Require feedback from customer. Ticket will automatically be set to Unsolved after 2 days. 5 Request Feedback Request feedback from customer. Ticket will be automatically closed after one week. 6 Confirm Solved Customer has two days to mark this ticket as Unsolved before it is marked as Closed. 7 New New 8 Closed Closed 9 Unsolved Unsolved 10 Awaiting Response Awaiting Response 11 Request Feedback Request feedback from customer. Ticket will be automatically closed after one week. 12 Confirm Solved Customer has two days to mark this ticket as Unsolved before it is marked as Closed. #> $obj = @{ "class"= "Ticket.Ticket"; "load_arg"= "$Ticket"; "method"= "setStatusByName"; "args"= @( "$Status" ) } $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON return $result.result <# .SYNOPSIS .DESCRIPTION See synopsis. .Notes Written by Martin Howlett - martin.howlett@rackspace.co.uk #> } Function Get-CoreTicketSearch { Param ( [Parameter(Mandatory=$true)]$DeviceID, [Parameter(Mandatory=$true)]$Subject ) $JSON = @" [ { "class": "Ticket.Ticket", "load_arg": { "class": "Ticket.TicketWhere", "values": [ [ [ "computer", "=", "$DeviceID" ], "&", [ "current_state_date", ">=", "2015-07-01 00:00:00" ], "&", [ "subject", "=", "$subject" ] ] ], "limit": 2000, "offset": 0 }, "attributes": [ "number", "subject", "computers.name", "created" ] } ] "@ Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result } Function Get-CoreTicketByQueueStatus { Param( [Parameter()]$limit = 100, [Parameter(Mandatory=$true)]$QueueName, [Parameter(Mandatory=$true)]$StatusName ) $offset = 0 $limit = 100 do { #write to screen what we are doing write-verbose "getting tickets $offset to $($limit + $offset) from CORE..." $JSON = @" [ { "class": "Ticket.Ticket", "load_arg": { "class": "Ticket.TicketWhere", "values": [ [ "queue_name", "=", "$QueueName" ], "&", [ "status_name", "=", "$StatusName" ] ], "limit": $limit, "offset": $offset }, "attributes": [ "number", "status.name", "assignee.name", "subject", "modified", "status.name", "queue.name" ] } ] "@ #Query CORE $QueryResult = "" $QueryResult = (Get-CoreQuery -JSON $JSON).Result #Add result to array $ResultArray += $QueryResult $oldoffset = $offset $offset = $offset + $limit #debug #write-host "limit $limit" -ForegroundColor Yellow #write-host "QueryResult.created $(($QueryResult.created).count)" -ForegroundColor Yellow }Until (($QueryResult.created).count -lt $limit) return $ResultArray } Function Set-CoreUnassignUser { Param( [Parameter(Mandatory=$true)]$Ticket ) #$URL = "https://core.rackspace.com/py/core/server/server_passwords_secrets.pt?computer_number=660537" #$URL = "https://core.rackspace.com/py/ticket/action.pt?assign_to_me=151113-06216" Validate-Token $URL = "https://core.rackspace.com/py/ticket/action.pt?unassign=$Ticket" $remoteFileLocation = "http://somewhere/on/the/internet" $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession $cookie = New-Object System.Net.Cookie $cookie.Name = "rackspace_admin_session" $cookie.Value = "$Global:Token" $cookie.Domain = "core.rackspace.com" $session.Cookies.Add($cookie); $wm = Invoke-WebRequest $url -WebSession $session } Function Set-CoreTicketStatus{ Param( [Parameter(Mandatory=$true)]$Ticket, [Parameter(Mandatory=$true)]$Status ) Validate-Token $URL = "https://ws.core.rackspace.com/ctkapi/attribute/Ticket/$Ticket/status" $result = Invoke-RestMethod -Uri $URL -Method "Post" -Headers @{"X-Auth-Token"=$Global:Token} -Body "$Status" } Function Get-CoreTicketStatus{ Param( [Parameter(Mandatory=$true)]$Ticket ) Validate-Token $URL = "https://ws.core.rackspace.com/ctkapi/attribute/Ticket/$Ticket/status" $TicketStatus = Invoke-RestMethod -Uri $URL -Method "GET" -Headers @{"X-Auth-Token"=$Global:Token} return $TicketStatus.name } <# .SYNOPSIS Returns a list of ADC tickets for supplied devices .DESCRIPTION Provide array of device numbers to get back a list of ADC tickets with ticket updates Includes tickets with "Windows ADC" and "VM Not accessible to ADC" in there subject Excluding tickets with "SCOM" in there subject .PARAMETER DeviceIDs Mandatory Array of device IDs - can be a single one so long as its an array object .PARAMETER ShowQueryTime Optional switch to write to host the time the CORE query took Only really useful in troubleshooting or proving to a user your script is not slow .PARAMETER Silent Stops output to screen, default is to output to screen .PARAMETER limit Limits the number of devices to get per query. Default value is 100. If there are 300 devices it will run three queries of 100 each and merge the result .EXAMPLE With an array of accounts Get-CoreADCTickets -DeviceIDs @(502664,560570) .EXAMPLE With a single device Get-CoreADCTickets -DeviceIDs @(560570) .OUTPUT Psobject of tickets, ticket updates and device IDs .NOTES Written by Martin Howlett Thanks to Mark Wichall for writing the base function and nailing the JSON! #> Function Get-CoreTicketsADC{ Param ( [Parameter(Mandatory=$true)][Array]$DeviceIDs, [Parameter()][switch]$ShowQueryTime, [Parameter()]$limit = 100, #100 seems to be reliable when core is responding slowly [Parameter()][switch]$Silent ) #Vars $offset = 0 $timetotal = 0 $Computers = @() $DeviceIDsRange = @() $Ticketresult = $null $DeviceCount = $DeviceIDs.count #make sure we have a unique list $DeviceIDs = $DeviceIDs | Sort-Object -Unique #Limit cannot be higher than 250 as CTKCore will error with http 400. If (($Limit -gt 250) -or ($Limit -lt 2)) { $Limit = 100 } If (-Not $Silent) { Write-Host "Total devices to query = $DeviceCount" } do { #Top limit on devices to query in this batch $max = $($offset + ($limit -1)) #Array of deivce id's limited by offset to max e.g. 0..99 $DeviceIDsRange = $DeviceIDs[$offset..$max] If (-Not $Silent) { #write to screen what we are doing write-host "Getting devices $offset to $max from CORE..." } $JSON = $null $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Ticket.Ticket"; "load_arg" = @{ "class" = "Ticket.TicketWhere"; "values" = @( @( "computer", "in", $DeviceIDsRange ), "&", @( "subject", "not like", "%SCOM%" ), "&", @(, @( "subject", "like", "%Windows ADC%" ) "|", @( "subject", "like", "%VM Not accessible to ADC%" ) ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "number", "subject", "created", "computers.number", "queue.name", "assignee.name", "status.name", "queue.name", @{ "id"="id" "attribute"="messages" "subattributes" = @( "text", "is_visible", "is_customer", "date", "private", "contact.name", "source.name" ) } ) }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $time = Measure-Command {$ticketresult += Get-CoreQuery -JSON $JSON} $timetotal += $time.Seconds #increments offset by limit so batch goes from 0..99 to 100..199 $offset = $offset + $limit } Until (($offset -ge $DeviceCount)) if($ShowQueryTime){ Write-host "CORE query completed in $timetotal seconds " } #Core returns the subattributes as messageIDs so we need to expand them out so they make sense ForEach ($ticket in $ticketresult.result){ $MessageIDs = ($ticket.messages.subattributes | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"} ).Name $Messages = @() ForEach($MEssageID in $MessageIDs){ $Messages += $ticket.messages.subattributes."$MessageID" } $ticket.messages = $Messages } Return $ticketresult.result } <# .SYNOPSIS Returns a list of failed cloud install tickets for supplied devices .DESCRIPTION Provide array of device numbers to get back a list of cloud agent failed tickets with ticket updates .PARAMETER DeviceIDs Mandatory Array of device IDs - can be a single one so long as its an array object .PARAMETER ShowQueryTime Optional switch to write to host the time the CORE query took Only really useful in troubleshooting or proving to a user your script is not slow .EXAMPLE With an array of accounts Get-CoreTicketsCloudAgentFailures -DeviceIDs @(502664,560570) .EXAMPLE With a single device Get-CoreTicketsCloudAgentFailures -DeviceIDs @(560570) .OUTPUT Psobject of tickets, ticket updates and device IDs .NOTES Written by Martin Howlett Thanks to Mark Wichall for writing the base function and nailing the JSON! #> Function Get-CoreTicketsCloudAgentFailures{ Param ( [Parameter(Mandatory=$true)][Array]$DeviceIDs, [Parameter()][switch]$ShowQueryTime ) $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Ticket.Ticket"; "load_arg" = @{ "class" = "Ticket.TicketWhere"; "values" = @( @( "computer", "in", $DeviceIDs ), "&", @( "subject", "in", @( "Cloud Monitoring Agent Installation Failed" ) ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "number", "subject", "created", "computers.number", "queue.name", "assignee.name", "status.name", "queue.name", @{ "id"="id" "attribute"="messages" "subattributes" = @( "text", "is_visible", "is_customer", "date", "private", "contact.name", "source.name" ) } ) }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $result = "" $time = Measure-Command {$ticketresult = Get-CoreQuery -JSON $JSON} if($ShowQueryTime){ Write-host "CORE query completed in $($Time.Seconds) seconds " } #Core returns the subattributes as messageIDs so we need to expand them out so they make sense ForEach ($ticket in $ticketresult.result){ $MessageIDs = ($ticket.messages.subattributes | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"} ).Name $Messages = @() ForEach($MEssageID in $MessageIDs){ $Messages += $ticket.messages.subattributes."$MessageID" } $ticket.messages = $Messages } Return $ticketresult.result } <# .SYNOPSIS Parses an ADC ticket from Get-CoreTicketsADC to get ADC result .DESCRIPTION Takes an ADC ticket object (get using Get-CoreTicketsADC) and parses the result to give you the result from the ADC process for that device .PARAMETER DeviceID Mandatory The number of the device - only used in output .PARAMETER Ticket A ticket object you get from Get-CoreTicketsADC .EXAMPLE $ADCTickets = Get-CoreTicketsADC -deviceids @(502664) $LatestTicket = $ADCTickets | Select-Object -Last 1 Parse-AdcTicket -DeviceID 502664 -Ticket $LatestTicket .OUTPUT ADC psobject with result, errors, ticket and device ID .NOTES Written by Martin Howlett #> Function Parse-AdcTicket { Param ( [Parameter(Mandatory=$true)][String]$DeviceID, [Parameter(Mandatory=$true)]$Ticket ) if(-not $Ticket.Messages){ throw "Parse-AdcTicket needs ticket object from Get-CoreTicketsADC" break } #sort the ticket updates by date $SortedUpdates = $Ticket.Messages | Sort-Object Date if($SortedUpdates){ #Look for the ADC ticket summary $ADCSummary = $SortedUpdates | Where-Object {$_.text -like "*WIN ADC SUMMARY*"} #This error is an exception, where the ADC summary is run but its still a hard failure, so we null the summary so we move to the else section #if we didnt we would try to parse the summary and it breaks if($ADCSummary -like "*Run Standard Scripts Time Out Expired*"){ #null the summary so the script goes to the hard failure switch $ADCSummary = $null } #if we find an ADC summary it, parse it for results if($ADCSummary){ #split it on line breaks so we convert the blob of line breaks into an array #the pipe means OR in this context $SummaryText = $ADCSummary.Text -split "`n|`r" #set some variables $SummarySection = $false $Summary = $false $SummaryEntry = @() $count = 0 #first 10 lines of ticket summary we can ignore, so trim them out $TrimmedSummary = $SummaryText[10..$($SummaryText.count-1)] #loop through the rest of the summary till we hit the text "LastBoot", which tells us its the end of the summary section ForEach($Line in $TrimmedSummary) { #when we find the last boot line, we can count, this way we know how many lines into the summary to go if($Line -like "*LastBoot*") { #we minus another 2 to get rid of the need help lines in the summary $End = 10 + ($count-2) } $count++ } #remove the bbcode line $SummaryEntry = $SummaryText[10..$($End)] | Where-Object {$_ -notlike "*[/b][/color]*"} #look for the passed text $Passed = $SummaryEntry | Where-Object {$_ -like "*ADC Passed - Ready for Customer*"} #check if skued for sql $sql = $SummaryEntry | Where-Object {$_ -like "*SQL Inventory had issues*"} #remove passed text so it doesnt report again as an error $SummaryEntry = $SummaryEntry | Where-Object {$_ -notlike "*ADC Passed - Ready for Customer*"} #check if we passed if($passed){ $AdcResult = "SUCCESS: ADC passed" $SummaryEntry = "" } else{ #If we didnt pass, count the errors if(($SummaryEntry | Measure-Object).count -eq 1) { if($sql) { $AdcResult = "SUCCESS: ADC passed but SKUed for SQL" } else { $AdcResult = "SUCCESS: ADC ran with summary error" } } else { if($sql) { $AdcResult = "SUCCESS: ADC ran with summary errors and SKUed for SQL" } else{ $AdcResult = "SUCCESS: ADC ran with summary errors" } } } $ADCErrors = $SummaryEntry } else { #No ADC summary means ADC didnt run. this is normally a hard failure but sometimes its a legitimate skip #in this scenrio we parse the whp;e ticket looking for the error or reason it didnt run switch -wildcard ($SortedUpdates.Text) { #switch our ticket updates for errors "*ICE Complete Field was {unchecked} for this device*" { $ADCErrors = "ICE Complete Field unchecked" $ADCResult = "WARNING: Hard failure - $ADCErrors" break } "*RBA could not connect the server and complete the necessary operation*" { $ADCErrors = "RBA could not connect the server" $ADCResult = "RBAERROR: Hard failure - $ADCErrors" break } "*This VM is not reachable from RBA to run ADC*" { $ADCErrors = "This VM is not reachable from RBA to run ADC" $ADCResult = "RBAERROR: Hard failure - $ADCErrors" break } "*Aric could not connect to server after reboot*" { $ADCErrors = "Aric could not connect to server after reboot" $ADCResult = "RBAERROR: Hard failure - $ADCErrors" break } "*Run Standard Scripts Time Out Expired*" { $ADCErrors = "Run Standard Scripts Time Out Expired" $ADCResult = "RBAERROR: Hard failure - $ADCErrors" break } #switch the ticket for known exceptions that stop ADC from running, but are not errors "*SKU for VM Replication - Target (VM)*" { $ADCResult = "WARNING: ADC skipped - SKU for VM Replication - Target (VM)" $ADCErrors = "None" break } #if we dont match anything above, then we dont know default { $ADCErrors = "ADC ran but unknown error" $ADCResult = "ERROR: Hard failure - ADC ran but unknown error" } } } } #if($SortedUpdates){ else{ $AdcResult = "WARNING: No ADC ticket found" $ADCErrors = "" $AdcTickets = "" $LatestAdcTicket = "" } $Output = New-Object PSObject -Property @{ AdcTicket = $Ticket.Number AdcResult = $AdcResult DeviceID = $DeviceID ADCErrors = $ADCErrors } Return $Output } <# .SYNOPSIS Passes the LastMile ticket update for success or error messages and retruns a PSO. .DESCRIPTION Parse-LastMilePhase parses the messages for the phase of last mile to this function for analysis or success or failure. .PARAMETER Messages Mandatory Ticket messages for the relivant phase of lastmile. See Parse-AdcTicketsLastMile for more information. .EXAMPLE Parse-LastMilePhase -ticket $ticket -phase 2 -device $device .OUTPUT Passes the LastMile ticket update for sucess or error messages and retruns a PSO. .NOTES Based on Parse-AdcTicket Written by Martin Howlett Modified for LastMile by Mark Wichall #> Function Parse-LastMilePhase { Param ( [Parameter(Mandatory=$true)]$ticket, [Parameter(Mandatory=$true)]$phase, [Parameter(Mandatory=$true)]$device ) #filters for lastmile update and sort the ticket updates by date $SortedUpdates = $Ticket.Messages | Where-Object {$_.text -like "*LastMile (Phase:*"} | Sort-Object Date If($SortedUpdates) { If (($phase -eq "1") -or ($phase -eq "2")) { $SortedUpdates | Where-Object {$_.text -like "*LastMile (Phase:$phase)*"} } Else { Write-Host "Parse-AdcTicketLastMile needs a Phase specified e.g. 1/2" -ForegroundColor Red return $device break } If(-not $SortedUpdates) { Write-Host "Parse-AdcTicketLastMile needs ticket object from Get-CoreTicketsADC" -ForegroundColor Red return $device break } #Look for the lastmile ticket summary $LastMileSummary = $SortedUpdates | Where-Object {$_.text -like "*LastMile Script Output:*"} | Sort-Object Date #if we find an lastmile summary it, parse it for results If($LastMileSummary) { #Sucessfull run of last mile $SuccessesFailed = $LastMileSummary | Where-Object {($_.text -like "*) completed successfully*") -or ($_.text -like "*) did not complete successfully*")} $LatestSuccessesFailed = $SuccessesFailed[-1] #gets last updated If ($LatestSuccessesFailed.text -like "*) completed successfully*") { If($($LatestSuccessesFailed.text) -like "*[table]*") { Try { #$device.LastMile."Phase$phase".Run = $true #don't update $device.LastMile."Phase$phase".Data = ConvertFrom-BBCode -Data $($LatestSuccessesFailed.text) $device.LastMile."Phase$phase".Result = "Success" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorMessage = $null $device.LastMile."Phase$phase".ErrorFunction = $null } Catch { #$device.LastMile."Phase$phase".Run = $true #not required $device.LastMile."Phase$phase".Data = $null $device.LastMile."Phase$phase".Result = "Failed" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorMessage = "Script Error BBCode table content not found" $device.LastMile."Phase$phase".ErrorFunction = $null } } Else { #$device.LastMile."Phase$phase".Run = $true #don't update $device.LastMile."Phase$phase".Data = $null $device.LastMile."Phase$phase".Result = "Failed" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorMessage = "Script Error BBCode table not found" $device.LastMile."Phase$phase".ErrorFunction = $null } Return $device } ElseIf ($LatestSuccessesFailed.text -like "*) did not complete successfully*") { #$device.LastMile."Phase$phase".Run = $true #don't update $device.LastMile."Phase$phase".Data = $null $device.LastMile."Phase$phase".Result = "Failed" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorFunction = $null $TextLinebyLine = $LatestSuccessesFailed.text -split "`n|`r" If (($LatestSuccessesFailed.text -like "*Error Function:*") -and ($LatestSuccessesFailed.text -like "*Error Message:*")) { #Gets the Error Function: ForEach($Line in $TextLinebyLine) { If($Line -like "*Error Function:*") { $ErrorFunction = $Line.split(":")[1] Break } } #Gets the Error Message: For ($i=0; $i -le $TextLinebyLine.length -1; $i++) { $NextLine = $TextLinebyLine[($i + 1)] If($TextLinebyLine[$i] -like "*Error Message:*") { $NextLine = $TextLinebyLine[($i + 1)] If($NextLine -like "*Ping*") { $device.LastMile."Phase$phase".ErrorMessage = "Ping" } ElseIf($NextLine -like "*simple PowerShell test*") { $device.LastMile."Phase$phase".ErrorMessage = "Simple Powershell Test" } ElseIf($NextLine -like "*Download*") { $device.LastMile."Phase$phase".ErrorMessage = "Cloud Files Download" } ElseIf($NextLine -like "*Rack password is incorrect*") { $device.LastMile."Phase$phase".ErrorMessage = "Rack password is incorrect" } ElseIf($NextLine -like "*Unhandelled Exception*") { $device.LastMile."Phase$phase".ErrorMessage = "ARIC Unhandelled Exception" } Else { $device.LastMile."Phase$phase".ErrorMessage = $NextLine } Break } } } Else { $device.LastMile."Phase$phase".ErrorMessage = "Unknown" $device.LastMile."Phase$phase".ErrorFunction = "CTKCore - Parse-LastMilePhase" } Return $device } Else { #$device.LastMile."Phase$phase".Run = $true #don't update $device.LastMile."Phase$phase".Data = $null $device.LastMile."Phase$phase".Result = "Failed" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorMessage = "Failed to find complete successfully in ticket message" $device.LastMile."Phase$phase".ErrorFunction = "CTKCore - Parse-LastMilePhase" } Return $device } Else { #$device.LastMile."Phase$phase".Run = $true #don't update $device.LastMile."Phase$phase".Data = $null $device.LastMile."Phase$phase".Result = "Failed" $device.LastMile."Phase$phase".Ticket = $Ticket.number #$device.LastMile."Phase$phase".RanFrom #don't update $device.LastMile."Phase$phase".ErrorMessage = "Could not find ticket update with LastMile Script Output" $device.LastMile."Phase$phase".ErrorFunction = "CTKCore - Parse-LastMilePhase" Return $device } } } <# .SYNOPSIS Gets Ticket and Accoutn team contacts for a ticket or array of tickets. .DESCRIPTION Gets Ticket and Accoutn team contacts for a ticket or array of tickets. .EXAMPLE Get-CoreTicketsAccountContacts -TicketIDs $TicketIDs .OUTPUT Array of PSO's .NOTES Used in IAW Desktop Scripts > Pre-BookedMaintenances.ps1 #> Function Get-CoreTicketsAccountContacts{ Param ( [Parameter(Mandatory=$true)][Array]$TicketIDs ) $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Ticket.Ticket"; "load_arg" = @{ "class" = "Ticket.TicketWhere"; "values" = @( @( "number", "in", $TicketIDs ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "number", "subject", "created", "assignee.name", "status.name", "queue.name", @{ "id" = "id"; "attribute" = "account"; "subattributes" = @( "account_exec", "primary_lead_tech" ) }; ); }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $time = Measure-Command {$ticketresult = Get-CoreQuery -JSON $JSON} Return $ticketresult.result } ################################## ## Contact functions ################################## #Functions related to contacts Function Get-AccountTeam { Param ( [Parameter(Mandatory=$true)]$Account ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-AccountTeam function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "Account = $Account" -noconsole $obj = @( @{ "class" = "Account.Account"; "attributes" = @( "account_exec", "primary_lead_tech", "business_development", "am_queue" ) } ) $obj[0]['load_arg'] = $Account $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $result = Get-CoreQuery -JSON $JSON $Contacts = @() Try{ $Contacts += Get-CoreContact -ID $result.result.primary_lead_tech.load_value } Catch { #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "lead tech not found, this is probably intensive managed" -type error $Contacts += $false } $Contacts += Get-CoreContact -ID $result.result.account_exec.load_value $Contacts += Get-CoreContact -ID $result.result.business_development.load_value $Contacts += $result.result.am_queue.id Return $Contacts #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-AccountTeam function" -noconsole } Function Get-CoreContact { Param ( [Parameter(Mandatory=$true)]$ID ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreContact function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "ID = $ID" -noconsole $obj = @( @{ "class" = "Contact.Contact"; "attributes" = @( "first_name", "last_name", "name", "primary_email", "employee_userid", "id", "teams", "title", "org", "ldap_groups", "individual", "departments", "imaway" ) } ) $obj[0]['load_arg'] = $ID $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreContact function" -noconsole } Function Get-CoreEmail { Param ( [Parameter(Mandatory=$true)]$ID ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreEmail function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "ID = $ID" -noconsole $obj = @( @{ "class" = "Contact.Email"; "attributes" = @( "address" ) } ) $obj[0]['load_arg'] = $ID $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreEmail function" -noconsole } <# .SYNOPSIS Gets CORE contact information from an SSO .DESCRIPTION Gets CORE contact information from an SSO .PARAMETER ContactSsos Array or single SSO username of the user you want to query You can get it in powershell using $env:username .PARAMETER ShowQueryTime Return the time of the CORE query .EXAMPLE Standard use Get-CoreContactFromUsername -ContactSsos $ENV:username .EXAMPLE SMultiple SSos Get-CoreContactFromUsername -ContactSsos @("joh1234","mart9999","gilb6659") .Notes Written by Martin Howlett - martin.howlett@rackspace.co.uk Used in IAW Desktop Scripts > ImpNewAccountTicketGenerator.ps1 #> Function Get-CoreContactFromUsername { Param ( [Parameter(Mandatory=$true)][Array]$ContactSsos, [Parameter()][switch]$ShowQueryTime ) $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Contact.Contact"; "load_arg" = @{ "class" = "Contact.ContactWhere"; "values" = @( @( "employee_userid", "in", $ContactSsos ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "first_name", "last_name", "name", "primary_email", "employee_userid", "id", "teams", "title", "org", "ldap_groups", "individual", "departments", "imaway" ) }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $result = "" $time = Measure-Command {$result = Get-CoreQuery -JSON $JSON} if($ShowQueryTime){ Write-host "CORE query completed in $($Time.Seconds) seconds " } return $result.result } ################################## ## Device functions ################################## #Functions that relate to getting information on devices/computers Function Get-CoreDeviceSKU { Param ( [Parameter(Mandatory=$true)]$Device ) $JSON = @" [ { "class": "Computer.Computer", "load_arg": $Device, "attributes": [ "number", { "attribute": "parts", "subattributes": { "Description": "ctk_product.description", "sku": "sku_number", "label": "display_label", "modified": "modified", "part_attributes": { "attribute": "partributes", "subattributes": [ "value", "sku_attribute.description" ] } } } ] } ] "@ Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON $SKUs = $result.result.parts.subattributes $SKUarr = @() ForEach ($Sku in $SKUs) { $temp = "" $temp = New-Object PSObject -Property @{ SKU = $Sku.Sku Value = $Sku.part_attributes.subattributes.value ValueLabel = $Sku.part_attributes.subattributes."sku_attribute.description" Label = $sku.label Description = $sku.Description Server = $Device } $SKUarr += $temp } Return $SKUarr } #Function to test if number is device and return true/false Function Test-COREWindowsDevice{ Param( [Parameter (Mandatory=$true)]$Device ) $obj = @( @{ "class" = "Computer.Computer"; "attributes" = @( "name", "os_type" ) } ) $obj[0]['load_arg'] = $Device $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $result = Get-CoreQuery -ErrorAction SilentlyContinue -JSON $JSON if( $result.result.os_type -like "*Windows*"){ return $true }else{ return $false } } <# .SYNOPSIS Gets the DRAC details for a device from CORE .DESCRIPTION Uses CTKCore to retrieve the DRAC IP, username and password from CORE .PARAMETER DeviceNumber The CORE device number of the device to retrieve the DRAC details for .EXAMPLE Get-CoreDeviceDrac -DeviceNumber 123456 #REQUIRES -Version 3.0 #> Function Get-CoreDeviceDrac { Param( [Parameter(Mandatory=$true)][string] $DeviceNumber ) # Generate or retrieve a CORE token Validate-Token # Create the JSON request for the attributes that we want to retrieve $JSON = ' [ { "class": "Computer.Computer", "load_arg": '+ $DeviceNumber +', "attributes": [ "dracnet_ip.ip_address", "drac_passwords.username", "drac_passwords.password" ] } ] ' # Invoke a CORE query to retrieve the results $result = Get-CoreQuery -JSON $JSON # Return the results Return $result.result | select-object @{Label="DracIp"; Expression={$_."dracnet_ip.ip_address"}}, @{Label="DracUsername"; Expression={$_."drac_passwords.username"}}, @{Label="DracPassword"; Expression={$_."drac_passwords.password"}} } <# .SYNOPSIS Changes status of Windows Devices .DESCRIPTION Changes status of Windows Devices .EXAMPLE Set-CoreDeviceStatus -Device 443486 -Reason "This is a Test" -Status 10 #REQUIRES -Version 3.0 .Notes Written by Bob Larkin list of statuses #https://ws.core.rackspace.com/ctkapi/browse/Computer/Status -1 Computer No Longer Active 1 Order Submitted 2 Sent Contract 3 Received Contract 4 In Assembly 5 Ready for QC 6 QC Complete/Ready to Rack 9 Segment Configuration 10 Ready for Account Team 12 Online/Complete #> Function Set-CoreDeviceStatus { Param ( [Parameter(Mandatory=$true)]$DeviceNum, [Parameter(Mandatory=$true)]$Status, [Parameter(Mandatory=$true)]$Reason, [Parameter()]$ContractID ) #this inlcudes the contract id of the device so it will be upgraded via the contract $obj = @( @{ "class" = "Computer.Computer"; "load_arg" = "$($DeviceNum)"; "method" = "changeStatus"; "args" = @( "$Status" ) "keyword_args" = @{ "reason" = "$Reason" } } ) #add the contract ID if user specified it If($ContractID) { $obj."keyword_args".Add("contract_id", $ContractID) } $JSON = $obj | ConvertTo-Json -Depth 10 $result = Get-CoreQuery -JSON $JSON #return if query sucessful return $result[0] } Function Get-COREPasswordSecrets{ Param( [Parameter(Mandatory=$true)]$DeviceID, [Parameter(Mandatory=$true)]$session #use Get-CORESession to get a session ) $URL = "https://core.rackspace.com/py/core/server/server_passwords_secrets.pt" Try { $response = Invoke-WebRequest $url -WebSession $session -Body @{computer_number="$DeviceID"} -ErrorAction Stop -Method Post } catch { switch -Wildcard ($_){ "*underlying*closed*" { Throw "ERROR: Could not connect to CORE, close and open PowerShell and try again"} "*404*" {Throw "ERROR: 404 $url not found"} "*500*" {Throw "ERROR: 500 CORE server error"} default {Throw "ERROR: unknown Invoke-WebRequest error"} } } if ($response.AllElements.innerhtml -like "*CORE: Waiting for Authentication*") { Throw "ERROR: Failed to authenticate with CORE" } return $response } Function Get-CoreLoginMethod { Param( [Parameter()]$Account, [Parameter()]$Ticket, [Parameter()]$Device ) if($account.length -gt 0) { $computers = Get-CoreDevicesAccount -Account $Account } if($ticket.length -gt 1) { $computers = Get-CoreDevicesTicket -Ticket $ticket } if($Device.length -gt 1) { $computers = Get-CoreDevice -Device $Device } #filter windows only devices $computers = $computers | Where-Object {($_.is_windows -eq $true) -AND ($_.Plattype -notlike "*cluster*")} $Session = Get-CORESession $output = @() ForEach ($computer in $computers) { $LoginMethod = $Secrets = $InnerText = $temp = $null $Secrets = Get-COREPasswordSecrets -DeviceID $computer.number -session $session #yeah... not my best work but it gets the job done $InnerText = ($Secrets.AllElements | where-object { $_.innertext -like "*Login Method*"})[1].innertext $LoginMethod = ($InnerText.split([environment]::NewLine) | Where-Object {$_ -like "Login Method*"}).Split(":")[1] #Temp psobject $temp = New-Object PSObject -Property @{ "Server" = $computer.servername "Login Method" = $LoginMethod } $output += $temp } return $output } Function Get-CoreSqlPasswords { Param( [Parameter(Mandatory=$true)]$DeviceID, [Parameter(Mandatory=$true)]$Session ) $Secrets = Get-COREPasswordSecrets -DeviceID $DeviceID -session $Session #Random stuff to get the correct out ofr the scrren $text = $Secrets.AllElements | where-object { $_.outertext -like "*Service Account*"} $inner = $text[2] $Passwords = (($inner.outerText).Replace("Notes","").split([environment]::NewLine)) | Where-Object {$_ -ne ""} return $Passwords } Function Set-CoreDeviceStatusFrontEnd { <# .SYNOPSIS Changes status of Windows devices via front end and not API .DESCRIPTION Changes status of Windows devices via front end and not API .EXAMPLE Set-CoreDeviceStatus -Device 443486 -Status 10 #REQUIRES -Version 3.0 .Notes Written by Martin Howlett list of statuses #https://ws.core.rackspace.com/ctkapi/browse/Computer/Status -1 Computer No Longer Active 1 Order Submitted 2 Sent Contract 3 Received Contract 4 In Assembly 5 Ready for QC 6 QC Complete/Ready to Rack 9 Segment Configuration 10 Ready for Account Team 12 Online/Complete #> Param( [Parameter(Mandatory=$true)][int32]$DeviceID, [Parameter(Mandatory=$true)][int32]$ContractID, [Parameter(Mandatory=$true)][int32]$Account, [Parameter(Mandatory=$true)][int32]$Status, [Parameter(Mandatory=$true)]$session #use Get-CORESession to get a session ) $URL = "https://core.rackspace.com/py/account/upgradeContractWithSelectStatus.pt" $params = @{ "$DeviceID" = $Status "contract_id" = $ContractID "account_number" = $Account } Try { $response = Invoke-WebRequest $url -WebSession $session -Body $params -ErrorAction Stop -Method Post } catch { switch -Wildcard ($_){ "*underlying*closed*" { Throw "ERROR: Could not connect to CORE, close and open PowerShell and try again"} "*404*" {Throw "ERROR: 404 $url not found"} "*500*" {Throw "ERROR: 500 CORE server error"} default {Throw "ERROR: unknown Invoke-WebRequest error"} } } if ($response.AllElements.innerhtml -like "*CORE: Waiting for Authentication*") { Throw "ERROR: Failed to authenticate with CORE" } return $response } Function Set-COREBuildInstructions { <# .SYNOPSIS Sets the build instructions for a device .DESCRIPTION Sets the field "Pre-Production Instructions (Hardware):" on a CORE device Depends on Get-CoreSession so we talk to core via the "front end" .PARAMETER Device Mandatory 6 digit device ID .PARAMETER Instructions Mandatory Test to add .PARAMETER Session Mandatory Core session with admin token use Get-CoreSession .EXAMPLE Set-COREBuildInstructions -Device 502664 -Instructions "Master ticket 123465-654987" -Session (Get-CoreSession) .OUTPUT True/False .NOTES Written by Martin Howlett #> Param( [Parameter(Mandatory=$true)]$Device, [Parameter(Mandatory=$true)]$Instructions, [Parameter(Mandatory=$true)]$Session ) $URL = "https://core.rackspace.com/py/computer/setBuildInstructions.pt" $params = @{ "pre_build_message" = $Instructions "computer_number" = $Device } Try{ $response = Invoke-WebRequest $url -WebSession $Session -Body $params -ErrorAction Stop -Method Post } catch{ $response = $false } if($response -like "*Set Build Instruction*"){ write-output $true } else{ write-output $false } } Function Set-COREDueDates { <# .SYNOPSIS Sets the implementation due dates for a device .DESCRIPTION Sets the field due to customer and due from support in CORE Depends on Get-CoreSession so we talk to core via the "front end" .PARAMETER Contract Mandatory Contract ID of device .PARAMETER CoNamentract Mandatory Full name of device in CORE .PARAMETER NickName Mandatory NickName of device in CORE .PARAMETER CustomerDueDate Mandatory Due date to customer Must be in format 'yyyy-MM-dd' .PARAMETER SupportDueDate Mandatory Due date from support customer Must be in format 'yyyy-MM-dd' .PARAMETER Session Mandatory Core session with admin token use Get-CoreSession .EXAMPLE $DateResult = Set-COREDueDates -Contract $Computer.Contract -Name $computer.Name -Number $Computer.Number -NickName $computer.NickName -CustomerDueDate $(get-date $CustomerDate -format 'yyyy-MM-dd') -SupportDueDate $(get-date $SupportDate -format 'yyyy-MM-dd') -Session $session .OUTPUT True/False .NOTES Written by Martin Howlett #> Param( [Parameter(Mandatory=$true)]$Contract, [Parameter(Mandatory=$true)]$Name, [Parameter(Mandatory=$true)]$Number, [Parameter(Mandatory=$true)]$NickName, [Parameter(Mandatory=$true)]$CustomerDueDate, [Parameter(Mandatory=$true)]$SupportDueDate, [Parameter(Mandatory=$true)]$Session ) $URL = "https://core.rackspace.com/ph3/contract/edit_computer/$Contract" $body = @" [{"number":$Number,"name":"$Name","nickname":"$NickName","due_date":"$CustomerDueDate`T00:00:00","due_to_support_date":"$SupportDueDate`T00:00:00"},{"number":$Number,"name":"$Name","nickname":"$NickName","due_date":"$CustomerDueDate`T00:00:00","due_to_support_date":"$SupportDueDate`T00:00:00"}] "@ $params = @{ "modified_data" = "$body" } Try{ $response = Invoke-WebRequest $url -WebSession $Session -Body $params -ErrorAction Stop -Method Post } catch{ $response = $false } return $response } <# .SYNOPSIS Gets device statuses from a list of device IDs .DESCRIPTION Provide array of device numbers to get back a list of device statuses Can supply a single device ID .PARAMETER DeviceIDs Mandatory Array of device IDs - can be a single one so long as its an array object .PARAMETER ShowQueryTime Optional switch to write to host the time the CORE query took Only really useful in troubleshooting or proving to a user your script is not slow .PARAMETER Silent Stops output to screen, default is to output to screen .PARAMETER limit Limits the number of devices to get per query. Default value is 100. If there are 300 devices it will run three queries of 100 each and merge the result .EXAMPLE With an array of accounts Get-CoreDeviceStatus -DeviceIDs @(502664,560570) .EXAMPLE With a single account Get-CoreDeviceStatus -DeviceIDs @(560570) .OUTPUT Psobject of statuses and device IDs .NOTES Written by Martin Howlett, Mark Wichall #> Function Get-CoreDeviceStatus{ Param ( [Parameter(Mandatory=$true)][Array]$DeviceIDs, [Parameter()][switch]$ShowQueryTime, [Parameter()]$limit = 100, #100 seems to be reliable when core is responding slowly [Parameter()][switch]$Silent ) #Vars $offset = 0 $timetotal = 0 $Computers = @() $DeviceIDsRange = @() $Ticketresult = $null #make sure we have a unique list $DeviceIDs = $DeviceIDs | Sort-Object -Unique $DeviceCount = $DeviceIDs.count #Limit cannot be higher than 250 as CTKCore will error with http 400. If (($Limit -gt 250) -or ($Limit -lt 2)) { $Limit = 100 } If (-Not $Silent) { Write-Host "Total devices to query = $DeviceCount" } do { #Top limit on devices to query in this batch $max = $($offset + ($limit -1)) #Array of deivce id's limited by offset to max e.g. 0..99 $DeviceIDsRange = $DeviceIDs[$offset..$max] If (-Not $Silent) { #write to screen what we are doing write-host "Getting devices $offset to $max from CORE..." } write-host "`$DeviceIDsRange = $DeviceIDsRange" $JSON = $null $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Computer.Computer"; "load_arg" = @{ "class" = "Computer.ComputerWhere"; "values" = @( @( "number", "in", $DeviceIDsRange ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "status.name", "status.active", "number" ) }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $time = Measure-Command {$ticketresult += Get-CoreQuery -JSON $JSON} $timetotal += $time.Seconds #increments offset by limit so batch goes from 0..99 to 100..199 $offset = $offset + $limit } Until (($offset -ge $DeviceCount)) if($ShowQueryTime){ Write-host "CORE query completed in $timetotal seconds " } Return $ticketresult.result } <# .SYNOPSIS Adds cluster sql node pass note information to its child notes .DESCRIPTION Takes a computer object (list of devices from core got with get-coredevice/ticket/account PArse through it looking for Windows virtual servers If they are SKUed for CORE, it then adds the password notes to its child nodes If the child nodes are not present, it will add them .PARAMETER Computers Mandatory Array list of CORE computer object got with get-coredevice/ticket/accountof device IDs .EXAMPLE With an array of accounts Merge-SqlPasswordNotes -Computers $Computers .OUTPUT Array list of CORE computer objects .NOTES Written by Martin Howlett #> Function Merge-SqlPasswordNotes { Param ( [Parameter(Mandatory=$true)][array]$Computers, [Parameter()][Switch]$GetMissingNodes ) #find virtual server objects $VirtualComputers = $Computers | Where-Object {$_.PlatName -like "Windows Cluster"} #Find virtual server objects SKUed for SQL $SqlObjects = @() ForEach($Computer in $VirtualComputers) { if($Computer.Skus | Where-Object {($_.Description -like "*SQL Server*") -or ($_.Description -like "*MS SQL*")}) { $SqlObjects += $Computer } } write-verbose "---- Merge-SqlPasswordNotes" #iterate through our sql virtual objects #update the password notes on these devices to match the cluster node, this way we can use the SA password on the nodes Foreach ($Computer in $SqlObjects) { write-verbose "Working on virtual server SQL object $($Computer.ServerName)" #Iterate through each node behind the sql cluster object ForEach ($ComputerBehind in $Computer.attached_devices) { write-verbose " Working on node $($ComputerBehind.load_value) behind $($Computer.ServerName)" $FoundComputer = $null #look for a matching server in the core list $FoundComputer = $Computers | Where-Object {$_.Number -eq $ComputerBehind.load_value} if($FoundComputer) { #update the passnotes to match the sql notes Add-Member -InputObject $FoundComputer -MemberType NoteProperty -Name Passnotes -Value ($Computer.Passnotes | out-string) -Force } } } return $Computers } ################################## ## Feature functions ################################## #Functions that do more then a single thing and should probably be their own script Function Get-CoreTicketSummary { <# .Synopsis Summary of all tickets in a given time frame for an account .Description The Get-CoreTicketSummary cmdlet will pull a list of all tickets for a customer within a given time frame. .Example Get-CoreTicketSummary -Account 24645 -StartDate "2015-01-01- 00:00:00" -EndDate "2015-01-31 23:59:59" This example shows how to retrive a list of tickets for the customer between 1st Jan 2015 00:00:00 and 31st Jan 2015 at 23:59:59 .Example Get-CoreTicketSummary -Account 24645 This example shows how to retrive a list of tickets for the customer between 1st day of the month 00:00:00 to the last day of the month at 23:59:59 for the last month. For example if it was March 18th, this command would pull everything from 1st-28th Feburary. #> Param( [Parameter(Mandatory=$true)][string]$Account, [Parameter()][datetime]$StartDate, [Parameter()][datetime]$EndDate, [Parameter()][switch]$UseLastMonth ) If($UseLastMonth){ $date = Get-Date $numMonthsAgo = -1 $startofmonth = Get-Date $date -day 1 -hour 0 -minute 0 -second 0 $endofmonth = (($startofmonth).AddMonths(1).AddSeconds(-1)) $firstDayOfMonth = $startofmonth.AddMonths($numMonthsAgo) $lastDayOfMonth = $endofmonth.AddMonths($numMonthsAgo) }else{ $firstDayOfMonth = $StartDate $lastDayOfMonth = $EndDate } $limit = 50 $offset = 0 $result = @() do { $JSON = @" [ { "class": "Ticket.Ticket", "load_arg": { "class": "Ticket.TicketWhere", "values": [ [ "account", "=", "$Account" ], "&", [ "created", ">", "$($firstDayOfMonth.ToString("yyyy-MM-dd HH:mm:ss"))" ], "&", [ "created", "<", "$($LastDayOfMonth.ToString("yyyy-MM-dd HH:mm:ss"))" ] ], "limit": $limit, "offset": $offset }, "attributes": [ "number", "status.name", "subject", "source", "created", "creator.name", "severity.name", "classification.name", "category.name", "subcategory.name", "last_response_date", "computers", { "attribute": "computers", "subattributes": [ "name" ] } ] } ] "@ Validate-Token $QueryResult = "" $QueryResult = (Get-CoreQuery -JSON $JSON).Result $result += $QueryResult $offset = $offset + $limit }Until (($QueryResult.created).count -lt $limit) $Tickets = @() foreach($Ticket in $result){ [string]$lstComputers = "" $arrComp = @() ForEach ($Comp in $Ticket.Computers.subattributes){ [string]$strComp = $Comp.name $arrComp += $strComp.Split(".")[0] } $lstComputers = $arrComp -join " : " $Tickets += [PSCustomObject]@{ TicketID=$Ticket.number; Status=$Ticket."status.name"; Subject=$Ticket."subject"; Created=$Ticket.created; Creator=$Ticket."creator.name"; Severity=$Ticket."severity.name"; Classification=$Ticket."classification.name"; Category=$Ticket."category.name"; SubCategory=$Ticket."subcategory.name"; Last_Response_Date=$Ticket.last_response_date; Source=$Ticket.source.name; Devices=$lstComputers; } } Return $Tickets } Function Get-ClusterHostfile{ param( [Parameter()]$Devices, [Parameter()]$Domain ) #Create the arrays we will use $Nodes =@() $VirtualComputers =@() $NodesBackup =@() $VirtualComputersBackup =@() $Heartbeats =@() $Blank =@() IF ($HeartbeatIP.Length -lt 1) {$HeartbeatIP = 0} If(-Not $Domain) { #The domain is often incorrect on virtual servers, so we get the domain for the first node and ask user $Node1 = ($devices | Where-Object {$_.is_cluster -ne $True})[0] Write-host "Use domain " -NoNewline -ForegroundColor DarkYellow Write-host "$($Node1.Domain)" -NoNewline -ForegroundColor Yellow Write-host " in the hostfile? (Y/N)" -ForegroundColor DarkYellow $Answer = Read-Host if($Answer -like "y*") { $Domain = $Node1.Domain }else { Write-host "Enter fully qualified domain name to use..." -ForegroundColor Yellow $Domain = Read-Host } } ForEach ($Device in $Devices) { #we null values first to stop errors with previous loops $ServiceNetIP = $null $PrivateIP = $null $Account = $null $Number = $null $NoBackup = $null $Account = $Device.Account $Number = $Device.number $Name = $Device.ServerName write-host "Generating hostfile entries for $($Device.ServerName), please wait..." -ForegroundColor Cyan if($Device.Domain -ne $domain){ $DomainError = $true write-host "ERROR! Domain set in CORE doesn't match for all devices. Please update CORE as it could be incorrect" -ForegroundColor Red } #check name and exclude if NT or MSDTC If (($Name -like "*-NT*") -OR ($Name -like "*MSDTC*")){ $NoBackup = $true } $IPs = Get-IPsfromIPCommander -Device $Number $ServiceNetIP = $IPs.ServiceNetIP $PrivateIP = $IPs.PrivateIP #check to make sure its not blank If((-NOT $ServiceNetIP) -AND (-NOT $NoBackup)){ $ServiceNetIP = "BLANK" $Blank += $Number write-host "ERROR! Blank ServiceNet for $name, please manually add" -ForegroundColor Red } #check if its a virtual cluster device IF ($Device.is_cluster){ #build the line for the host file for the nat ip $VirtualComputers += "$PrivateIP $Name $Name.$Domain" IF (-NOT $NoBackup){ #and the line for the backup ip $VirtualComputersBackup += "$ServiceNetIP $Account-$Number" } #if its not a virtual device, it must be a node }ELSE{ #build the line for the host file for the nat ip $Nodes += "$PrivateIP $Name.$Domain" IF (-NOT $NoBackup){ #and the line for the backup ip $NodesBackup += "$ServiceNetIP $Account-$Number" } #increase the heartbeat IP by 1 and build the heartbeat IP $HeartbeatIP ++ $Heartbeats += "10.10.10.$HeartbeatIP heartbeat$HeartbeatIP" } } #Create the temp text file for the hostfile #$Hostfiletemp = "C:\RS-PKGS\hostfile.txt" #New-Item $Hostfiletemp -Type File -Force | Out-Null #Write out to the new lines to an array $newlines = @() If($Blank){$newlines += "WARNING! BLANK SERVICENET DETECTED FOR $($Blank -join ",")!!"} If($DomainError){$newlines += "WARNING! DOMAIN IS NOT THE SAME FOR ALL DEVICES IN CORE, PLEASE CONFIRM / UPDATE CORE!"} $newlines += " " $newlines += " " $newlines += "#Node IPs" $newlines += "$(($Nodes) | Out-String)" $newlines += "#Cluster device IPs" $newlines += "$($VirtualComputers | Out-String)" $newlines += "#Heartbeat IPs" $newlines += "$($HeartBeats | Out-String)" $newlines += "#Node backup IPs" $newlines += "$($NodesBackup | Out-String)" $newlines += "#Cluster device backup IPs" $newlines += "$($VirtualComputersBackup | Out-String)" #Add them to the temp file we created #Add-content $Hostfiletemp -value $newlines | Out-Null #Update-Hostfile -newlines $newlines return $newlines } ################################## ## Misc functions ################################## #Functions that dont belong anywhere else Function Get-CoreIP { Param ( [Parameter(Mandatory=$true)]$ID ) #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---Start of Get-CoreIP function" -noconsole #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "ID = $ID" -noconsole $obj = @( @{ "class" = "IPSpace.IPAssignment"; "attributes" = @( "ip_address" ) } ) $obj[0]['load_arg'] = $ID $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result.ip_address #Update-Logfile -logfilepath $Global:CTKCoreLogPath -Message "---End of Get-CoreIP function" -noconsole } #Use with CTKCoreTestCode.ps1 #Function you can use to test/troubleshoot Json. #Author:Mark Wichall Function Test-CTKJson { Param ( [Parameter()]$JSON ) Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON return $result.result } Function Get-CoreSSLCertificate { Param ( [Parameter()]$ID, [Parameter()]$Ticket ) #$ID = "150117-06312" #$ID = "2105454" IF ($ID) { $loadArg = $ID } ELSE { $loadArg = $Ticket } $obj = @( @{ "class" = "Certificate.Certificate"; "attributes" = @( "account", "active", "cert", "company_name", "csr", "customer_ticket", "domain_name", "end_date", "id", "order_number", "pretty_end_date", "private_key", "start_date", "state", "status", "ticket", "product_type", "computers" ) } ) $obj[0]['load_arg'] = $loadArg $JSON = $obj | ConvertTo-Json -Depth 10 Validate-Token $result = "" $result = Get-CoreQuery -JSON $JSON Return $result.result <# $JSON=@" [ { "class": "Certificate.Certificate", "load_arg": { "class": "Certificate.CertificateWhere", "values": [ [ "ticket", "=", "140909-04864" ] ], "limit": 200, "offset": 0 }, "attributes": [ "id" ] } ] "@ #> } Function Test-COREAccount{ Param( [Parameter (Mandatory=$true)]$Account ) $obj = @( @{ "class" = "Account.Account"; "attributes" = @( "number" ) } ) $obj[0]['load_arg'] = $Account $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $result = Get-CoreQuery -ErrorAction SilentlyContinue -JSON $JSON if( $result.result){ return $true }else{ return $false } } Function Get-CORESession{ #confirm out $Global:Token is good validate-Token #Create a new session and add our core admin cookie $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession $cookie = New-Object System.Net.Cookie $cookie.Name = "rackspace_admin_session" $cookie.Value = $Global:Token $cookie.Domain = "core.rackspace.com" $session.Cookies.Add($cookie); return $session } <# .SYNOPSIS Gets account segments from a list of account IDs .DESCRIPTION Provide array of account numbers to get back a list of segments Can supply a single account ID .PARAMETER Accounts Mandatory Array of account IDs - can be a single one so long as its an array object .PARAMETER ShowQueryTime Optional switch to write to host the time the CORE query took Only really useful in troubleshooting or proving to a user your script is not slow .EXAMPLE With an array of accounts Get-CoreAccountSegment -Accounts @(1103359,2564896) .EXAMPLE With a single account Get-CoreAccountSegment -Accounts @(1103359) .OUTPUT Psobject of segments and account IDs .NOTES Written by Martin Howlett #> function Get-CoreAccountSegment{ Param ( [Parameter(Mandatory=$true)][Array]$AccountIDs, [Parameter()][switch]$ShowQueryTime ) $JSON = (ConvertTo-Json -Depth 10 @{ "class"= "Account.Account"; "load_arg" = @{ "class" = "Account.AccountWhere"; "values" = @( @( "number", "in", $AccountIDs ) ); "limit" = "250"; "offset" = "0" }; "attributes" = @( "segment.name", "number" ) }).Replace("\u0026","&") #the replace is important as convertto-json will break &'s $result = "" $time = Measure-Command {$result = Get-CoreQuery -JSON $JSON} if($ShowQueryTime){ Write-Host "Core query completed in $($time.seconds) seconds" } return $result.result } <# .SYNOPSIS Convert account number to the account name .DESCRIPTION Convert account number to the account name .PARAMETER Account Mandatory The account number .Notes Written by Martin Howlett - martin.howlett@rackspace.co.uk Used in IAW Desktop Scripts > NewAccountTicketGenerator.ps1 #> Function Get-AccountName { Param ( [Parameter(Mandatory=$true)]$Account ) $obj = @( @{ "class" = "Account.Account"; "attributes" = @( "name" ) } ) $obj[0]['load_arg'] = $Account $JSON = $obj | ConvertTo-Json -Depth 10 $result = "" $result = Get-CoreQuery -JSON $JSON return $result.result.name } <# .SYNOPSIS Sets the bandwidth allowance on an account .DESCRIPTION Uses a Core session to set the bandwidth on a CORE account Depends on Get-CoreSession so we talk to core via the "front end" .PARAMETER Bandwidth Mandatory The amount of bandwidth (in GB) to set on the account .PARAMETER AccountNum Mandatory CORE account number .PARAMETER TicketNumm Mandatory Ticket Number to associate the bandwidth upgrade with .PARAMETER Session Mandatory Core session with admin token use Get-CoreSession .EXAMPLE $Result = Set-CoreAccountBandwidth -Bandwidth 1000 -AccountNum 1103359 -TicketNum 123456-65487 -Session (Get-CoreSession) .OUTPUT True/False .NOTES Written by Martin Howlett #> Function Set-CoreAccountBandwidth{ Param ( [Parameter(Mandatory=$true)][int]$Bandwidth, [Parameter(Mandatory=$true)][int]$AccountNum, [Parameter(Mandatory=$true)][string]$TicketNum, [Parameter(Mandatory=$true)]$Session ) $URL = "https://core.rackspace.com/py/bandwidth/ajax.pt?=hidden_button&additional_value=$Bandwidth&ticket_or_contract=$ticketnum&account_number=$AccountNum&edit_mode=" Try { $response = Invoke-WebRequest $url -WebSession $Session -Body $params -ErrorAction Stop -Method Post } catch { $response = $false } #if we confirmation message in the output return true if($response.Content -like "*Attributes saved for Additional Bandwidth*") { return $true } else { #check if we just set the same thing again if($response.Content -like "*You didn't change anything*") { return $true } else { return $false } } } <# .SYNOPSIS Parses the notes field in Core .DESCRIPTION Parses the notes field in Core for SQL credentials and returns a formatted object Expecting this format in CORE (multiple instances supported: Instance: -DB1\ ---------------------------------------------------- Service Account: -SQLSVC1 Service Password: xxxxxxxxxxxxx Agent Account: -AGTSVC1 Agent Password: xxxxxxxxxxxxx Analysis Account: -ANLSVC1 Analysis Password: xxxxxxxxxxxxx Integration Account: -INTSVC1 Integration Password: xxxxxxxxxxxxx Reporting Account: -RPTSVC1 Reporting Password: xxxxxxxxxxxxx SQL SA password: xxxxxxxxxxxxx SQL Service Group: -SVCGROUP1 ---------------------------------------------------- Will still parse without the last "----" line present on single instances. .PARAMETER Notes Mandatory The notes field from Core (Computer.Computer - Notes attribute) Added by default in Get-CoreDevices etc .Notes Written by Martin Howlett - martin.howlett@rackspace.co.uk Used in WHAM to get SA passwords in TSQL templates #> Function Parse-CoreDevicePasswordNotes { Param ( [Parameter()]$Notes ) if($Notes.Length -lt 3) { Return "ERROR No SQL password notes found" } $split = $Instance = $instanceHeader = $null $Connection = @() #split the string into arrays of strings $split = $notes -split "`r|`n" | Where-Object {$_ -ne ""} #check we have the instance line, if not assume we have no SQL data If($split | Where-Object {$_ -like "Instance:*"}) { $instanceHeader = $true } if($instanceHeader) { #loop through each line and parse it ForEach($line in $split) { Write-Verbose "LINE:: $line" if ($line -like "Instance:*") { #if instance line assume new instance and create new Instance object Write-Verbose "New Instance: True" $ConnectionString = ($line -replace "Instance:","").Trim() $Instance = New-Object PSObject -Property @{ "ConnectionString" = $ConnectionString "SqlSvcUser" = "-" "SqlSvcPass" = "-" "AgtSvcUser" = "-" "AgtSvcPass" = "-" "AnlSvcUser" = "-" "AnlSvcPass" = "-" "IntSvcUser" = "-" "IntSvcPass" = "-" "RptSvcUser" = "-" "RptSvcPass" = "-" "SaPass" = "-" "SqlGRoup" = "-" } } if ($line -like "*----------------*") { #we use these to find the seperators between instances called header and footers for the 1st and 2nd we find Write-Verbose "LINE :: Matches ----------" if($Header) { Write-Verbose "LINE :: Footer" $Footer = $true $Header = $false #if we find a footer, add the current instance to our output array $Connection += $Instance } else { Write-Verbose "LINE :: Header" $Header = $true $Footer = $false } } #parse the other lines to find the data Switch -Wildcard ($line) { {$_ -like "Service Account:*"} {$Instance.SqlSvcUser = ($($_ -replace "Service Account:","").Trim())} {$_ -like "Service Password:*"} {$Instance.SqlSvcPass = ($($_ -replace "Service Password:","").Trim())} {$_ -like "Agent Account:*"} {$Instance.AgtSvcUser = ($($_ -replace "Agent Account:","").Trim())} {$_ -like "Agent Password:*"} {$Instance.AgtSvcPass = ($($_ -replace "Agent Password:","").Trim())} {$_ -like "Analysis Account:*"} {$Instance.AnlSvcUser = ($($_ -replace "Analysis Account:","").Trim())} {$_ -like "Analysis Password:*"} {$Instance.AnlSvcPass = ($($_ -replace "Analysis Password:","").Trim())} {$_ -like "Integration Account:*"} {$Instance.IntSvcUser = ($($_ -replace "Integration Account:","").Trim())} {$_ -like "Integration Password:*"} {$Instance.IntSvcPass = ($($_ -replace "Integration Password:","").Trim())} {$_ -like "Reporting Account:*"} {$Instance.RptSvcUser = ($($_ -replace "Reporting Account:","").Trim())} {$_ -like "Reporting Password:*"} {$Instance.RptSvcPass = ($($_ -replace "Reporting Password:","").Trim())} {$_ -like "SQL SA password:*"} {$Instance.SaPass = ($($_ -replace "SQL SA password:","").Trim())} {$_ -like "SQL Service Group:*"} {$Instance.SqlGroup = ($($_ -replace "SQL Service Group:","").Trim())} } } #sometimes the user does not paste in the last ---- line, so we dont hit the footer and save it #in this case we will just save it ourselves again if(-not $Footer) { $Connection += $Instance } Return $Connection } else { Return "ERROR Could not parse SQL password notes" } }