Search This Blog

Showing posts with label Powershell. Show all posts
Showing posts with label Powershell. Show all posts

Wednesday, September 29, 2021

Windows Terminal - Add to context menu.

Windows Terminal is a windows APP so is a little different than normal programs. I am not an expect but just include this info for some background.

Windows terminal runs a wt.exe

it can be found here..

%LOCALAPPDATA%\Microsoft\WindowsApps\wt.exe


So to do what we want we have to override some settings in the config of the wt.exe. These can be found here.. This may change depending on what version of the windows terminal you  have installed.. I have the preview...

%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState

but could be 

%LOCALAPPDATA%\Packages\Microsoft. WindowsTerminal_8wekyb3d8bbwe\LocalState\

So we need to add this line 

"startingDirectory": null

to the settings.json file...

"profiles":
    {
        "defaults":
        {
            // Put settings here that you want to apply to all profiles.
            "startingDirectory": null
        },


Then we can update the registry to add an entry to the right click context menu.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\wt]
@="Open Windows Terminal here"
"ShowBasedOnVelocityId"=dword:00639bc8
"Extended"=""

[HKEY_CLASSES_ROOT\Directory\Background\shell\wt\command]
@="%LOCALAPPDATA%\\Microsoft\\WindowsApps\\wt.exe"

And now we should be good.. Right click on a folder and there should be an option







Share/Bookmark

Wednesday, May 20, 2020

automated - timestamped ping and tracert with logs (powershell)

Recently I had to run ping tests to provide to my internet provider... TPG.... I have an issue where in the evening my latency spikes (goes from around 7-10ms to up over 200ms, which makes gaming a joke and non event) from around 19:00 to 22:00... This has never happened before, so is abnormal..

In the past I have had no real issue with the support, however this time it has been slow... and a real waste of time... I had already turned off wifi and disconnected all devices except my test machine.. The issue still existed... the increased latency was appearing on the first hop to the TPG gateway from my router.. and only in the evening.. by 22:30 the issue was going away.
They wanted me to run various traceroutes, but the main one to TPG.COM.AU, to show that the latency appeared within the tpg network... which it clearly did as it was the first hop... my gateway..

Multiple times I provided them the same information, and then it took some convincing to get access to an engineer... although I am still waiting on that call... over a week later...

So.. I then decided I wanted to automate this to run a constant ping and regular tracerts.. which were timestamped...

I already have a page that detailed how to created a timestamped ping... so I used this as the basis o a couple of scripts I would schedule.

The ping would run hourly, running a ping for 59 minutes... (3540 successfull pings (1 ping a second)) (there are issues with this and it occurs with timeouts...as they are 4 seconds..).. However this will be better than not having it at all, and potentially something I could fix.

The tracert would run every 5 minutes, with dnslookup enabled to ensure the host details were recorded..

Scripts are below but logging and stdout look like the following, the logs will be created in the same folder as the script and with a timestamped name. ping

2020-05-20 20:36:01 :  2020-05-20T20:36:01.9175593+10:00: 
2020-05-20 20:36:01 :  2020-05-20T20:36:01.9378688+10:00: Pinging tpg.com.au [203.26.27.38] with 32 bytes of data:
2020-05-20 20:36:02 :  2020-05-20T20:36:02.0514975+10:00: Reply from 203.26.27.38: bytes=32 time=134ms TTL=125
2020-05-20 20:36:03 :  2020-05-20T20:36:03.0439801+10:00: Reply from 203.26.27.38: bytes=32 time=125ms TTL=125
tracert

2020-05-20 22:03:01 :  2020-05-20T22:03:01.8844980+10:00: 
2020-05-20 22:03:01 :  2020-05-20T22:03:01.9053306+10:00: Tracing route to tpg.com.au [203.26.27.38]
2020-05-20 22:03:01 :  2020-05-20T22:03:01.9083060+10:00: over a maximum of 30 hops:
2020-05-20 22:03:01 :  2020-05-20T22:03:01.9112846+10:00: 
2020-05-20 22:03:06 :  2020-05-20T22:03:06.4692141+10:00:   1    <1 1.1.1.1="" 1="" 2-2-2-2-static.tpgi.com.au="" 2020-05-20="" 2020-05-20t22:03:12.1730325="" 2020-05-20t22:03:13.2970152="" 2020-05-20t22:03:14.4112638="" 2020-05-20t22:03:15.6023222="" 2020-05-20t22:03:16.7043576="" 2020-05-20t22:03:17.8930681="" 2020-05-20t22:03:17.8970367="" 2020-05-20t22:03:17.9005089="" 22:03:12="" 22:03:13="" 22:03:14="" 22:03:15="" 22:03:16="" 22:03:17="" 2="" 3-3-3-3-s.tpgi.com.au="" 37="" 3="" 4-4-4-4-s.static.tpgi.com.au="" 41="" 43="" 4="" 5-5-5-5-s.tpgi.com.au="" 53="" 54="" 55="" 56="" 57="" 58="" 59="" 5="" 60="" 6="" 77="" 78="" 79="" 7="" 80="" 85="" 86="" :="" code="" complete.="" gateway="" ms="" router="" trace="" www.tpg.com.au="">
And here are both scripts...

ping (pingTPGwithTimestamp.ps1)

3 input parameters
  • $pingDestination (default tpg.com.au) 
  • $pingAmount (default 4) 
  • $Logging (switch) 

Here is the command I run
".\pingTPGwithTimestamp.ps1" -logging -pingDestination "tpg.com.au" -pingAmount 3540

How to configure Taskscheduler to run the powershell script
Program\Script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe 
Add arguments: -ExecutionPolicy Bypass -WindowStyle Minimized -File ".\pingTPGwithTimestamp.ps1" -logging -pingDestination "tpg.com.au" -pingAmount 3540 
Start in: C:\Powershell\~Home\TPG (do not put quotes around this it will fail to run)



[CmdletBinding()]
param( 
        [Parameter(Mandatory=$false)] [AllowEmptyString()] [string] $pingDestination ='tpg.com.au',
        [Parameter(Mandatory=$false)] [AllowEmptyString()] [int16] $pingAmount = 4,
        [switch] $Logging
    )

$FormatENumerationLimit=-1;

function timestamp ($log)
{
    Process {
            $resultPing = "$(Get-Date -Format o): $_";
            write-host $resultPing;
            if ($Log)
            {
                LogWrite $resultPing;
            }
    }
}


function Get-ScriptDirectory
{
 #Determine the folder in which the script lives.
 $Invocation = (Get-Variable MyInvocation -Scope 1).Value
 Split-Path $Invocation.MyCommand.Path
}

#needed to find the location of the script.
$scriptPath = Get-ScriptDirectory;
$scriptName = $MyInvocation.MyCommand.Name;

$LogfileTimeStampName = (Get-Date).toString("yyyyMMddHHmmss");
$Logfile = "$scriptPath\$($scriptName)_$LogfileTimeStampName.log";

Function LogWrite
{
   Param ([string]$logstring);
   $LogStamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss");
   Add-content $Logfile -value "$LogStamp :  $logstring";
}

PING.EXE $pingDestination -n $pingAmount | timestamp($logging.IsPresent)

tracert(tracertTPGwithTimestamp.ps1)

2 input parameters
  • $dnslookup (switch) 
  • $Logging (switch) 
Here is the command I run
".\tracertTPGwithTimestamp.ps1" -logging  -dnslookup

How to configure Taskscheduler to run the powershell script
Program\Script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe 
Add arguments: -ExecutionPolicy Bypass -windowstyle Minimized -File ".\tracertTPGwithTimestamp.ps1" -logging  -dnslookup
Start in: C:\Powershell\~Home\TPG (do not put quotes around this it will fail to run)




[CmdletBinding()]
param( 
        [Parameter(Mandatory=$false)] [AllowEmptyString()] [string] $tracertDestination ='tpg.com.au',
        [switch] $dnslookup,
        [switch] $Logging
    )

$FormatENumerationLimit=-1;

function timestamp ($log)
{
    Process {
            $resulttracert = "$(Get-Date -Format o): $_";
            write-host $resulttracert;
            if ($Log)
            {
                LogWrite $resulttracert;
            }
    }
}


function Get-ScriptDirectory
{
 #Determine the folder in which the script lives.
 $Invocation = (Get-Variable MyInvocation -Scope 1).Value
 Split-Path $Invocation.MyCommand.Path
}

#needed to find the location of the script.
$scriptPath = Get-ScriptDirectory;
$scriptName = $MyInvocation.MyCommand.Name;

$LogfileTimeStampName = (Get-Date).toString("yyyyMMddHHmmss");
$Logfile = "$scriptPath\$($scriptName)_$LogfileTimeStampName.log";

Function LogWrite
{
   Param ([string]$logstring);
   $LogStamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss");
   Add-content $Logfile -value "$LogStamp :  $logstring";
}

if ($dnslookup)
{
    tracert.exe $tracertDestination | timestamp($logging.IsPresent)
}
else {
    tracert.exe -d $tracertDestination | timestamp($logging.IsPresent)
}


Share/Bookmark

Tuesday, November 13, 2018

Run TimeStamped Pings against large Computer list

Had to run long term ping test against a number of devices. As such I created the following script that will ping the devices, with a timestamp, logging to a file.

computer list is in a file, should be in the same folder as the script. In this case file called ips.txt.

param ([ Int32 ] $ PingCount = 14400 , [ string ] $ file = '.\ips.txt' )

 

function Get-ScriptDirectory

{

    #Determine the folder in which the script lives.

    $ Invocation = ( Get-Variable MyInvocation -Scope 1 ).Value

    Split-Path $ Invocation .MyCommand.Path

}

 

$ scriptPath = Get-ScriptDirectory

 

$ TimeStampPing = {

    param ( $ comp , $ count , $ scriptPathBlock )

    filter timestamp { " $ ( Get-Date - Format o): $ _ " } ;

    ping $ comp -n $ count | timestamp > " $ ( $ scriptPathBlock )\pingtest- $ ( $ comp ).txt"

  }

 

foreach ( $ line in Get-Content $ file ) {

    Start-Job -ScriptBlock $ TimeStampPing -ArgumentList $ line , $ PingCount , $ scriptPath

}


Share/Bookmark

Monday, November 12, 2018

Powershell: Post code to blogger from Visual Studio Code

Using the blogger web tool, have been able to paste from VSC to web tool and it will colour code.

Just need to jump to HTML view and change the style paramters of the div's from

 white-space: pre;

to

 white-space: pre-wrap;

which will allow for the code to wrap.


OLD: Notes for myself, as I forget by the time I write something new.Smile

Using Visual Studio Code to create Powershell code.

You can copy and paste to word and the formatting is maintained.

Save the file as an rtf file.

Use an rtf converter to convert to HTML. I use the MultiDOC Converter (freeware).

I then paste this HTML code into Windows Live Writer.

Note: If you go from the edit tab, then live writer will change the style class to lower case, and then they wont match the uppercase class names in the code.

So to publish just paste the style block into the code page, just prior to publish.

Reference:

http://www.multidoc-converter.com/en/index.html


Share/Bookmark

Powershell: Cloudflare List all domains and search for ip

Quick Note: This code is quickly put together to prompt for the Cloudflare API key and email, then will list all the domains in cloudflare and also prompt for an ip to search for within all domains. Please ignore the debugging and extra code

$ CloudflareAPIKey = Read-Host -Prompt 'Enter the API Key' ;

$ CloudflareAPIEmail = Read-Host -Prompt 'Enter the API email e.g. user@domain' ;

$ headers = @ {

    "X-Auth-Key" = $ CloudflareAPIKey ;

    "X-Auth-Email" = $ CloudflareAPIEmail ;

    "Content-Type" = 'application/json'

}

 

#this may need changing when Cloudflare update the api

[ string ] $ cloudFlareApiBaseUrl = "https://api.cloudflare.com/client/v4"

 

#Force TLS version to be 1.2 (cloudflare only allows tls1.2 now)

[ Net.ServicePointManager ]::SecurityProtocol = [ Net.SecurityProtocolType ]::Tls12

 

# Get Zones from cloudflare

$ request = Invoke-WebRequest -Uri " $ { cloudFlareApiBaseUrl }/zones" -Method "GET" -Headers $ headers

$ CloudflarePagedDomains = ConvertFrom-Json $ request .Content

[ int ] $ pageCount = $ CloudflarePagedDomains .result_info [ 0 ].total_pages

 

$ CloudflareAllDomains = @ ()

 

$ IpSearchValue = Read-Host -Prompt 'IP address to search for in Cloudflare' ;

 

write-host "List of all domains in cloudflare" -foregroundcolor "Yellow"

for ( $ i = 1 ; $ i -le $ pageCount ; $ i ++)

{

    $ Parameters = @ {

        page = $ i

      }

      $ request = Invoke-WebRequest -Uri " $ { cloudFlareApiBaseUrl }/zones" -Method "GET" -Headers $ headers -Body $ Parameters

      $ CloudflarePagedDomains = ConvertFrom-Json $ request .Content

    #loop through $request.result array...

    Foreach ( $ CloudflareDomain IN $ CloudflarePagedDomains .result ) {

        $ CloudflareAllDomains += $ CloudflareDomain .name ;

        write-host " $ ( $ CloudflareDomain .name ) `r " -foregroundcolor "DarkCyan"

    }

}

 

Foreach ( $ CloudflareDomain IN $ CloudflareAllDomains ) {

    $ request = Invoke-WebRequest -Uri " $ { cloudFlareApiBaseUrl }/zones/?name= $ { CloudflareDomain }" -Method "GET" -Headers $ headers

   

    $ zoneId = $ ( ConvertFrom-Json $ request .Content ).result[ 0 ].id

    write-host "Searching $ (( $ CloudflareDomain ).padright( 75 )) $ ( $ zoneId )" -foregroundcolor "Yellow"

    $ Parameters = @ {

        type = 'A'

        content = $ IpSearchValue

      }

    $ request = Invoke-WebRequest -Uri " $ { cloudFlareApiBaseUrl }/zones/ $ { zoneId }/dns_records" -Method "GET" -Headers $ headers -Body $ Parameters

 

    Foreach ( $ RecordFound IN $ ( ConvertFrom-Json $ request .Content ).result) {

        write-host " $ (( $ RecordFound .name ).padright( 75 )) $ ( $ RecordFound .content )" -foregroundcolor "Green"

    }

}

 

 


Share/Bookmark

Tuesday, June 19, 2018

I don’t like mondays… Powershell: Move specific files

Recently had to move files from an archive location. THe archive location was a reasonable large folder structure containing around 60000 files of various sizes (between 1Kb to 100GB+).
The archive was to be cleaned out leaving only files created on a Monday. I won’t go into why this was the case.
Came up with the following script, which works pretty well.


$SourceRootPath = '\\server01\sourceRootFolder\'
$DestinationRootPath = '\\server02\DestinationRootFolder\DesitnationSubFolder'
Set-Location $SourceRootPath

$Count = 0;

#Replicate source folder structure in destination folder
Copy-Item $SourceRootPath $DestinationRootPath -filter {PSIContainer} -Recurse -Force

$allbackups = get-childitem $SourceRootPath -Recurse -filter '*.bak' | Where-Object {!$_.PSIsContainer -and $_.lastwritetime.DayOfWeek -ne 'Monday'}

ForEach ($file in $allbackups)
{
    $Count++;
    $relativePath = Resolve-Path -Path $file.DirectoryName -Relative
    $DestinationPath = Join-Path $DestinationRootPath -childpath $relativePath.replace('.\','')
    Write-Progress -Activity "Moving  $($file.FullName)" -Status 'Progress ->' -PercentComplete (([int]($Count / $allbackups.Count)) * 100)
    Move-Item -Path $file.FullName -Destination $DestinationPath
}


Share/Bookmark

Monday, May 07, 2018

Powershell: Format Fixed Width String (-f format operator)

Had to build a table in a text format of a predetermined width. What I found is that the –f operator can assist with building this

I had a variable that I stored the text output in, and then appened the results to it.

Add table header to string variable

$StringOutput += "{0,-80}{1,10}{2,25}" -f 'Filename','Size (KB)','Date';

Then loop through results

foreach (item in list)

{

$sqlStringOutput += "{0,-80}" -f $item.Name + "{0,10}" -f ("{0:n2}" -f ($item.Length/1kb)) + "{0,25}" -f ("{0:yyyy-MM-dd HH:mm:ss}" -f ([datetime]::ParseExact($item.LastWriteTime,'MM/dd/yyyy HH:mm:ss',$null))) + $htmlbr;

}

Output

Filename                                                                         Size (KB)                     Date

filename1.bak                                                                         0.50      2018-04-22 22:37:40
filename2.bak                                                                         0.50      2018-04-23 22:39:35

So I have used different ways to build the string using formatting.

1. "{0,-80}{1,10}{2,25}"  - this is one format string, the first number 0,1,2 are the indexes of the following strings. the –80,10,25 are how wide the end string is (with padding), the – number means align left, positive align right.

2. I used multiple format strings then concatenate them together, I just found it easier to read (due to nested format strings on the numbers and dates.

References

https://blogs.technet.microsoft.com/heyscriptingguy/2013/03/11/understanding-powershell-and-basic-string-formatting/

https://ss64.com/ps/syntax-f-operator.html


Share/Bookmark

Thursday, May 03, 2018

Powershell: List basic path info for all folders recursively

Had the need to create one big list of all folders in a folder tree. I could get this using Get-Childitem but it broke it down into a nicely formatted output, which is not what I needed.

I wanted just one path per line.

I came up with the following. Initially the output was being truncated, so long paths were truncated with …

This truncate can be removed by setting the following variable to –1

$FormatEnumerationLimit=-1

Not sure why but $FormatEnumerationLimit didnot work, I have to use the ExpandProperty function of the select-object cmdlet.

Get-ChildItem -path \\server\rootfolderpath -Recurse | ?{ $_.PSIsContainer }| Select -ExpandProperty FullName | Format-table FullName > folders.txt


Share/Bookmark

Friday, April 28, 2017

AzureRM - Powershell Notes

Run POWERSHELL as admin

Get Verison of PowerShellGet

Get-Module PowerShellGet -list | Select-Object Name,Version,Path

What is PowerShellGet
https://blogs.msdn.microsoft.com/mvpawardprogram/2014/10/06/package-management-for-powershell-modules-with-powershellget/

I had to use –allowclobber to force the install.

Install-Module AzureRM (–allowclobber)

Load the module

Import-Module AzureRM

Login to azure

Login-AzureRmAccount

Get-AzureRmSubscription

Set-AzureRmContext -SubscriptionName "Example Subscription Two"

New-AzureRmResourceGroup -Name TestRG1 -Location "South Central US"

Get-AzureRmResourceGroup -ResourceGroupName TestRG1

Get-AzureRmResourceGroup
(returns all ResourceGroups)

References:

https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-3.8.0

https://docs.microsoft.com/en-us/powershell/azure/overview?view=azurermps-3.8.0

https://docs.microsoft.com/en-us/azure/azure-resource-manager/powershell-azure-resource-manager


Share/Bookmark

Tuesday, July 12, 2016

PowerShell: Force multiple user log off Terminal Services Sessions (RDP) - PSTerminalServices

We have a product that was using terminal services sessions to run multiple processes under multiple users. This was controlled by an overriding service.

This was working well but now and again we experienced an issue in that the service would fall over leaving some orphaned TS Sessions.

We have a batch script set to run on service failure to restart the service and send an alert message to admin staff. However this did not fix all issues, it appeared that it did not like the fact the sessions were left. We then came up with a small PowerShell script that utilises the PSTerminalServices module to log off all sessions with specific users.

Luckily the PowerShell command we used allowed for wildcards, our users were all named similarly.

  • usernameProcess1
  • usernameProcess2
  • usernameProcess3

We could then easily get the sessions for the users above and force logoff these.

Import-Module PSTerminalServices

#Get the local computername
[String]$computerName = $env:computername;

#Get all TS sessions with username beginning with usernameProcess*
$Sessions = Get-TSSession -UserName usernameProcess* -ComputerName $computerName;

ForEach ($session in $sessions)
{
    Stop-TSSession -Computername $computerName -ID $session.sessionid -Confirm:$false -force;
}

Share/Bookmark

Tuesday, June 14, 2016

Powershell: Ping with timestamp, logging and tail

I include a refernce to the page where I found out how to timestamp each ping request at the bottom of this article.

This code excerpt will timestamp each ping request, redirect it to a log file and then we can use the get-content command to look at the contents of the log file as it is written to (like tail in linux)

filter timestamp {"$(Get-Date -Format o): $_"}
ping servername -t | timestamp > c:\temp\logging.txt
#tail log
Get-Content c:\temp\logging.txt -wait

To tail in powershell

Get-Content c:\temp\logging.txt -Tail 10 –Wait

Returns last 10 lines,  -wait will then wait for new entries

References:

http://stackoverflow.com/questions/27361452/how-to-add-timestamps-to-individual-lines-of-powershell-output


Share/Bookmark

Wednesday, June 08, 2016

Powershell: Find and Kill defined processes owned by specific user.

[string]$userName = 'user1';
[string]$exeName = 'cmd.exe';
Get-WmiObject -Query "Select * from Win32_Process where name = "$exeName" | Select Name, Handle, @{Label='Owner';Expression={$_.GetOwner().User}} | Where-object {$_.Owner -like "$userName"} | Select-object @{Label='Handle';Expression={[int]$_.Handle}} |foreach { stop-process -id $_.Handle -force }

Share/Bookmark

Powershell: IIS add request filtering (CFIDE)

Recently had to add some requestfiltering deny rules to a  number of servers. This was because of a detected vunerabiltiy with Coldfusion and its admin portal.

So to achieve this quickly and consistently I developed the following powershell script.

It adds a deny rule at the server level and then an removes the rule for the CF administration portal site.

The first two lines ensure that the web administration tools are imported in all versions of powershell.

I like my config to be in the central applicationhost.config file, which runs less risk of developers overwriting the config in a web.config file. The line

-pspath 'MACHINE/WEBROOT/APPHOST'

ensures the config is saved in the applicationhost.config file.

–location "CFAdminPortal”

This line ensures that the config is applied to the site called CFAdminPortal

Add-PSSnapin WebAdministration
Import-Module WebAdministration
 
#Add requestfiltering
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/denyUrlSequences" -name "." -value @{sequence='/CFIDE'}
 
#Remove requestfiltering  for the CFIDE site
Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' –location "CFAdminPortal" -filter "system.webServer/security/requestFiltering/denyUrlSequences" -name "." -AtElement @{sequence='/CFIDE'}

Share/Bookmark

Powershell: Cloudflare autoupdate public IP and send SMS

[2020-12-24 Edit-start]
Two things here - 

1. Cloudflare now allows for access tokens which you can lock down to specific areas and actions.. like dns zones and read\edit. So its better to now use these than the global api key that was only available when I wrote this article. You will need to create the access token in cloudflare and then change the $header variable to something like this.

$headers = @{
    "Authorization" = 'Bearer <access token>';
    "Content-Type" = 'application/json'
}

2. Also now with the demise of internet explorer I have found that to use the Invoke-WebRequest
you have to add -UseBasicParsing to the command. So it will read like this.

$request = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/?name=${Zone}" 
-Method 'GET' -Headers $headers -UseBasicParsing

[2020-12-24 Edit-end]

We had a connection that ran our company VPN. The VPN was available on a couple of URLs. This connection also had a backup connection, so if the primary link failed it would swap to the backup. However this would change the IP address.
I wanted to make it so that the when the connection failed over the VPN URLs were updated.
The script below achieves that by updating the DNS records in cloudflare with the new public IP of the connection.
It looks at the public IP and then compares it to the record in dns, if the same do nothing. If different then update and send SMS
The SMS api maybe different for you depending on the company you use.
 
function Get-ScriptDirectory
{
    #Determine the folder in which the script lives.
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
}
#[string]$CRLF
#needed to find the location of the script.
$scriptPath = Get-ScriptDirectory

[String]$scriptCurrentDateTime = Get-Date -format "yyyyMMddHHmmss"

$headers = @{
        'X-Auth-Key' = '<auth-key>';
        'X-Auth-Email' = '<email>';
        'Content-Type' = 'application/json'
    }

[string]$urlRequest = 'https://api.smscompanyurl.com/api-adv.php?';
[string]$smsUsername = '<username>';
[string]$smsPassword = '<password>';
[string]$smsNumbers = '<phonenumber1>,<phonenumber2>,<phonenumber3>';
[string]$smsFrom = '<Company Name>';
[string]$smsMessage = [uri]::EscapeDataString('Office IP Address has changed. Attempting auto update.');



$ZoneRecords = @(
'temp.contoso.com',
'tempytemp.contoso.com'
)

[string]$Zone = 'contoso.com';
[string]$cloudFlareApiBaseUrl = 'https://api.cloudflare.com/client/v4';
[string]$public_IP='';
[string]$public_IP='';


# Get Zone ID from cloudflare
    try 
    {
        $request = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/?name=${Zone}" -Method 'GET' -Headers $headers
    }
    catch
    {
        $MyError = $_
        Throw $MyError
    }
    $zoneId = $(ConvertFrom-Json $request.Content).result[0].id


#Lookup external ip
    try 
    {
        $public_IP = (Invoke-RestMethod https://api.ipify.org?format=json).ip.trim()
    }
    catch
    {
        $MyError = $_
        Throw $MyError
    }
     

Foreach ($Element IN $ZoneRecords) {

    #GET record info from cloudflare
    #$RecordVPNweb = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/${ZoneId}/dns_records/?name=${Element}" -Method 'GET' -Headers $headers
    try 
    {
        $RecordVPN = Invoke-RestMethod -Uri "${cloudFlareApiBaseUrl}/zones/${ZoneId}/dns_records/?name=${Element}" -Method 'GET' -Headers $headers -ContentType 'application/json'   
    }
    catch
    {
        $MyError = $_
        Throw $MyError
    }
    
    #When using webrequest need to convert from jason
    #$RecordVPN_ID = $(ConvertFrom-Json $RecordVPN.Content).result[0].id
    #$RecordVPN_IP = $(ConvertFrom-Json $RecordVPN.Content).result[0].content
    $RecordVPN_ID = $RecordVPN.result[0].id
    $RecordVPN_IP = $RecordVPN.result[0].content
    
    if ($RecordVPN_IP -ne $public_IP) {

        $smsMessage = $smsMessage + [uri]::EscapeDataString("Changing from $RecordVPN_IP to new $public_IP");
        $queryString = "username=$smsUsername&password=${smsPassword}&to=${smsNumbers}&from=${smsFrom}&message=${smsMessage}";
        ${urlRequest}+${queryString}
        [string]$textfileName = "$scriptPath\IPChanged$scriptCurrentDateTime.txt"
        
        try 
        {
            #send sms
            [String]$scriptIPChangeDateTime = Get-Date -format "yyyy-MM-dd HHmmss"
            [string]$textfileContent = "IP Change attempted at $scriptIPChangeDateTime : ${urlRequest}+${queryString}"
            out-file -filepath $textfileName -inputobject $textfileContent -encoding ASCII
            Invoke-RestMethod -Uri "${urlRequest}+${queryString}" -Method 'GET';   
        }
        catch
        {
            $MyError = $_
            Throw $MyError
        }

        $Data = @{
        'id' = "${RecordVPN_ID}";
        'type' = 'A';
        'name' = "${Element}";
        'content' = "${public_IP}";
        }
    
        # If it exists, UPDATE (PUT), if not, CREATE (POST)
        [string]$method = 'PUT';
        if ($RecordVPN.result.Count -eq 0) {
            $method = 'POST';
            $Data.Remove('id');
        }

        $DataJson = ConvertTo-Json $Data

        try 
        {
            $JSONResponse = Invoke-RestMethod -Uri "${cloudFlareApiBaseUrl}/zones/${ZoneId}/dns_records/${RecordVPN_ID}/" -Headers $Headers -Body $DataJson -Method "${method}" -ContentType 'application/json' -ErrorAction Stop
        }
        catch
        {
            $MyError = $_
            Throw $MyError
        }
       
    }
}

Share/Bookmark

Tuesday, June 07, 2016

Cloudflare API delete multiple records via Powershell

Recently whilst setting up cloudflare DNS, I had to delete a lot of records. With the API you can only do it one at a time. With thousands of records to remove I put together a little script tot do this.

Please ensure you fully test what you are doing with this, as I take no responsibility, but it worked for me.

You will various things to make this work, and I will assume you know how to use the API, by supplying your API key and email etc….

The script below can, with a small change, either read in the records to delete from a file or you can manually create the array in the script.

The script also contains output debugging that I left in.

Hope it helps somebody Smile

$headers = @{
"X-Auth-Key" = '<you need to get this from cloudflare>';
"X-Auth-Email" = '<replace this with email address>';
"Content-Type" = 'application/json'
}
#this may need changing when Cloudflare update the api
[string]$cloudFlareApiBaseUrl = "https://api.cloudflare.com/client/v4"
#file with records in it
[string]$recordlist= 'c:\path\fileRecordList.txt';
#dns domain to use
[string]$Zone = "contoso.com";

#Manually create array, add records here to delete.
<#
$ZoneRecords = @(
'example.contoso.com',
'example.subdomain.contoso.com'
)#>


#Create array from file, records to delete.
$ZoneRecords = Get-content -Path ".\${recordlist}"

# Get Zone ID from cloudflare
$request = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/?name=${Zone}" -Method "GET" -Headers $headers
$zoneId = $(ConvertFrom-Json $request.Content).result[0].id

Foreach ($Element IN $ZoneRecords) {
#debug
$Element

#GET record info from cloudflare
$Record = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/${ZoneId}/dns_records/?name=${Element}" -Method "GET" -Headers $headers
$RecordID = $(ConvertFrom-Json $Record.Content).result[0].id
$RecordName = $(ConvertFrom-Json $Record.Content).result[2].id

#debug
$Record

#DELETE record info from cloudflare
$RecordDelete = Invoke-WebRequest -Uri "${cloudFlareApiBaseUrl}/zones/${ZoneId}/dns_records/${RecordID}" -Method "DELETE" -Headers $headers

#debug
$RecordDelete
'RecordName:' + $RecordName;
'RecordID:' + $RecordID;
}

The format of the text file is just a flat text file

test.qa.contoso.com
test.demo.contoso.com
test.demo.contoso.com
test.contoso.com
test1.contoso.com
test2.contoso.com
test.qa2.contoso.com
test2.qa.contoso.com
test.training.contoso.com
test3.contoso.com
test4.contoso.com

Share/Bookmark