TNSNames File Parsing

If you’ve ever worked with  Oracle you are familiar with Oracle’s TNSNAMES file. This file describes how to get to a database.   With ODP.Net it doesn’t provide a means to parse the TNSNAMES.ora file and then in turn use it with ODP.Net.  From everything I’ve read you must just copy from the Description() and put Data Source = Description(). Then you can use that as a means to connect to your Oracle Database server.    With that in mind I set out to write some scripting to help with this problem.

The first thing I did was to follow this great article by the Scripting Guys about how to use ODP.NET.  After reading that article I found a great module on the Gallery that implemented much of what is spoken about there and I’ll be using that module here in this posting (SimplySQL)

Now I know where my  TNSNAMES.ora file is located so I’ll bring it into my session with:

$tnsnamesPath = 'c:\tns\tnsnames.ora'
$tn = get-content $tnsnamesPath -raw

I brought the file in -raw so that I knew I would have a full object.  Now with some REGEX I can get this file in to the fashion I want.  First to look for the common string in my TNSNAMES.ora file somename = (Description = . 

$parsedTN = $tn -replace '(.*\=.*|\n.*\=)(.*|\n.*)\(DESCRIPTION*.\=' ,'Data Source = (DESCRIPTION ='

Now that I have the connection Name replaced with Data Source = I can now split it into an array and then select my connection based on that array:

$splitTN = $parsedTN -split '(?=.*Data Source = \(DESCRIPTION \=)' 

Data Source = (DESCRIPTION =
 (ADDRESS = (PROTOCOL = TCP)(HOST = server3)(PORT = 1521))
 (ADDRESS = (PROTOCOL = TCP)(HOST = server58)(PORT = 1521)))

Now that  I have the connections split into an array I can now select the one I want using Where-Object -like “myconnectionName“.  Then with this handy commandlet Open-OracleConnection From this module (simplySQL) , all i have to do next is pass in my username and password and that should open My oracle connection.

$tnsnames = $splitTN |?{$_ -like "*$connectionName*"}
$connstring = "$tnsnames;User Id=$username;Password=$password"
Open-OracleConnection -ConnectionString $connstring -ConnectionName $connectionName

Below is the full script in a GitHub Gist:

I hope this helps someone.


Until then


Keep scripting





[QuickScript] Find out what that MAC is

I wanted to find out each mac address was on my router.  So I decided to find out what was available for a given IP Address.  What I found was there is an API that you can query to Get information about what company owns that mac address.

Now to see how we query and get that information from the API:

According to the site:

we only need to Query the api and pass it a mac address and then pass in the url either JSON or XML:

invoke-restmethod -uri | select result

@{company=Belkin International Inc.; mac_prefix=58:EF:68; address=12045 East Waterfront Drive,Playa Vista 90094,U...

Without a json Tag

(invoke-restmethod -uri

company : Apple, Inc.
mac_prefix : 7C:01:91
address : 1 Infinite Loop,Cupertino CA 95014,US
start_hex : 7C0191000000
end_hex : 7C0191FFFFFF
country : US
type : MA-L

Telling the API to return XML

(invoke-restmethod -uri

company : Belkin International Inc.
mac_prefix : 58:EF:68
address : 12045 East Waterfront Drive,Playa Vista 90094,US
start_hex : 58EF68000000
end_hex : 58EF68FFFFFF
country : US
type : MA-L

As you can see getting the results already comes in Json and or form of an object so getting this with PowerShell is pretty straightforward.


Hope this helps Someone


Until then


Keep Scripting



DacPac to Folders with PowerShell

A Question was posed on Stackoverflow.  How do you create a folder structure from a created DacPac or BacPac.  This article is how I went about doing this with PowerShell.

The first means was to try and find someone who’d tried this before. The best I could find searching was this article: Deploy DACPACs with PowerShell.  This Script does an excellent job of showing how you can use the SQL 2014 DLL’s to create a Script that can than be deployed to your database.   This did not answer the question though.

Thankfully one of the participants in the question was kind enough to show how to do this very thing in C# so I took their pseudo code and turned it into PowerShell Code.

using (TSqlModel modelFromDacpac = new TSqlModel(dacpacPath))
 IEnumerable<TSqlObject> allObjects = model.GetObjects(QueryScopes);
 foreach (TSqlObject tsqlObject allObjects)
 string script;
 if (tsqlObject.TryGetScript(out script))
 // Some objects such as the DatabaseOptions can't be scripted out.

// Write to disk by object type
 string objectTypeName = tsqlObject.ObjectType.Name;
 // pseudo-code as I didn't bother writing.
 // basically just create the folder and write a file
 this.WriteToFile(objectTypeName, tsqlObject.Name + '.sql', script;


Starting at the top of the script I need to translate the using Statement into a New-object in PowerShell. In order to do that I needed to find what Dot net class TSQLModel was in.  Based on that research I found that I needed to add the type to my session(Microsoft.SqlServer.Dac.Extensions.dll).  Once the type was added I was then able to get the model from my DacPac.


add-type -path 'C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\Microsoft.SqlServer.Dac.Extensions.dll'

$model =[Microsoft.SqlServer.Dac.Model.TSqlModel]::new(((get-item ".\$dacpac").fullname))


Now that I have the Model of my dacpac I need to figure out how to make this into a PowerShell piece of code IEnumerable<TSqlObject> allObjects = model.GetObjects(QueryScopes);

I know my return type is IEnumerable Tsql object.. Now the question is how do I query my model and get that return object.  Based on the C# code I need to call GetObjects. GetObjects expects you to pass an object type (QueryScopes variable) and optionally you  can pass the Object Identifier ID or the Object type.   The queryScopes is an enumeration that has the following values(All, Builtin, Default, None, SameDatabase, System, UserDefined). I chose the All so I could see what this method would return.


$returnObjects = $model.GetObjects([Microsoft.SqlServer.Dac.Model.DacQueryScopes]::All)

Now the next step is to step through the return result and test each item to see if it can be scripted.  When calling the TryGetScript it has an output so we must declare a variable before we call this method.


$s = ''
foreach($r in $returnObjects)
 if ($r.TryGetScript([ref]$s))
 $objectTypeName = $r.ObjectType.Name;
 if(!(test-path $d ))
 new-item $d -ItemType Directory
 $filename = "$d\$($r.Name.Parts).sql"

 if(! (test-path $filename))
 {new-item $filename -ItemType File}
 $s | out-file $filename -Force
 write-output $filename



I found when I ran this that it would error on creation of a directory.

dactest.ps1 (28, 10): ERROR: At Line: 28 char: 10
ERROR: + $s | out-file $filename -Force
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : OpenError: (:) [Out-File], NotSupportedException
ERROR: + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand

In order to find this exception I decided to put a Try Catch around the act of creating the file:

 new-item $filename -ItemType File
 "Filename error $filename"



What I found after putting the Try catch in place was that the exception was be cause the object names were a Url:

Filename error c:\temp\db\Service\

In order to fix this I implemented a test to see if the item name was a url

[system.uri]::IsWellFormedUriString(‘’, [system.uri]::IsWellFormedUriString)

Now that I know that the filename is a uri I can parse and get the last item in the uri for the filename:

$url = "$($r.Name.Parts)"
 if ([system.uri]::IsWellFormedUriString($url, [system.urikind]::Absolute))
 $u = ([uri]"$url").Segments[-1]
 $filename = "$d\$u.sql"
 new-item $filename -ItemType File -ErrorAction Stop -Force

Example output

Directory: C:\temp\db

Mode LastWriteTime Length Name
—- ————- —— —-
d—– 3/5/2018 7:10 PM Assembly
d—– 3/6/2018 9:04 AM Contract
d—– 3/5/2018 7:18 PM DataType
d—– 3/5/2018 7:19 PM Endpoint
d—– 3/5/2018 7:19 PM Filegroup
d—– 3/6/2018 9:04 AM MessageType
d—– 3/5/2018 7:20 PM Queue
d—– 3/5/2018 7:20 PM Role
d—– 3/5/2018 7:20 PM Schema
d—– 3/6/2018 9:04 AM Service
d—– 3/5/2018 7:20 PM Table
d—– 3/5/2018 7:20 PM User
d—– 3/5/2018 7:20 PM UserDefinedType
-a—- 3/5/2018 7:06 PM 0 [Microsoft.SqlServer.Types].sql

Directory: C:\temp\db\Schema

Mode LastWriteTime Length Name
—- ————- —— —-
-a—- 3/6/2018 9:11 AM 54 dbo.sql
-a—- 3/6/2018 9:11 AM 76 db_accessadmin.sql
-a—- 3/6/2018 9:11 AM 82 db_backupoperator.sql
-a—- 3/6/2018 9:11 AM 74 db_datareader.sql
-a—- 3/6/2018 9:11 AM 74 db_datawriter.sql
-a—- 3/6/2018 9:11 AM 70 db_ddladmin.sql
-a—- 3/6/2018 9:11 AM 82 db_denydatareader.sql
-a—- 3/6/2018 9:11 AM 82 db_denydatawriter.sql
-a—- 3/6/2018 9:11 AM 64 db_owner.sql
-a—- 3/6/2018 9:11 AM 80 db_securityadmin.sql
-a—- 3/6/2018 9:11 AM 58 guest.sql
-a—- 3/6/2018 9:11 AM 84 INFORMATION_SCHEMA.sql
-a—- 3/6/2018 9:11 AM 54 sys.sql



The entire script is posted in a gist:

The Power of the Round Table (AZ PowerShell)

Last night we had some technical difficulties with our user group and getting the broadcast and the speaker setup and going.    So we had to make up something good to talk about in the user group.

So what we ended up doing was what I like to call a round table.  In this discussion everyone relayed some of their successes with PowerShell.   This article is just a “glimpse’ of some of the tidbits I was able to capture during the meeting.

If you are anything like me you like things that you can do to help you remember a command you last typed or you like to bring back a command from history and modify it slightly and try again.   One of the users last night showed us this wonder full trick in powershell the #(tab).

The best way to show this feature is to tell you to use the Get-History cmdlet in PowerShell to show your last few Items you’ve typed.

PSGit:\> Get-History

Id CommandLine
-- -----------
2 h
3 $env:PSModulePath
4 $env:PSModulePath -split ';'
5 h
6 ($env:PSModulePath -split ';')[0]
7 ($env:PSModulePath -split ';')[1]
8 ($env:PSModulePath -split ';')[2]
9 h
10 get-process
11 h
12 get-service

I only show the history so there is context around what this little gem of a tip does. if I type #PS and then hit tab I’ll get each item from history that contains PS.  Demonstrated below:


Now for another tip that I found useful as well.  Have you ever wanted  to create a variable and have the output of the variable on the screen as well.  This can be done with Tee-Object but there is a much shorter method that one of the users in the AZ Powershell User group demonstrated:

PSGit:\> ($var = ($env:PSModulePath -split ';')[0])

PSGit:\> $var

Simply enclose your command in parens and you get the output in your variable and to your screen.

We had a great discussion about this post Merging hashtables. This spurned a discussion on a very cool means to copy your object intact to another object.  The participant in the user group informed me that he’d share his code with me.  When I receive it I’ll add to this Post.

Lastly another user demonstrated how they use data from the perfmon reliability counters that every windows machine has.   you can view those reliability counters through a simple command at your prompt:

perfmon /rel

Turns out these counters are part of WMI and you could drill into these to help you with diagnosing problems in your infrastructure through Powershell and WMI.

Here is a post from Richard Siddaway on how he used some of the items in the class that this provides:

It was a very fun Users group. If you are online or in Phoenix area Drop by and we’ll entertain you with a speaker or any of the great folks that attend.


Until then

Keep Scripting



UCS Director – Creating a Custom Workflow Task for PowerShell

If you are familiar with UCS director then you know you can create custom workfow tasks for anything that is JavaScript based.  I haven’t seen a means to do this for a PowerShell Script.  This is how I figured this out.

Note: Without the help of my co-worker  Don Reilly the task would have never worked.  He was able to find the correct methods to call for Director. 

First thing I did was to clone the in box PowerShell Task.

2018-01-05 09_11_59-Clipboard

That brings up a dialog to allow you to choose to clone from tasks that are already there

2018-01-05 09_15_01

Now that I have that cloned I can look at the contents of the javascript to find out how to call my script.   The script I chose to run is one from another community member.  His script gets the last error from the PowerShell agent.  I took his script and saved it to my PSA server in d:\director\powershell\director folder and am using this custom work flow task to call this when there is an error.

On to the rest of the setup.   Once  I cloned the script and then put in the code that I needed with the necessary input’s from the director custom task.  I found that the task wouldn’t run.  Researching further I found that when you clone the task the PowerShell Task itself is running differently than any other custom task.  You can see this behaviour in the screen shot below:

2018-01-05 09_23_22

So I then had to goto my resident expert Don Reilly who helped me with discovering the right controller to add to my custom workflow task.  I’ll step through in pictures what my work flow in UCS Director 6.5 looks like:

2018-01-05 09_26_58

2018-01-05 09_28_50

The input for tasks has one property that i chose to just enter in its values.  In the LOV values for OutputFormat I entered in the values of XML and JSON.

2018-01-05 09_28_50-2

2018-01-05 09_28_50-3

2018-01-05 09_28_50-4

2018-01-05 09_28_50-5

The method added for the controller is before martial with the following calls to the clopia libs:


var agentPairs = PSAgentTabularLOV.getAllPowerShellAgentsLOV();
page.setEmbeddedLOVs(id + ".psAgent", agentPairs);

Here is what my script code looks like in the custom task (Powershell is highlighted in blue in the Java script).

For Clarity here is the script$s = “D:\Director\powershell\director\Get-LastUCSDError.ps1”; if(test-path $s){. $s;}else{“Cannot find $s check path on PowerShell Agent Server”}

// Auto generated to code invoke following task
// Task Label:  Execute Native PowerShell Command
// Task Name:  Execute Native PowerShell Command

function Execute_Native_PowerShell_Command()
    var task = ctxt.createInnerTaskContext("Execute Native PowerShell Command");

    // Input 'Label', mandatory=true, mappableTo=
    task.setInput("Label", input.label);

    // Input 'PowerShell Agent', mandatory=true, mappableTo=gen_text_input
    task.setInput("PowerShell Agent", input.psAgent);

    // Input 'Hide Input in PSA, inframgr logs', mandatory=false, mappableTo=gen_text_input
    task.setInput("Hide Input in PSA, inframgr logs", input.isHideInput);

    // Input 'Hide Output in PSA, inframgr logs', mandatory=false, mappableTo=gen_text_input
    task.setInput("Hide Output in PSA, inframgr logs", input.isHideOutput);

    // Input 'Commands/Script', mandatory=true, mappableTo=gen_text_input
    //Changed the command to be hard coded to a script on the
	var command = '$s = "D:\\Director\\powershell\\director\\Get-LastUCSDError.ps1"; if(test-path $s){. $s;}else{"Cannot find $s check path on PowerShell Agent Server"}';
    task.setInput("Commands/Script", command);

    // Input 'Commands/Rollback Script', mandatory=false, mappableTo=gen_text_input
    task.setInput("Commands/Rollback Script", '');

    // Input 'Output Format', mandatory=false, mappableTo=
    task.setInput("Output Format", input.outputFormat);

    // Input 'Depth', mandatory=true, mappableTo=
    task.setInput("Depth", input.depth);

    // Input 'Maximum Wait Time', mandatory=true, mappableTo=
    task.setInput("Maximum Wait Time", input.maxWaitTimeMinutes);

    // Now execute the task. If the task fails, then it will throw an exception

    // Now copy the outputs of the inner task for use by subsequent tasks
    // Type of following output: gen_text_input

// Invoke the task
<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

Monitor your Connection to Internet – PowerShell

Recently I’ve been having issues with my internet connection.  So I decided to Write a script to monitor my connection and then record how long my connection drops to my Internet Service Provider.

To start this process out I had to make sure that I could ping the gateway of the adapter that I’m using to connect to the internet with.  So first step was to find my IP address and gateway.  I was able to do this using Get-NetIPConfiguration.

Get-NetIPConfiguration -InterfaceAlias 'vEthernet (ExternalSwitch)'

InterfaceAlias : vEthernet (ExternalSwitch)
InterfaceIndex : 11
InterfaceDescription : Hyper-V Virtual Ethernet Adapter
NetProfile.Name : Conn
IPv4Address :
IPv6DefaultGateway :
IPv4DefaultGateway :
DNSServer :

This told me my address and my Gateway.  So I put them in a variable:

$IP = (Get-NetIPConfiguration -InterfaceAlias 'vEthernet (ExternalSwitch)').ipv4address.ipaddress

$gateway = (Get-NetIPConfiguration).ipv4defaultGateway.nexthop

Now that I have them in a variable I can begin the process of  Pinging both addresses.  I chose to add this to a Function:

function Start-ConnectionMonitoring
param($isp, $gateway, $Logfile,[int]$Delay = 10,[Ipaddress] $adapter, [switch]$ispPopup, [switch]$gateWayPopup)
$spacer = '--------------------------'
if(!(Test-Connection $gateway -source $adapter -count 1 -ea Ignore))
get-date | Add-Content -path $Logfile
"$gateWay Connection Failure" |add-content -Path $Logfile
$outagetime = Start-ContinousPing -address $gateway -adapter $adapter -Delay $Delay
"Total Outage time in Seconds: $outageTime" | Add-Content -path $Logfile
New-PopupMessage -location $gateway -outagetime $outagetime
$spacer |add-content -Path $Logfile
if((!(Test-Connection $isp -Source $adapter -count 1 -ea Ignore)) -and (Test-Connection $gateway -count 1 -ea Ignore))
get-date | Add-Content -path $Logfile
"$isp Connection Failure" | Add-Content -Path $Logfile
$outagetime = Start-ContinousPing -address $isp -adapter $adapter -Delay $Delay
"Total Outage time in Seconds: $outageTime" | Add-Content -path $Logfile
New-PopupMessage -location $isp -outagetime $outagetime
$spacer|add-content -Path $Logfile
Start-Sleep -Seconds $Delay

In this function I have two Nested functions.  I’ll explain the first function(Start-ContinousPing).  If the connection/ping to either the local router ($gateway) or ($isp) fails then we call this function.  This puts the Ping/connection check in a loop until the connectivity comes back. At the end of the non connectivity the function passes back the seconds that we couldn’t reach that resource.

The Second function (New-PopupMessage) serves as a means to allow the user to choose whether or not they get a popup when there is  a period of no activity.  If the switch -ispPopup is set then when we have no connectivity to the ISP resource we’ll get a popup indicating no connection and how long the connection was out.

Finally we’ll look at the contents of the log:

12/27/2017 4:59:29 PM Connection Failure
Total Outage time in Seconds: 0.0380652
12/27/2017 4:59:33 PM Connection Failure
Total Outage time in Seconds: 0.0353273

As you can see the connection to my first gate way was out of .038 seconds. Also the connection to my provider was out for .035 seconds.

The entire script is located in this gist:

I Hope this helps someone.


Until then

Keep Scripting



Merging hashtables

Hash tables in PowerShell are very useful and can be used for a bunch of things.   Recently I had to use some code I found on StackOverflow to Merge hash tables. This post is about my experience and the really cool piece of code that iRon posted on StackOverFlow.

First I need to login to azure and find my application:



I see my resource in the Get-azureRmresource so now I know that I can query for it’s app settings using this command:

$myapp = Get-AzureRmWebAppSlot -resourcegroupname myresourcegroup -name myresourcename -slot production

This produces and object that contains all my web application settings in azure for my app in question.  The item i want to work on is the .siteconfig.AppSettings


This portion of the object will have the properties of the appsettings in the azure blade as shown below:


PS C:\Users\me&amp;gt; $myApp.siteconfig.AppSettings

Name Value


Now that I have the current version of what is in my application I now need to see what to do to put new settings in place and not wipe out any existing settings.  The cmdlet to do the addition is Set-AzureRmWebAppSlot.  After looking through the help I can see that I have a parameter that  I can pass for the settings i want called -appSettings.  It like most of the other settings require a hash table:  [[-AppSettings] <Hashtable>]

So The $myApp.SiteConfig.Appsettings is a list:

$appSettings = $Myapp.siteconfig.appsettings
&amp;nbsp;$appsettings -is [pscustomobject]

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True List`1 System.Object

$appsettings -is [hashtable]

This means I need to convert my object from List`1 to a hashtable so I’ll iterate through it and create a hashtable:

$appSettingsHash = @{}
&amp;nbsp;foreach($k in $appSettings) { $appSettingsHash[$] = $k.value }

Name Value
---- -----

&amp;nbsp;$appsettingshash -is [hashtable]


Ok now that I have my current settings in a hashtable I need to now work with entries that I want to add to a hashtable and then post it.

$appSettings ='{"AppSettings:testkey1": "45test","AppSettings:TestId": "This is a Test Key 28"}'

$newAppSettings = $appSettings | convertfrom-json 

$newAppSettingsHash = @{}
 $ | ForEach-Object { $newAppSettingsHash[$_.Name] = $_.Value }

$newappsettingsHash -is [hashtable]

This is where the magic of iRon‘s script comes into play. Since  I need to use this in a deployment from TFS I created the hashtable in Json Format first and then convert the Json format to a [hashtable]. Then I call iRon’s Script with the $newappSettingsHash and the $appsettingsHash. Now I have a merged hashtable that I can now update my application with.

Function Merge-Hashtables([ScriptBlock]$Operator) {
$Output = @{}
ForEach ($Hashtable in $Input) {
If ($Hashtable -is [Hashtable]) {
ForEach ($Key in $Hashtable.Keys) {$Output.$Key = If ($Output.ContainsKey($Key)) {@($Output.$Key) + $Hashtable.$Key} Else {$Hashtable.$Key}}
If ($Operator) {ForEach ($Key in @($Output.Keys)) {$_ = @($Output.$Key); $Output.$Key = Invoke-Command $Operator}}

$hashtable = $newAppSettingsHash, $appSettingsHash | Merge-Hashtables {$_[0]} 
$results = Set-AzureRmWebAppSlot -AppSettings $hashtable -name $website -ResourceGroupName $resourceGroup -slot $slot
$r = $results.SiteConfig.AppSettings
Write-Output $r

The really cool thing about the Merging of the hashtables function is that you can merge more than 2 hash tables.  See this comment from iRon about how it works:



Full code for this merge hashtable function against a azure application is below:

I hope that when you need to merge hashtables this article makes it a  bit easier for you.


Until then keep scripting



Uploading files to Azure Applications (kudu)

I needed to copy some content to my azure application that the Build and deploy that I constructed for it wouldn’t need to do every deploy every time.   So my quest began on how do I upload files to an Azure application.  The most common and recognized way of uploading files to azure applications is through webdeploy. I didn’t think I needed to package up and use webdeploy so I sought out a way to do this with PowerShell.  This post is about that pursuit.

Thanks to this article most of the work was done Copy files to Azure Web App with PowerShell and Kudu API.  All I needed to do was to put a loop around my file upload and use Octavie van Haaften‘s scripts.

So I started with get-childitem -recurse “$downloadFolder\content”.  Now that I had my content in a variable called $files I can put this in a foreach loop and use Octavie van Haaften‘s  Upload-FileToWebapp.

During the upload of the files I need to determine if the file from my local disk is a File or Directory.  I used the following classes to determine this:

[System.IO.DirectoryInfo] &  [System.IO.FileInfo]

If the item was a directory then I had to make the upload location match the location on disk.  I did this through a little bit of replacement logic and used the $kudufolder as my variable to use for the upload function from Octavie.

$kudufolder = ((($file.FullName).Replace($uploadfrom,'Content'))`
$kudufolder = "$kudufolder/"
Upload-FileToWebApp -resourceGroupName myresourcegroup`
-webAppName mywebapp -kuduPath $kudufolder

The same holds true for the upload of a file. The only difference between the file and the directory is the /. When you are uploading/creating a directory / to kudu means a directory.

$kudufile = ((($file.FullName).Replace($uploadfrom,'Content'))`
Upload-FileToWebApp -resourceGroupName myresourcegroup`
-webAppName mywebapp -localPath $file.FullName -kuduPath $kudufile

Here is the full script in the foreach loop with each check for a directory or file.

$downloadfolder = 'c:\temp\myAzureStorage'

$uploadfrom = "$downloadfolder\Content"

$files = get-childitem -Recurse "$downloadfolder\Content"

foreach($file in $files)
if($file -is [System.IO.DirectoryInfo])
$kudufolder = ((($file.FullName).Replace($uploadfrom,'Content')).replace('\','/')).trimstart('/')
$kudufolder = "$kudufolder/"
Upload-FileToWebApp -resourceGroupName myresourcegroup -webAppName mywebapp -kuduPath $kudufolder
elseif($file -is [System.IO.FileInfo])
$kudufile = ((($file.FullName).Replace($uploadfrom,'Content')).replace('\','/')).trimstart('/')
Upload-FileToWebApp -resourceGroupName myresourcegroup -webAppName mywebapp -localPath $file.FullName -kuduPath $kudufile

I hope this helps someone
Until then keep Scripting


Azure File Storage Download

If you have an Azure account and you want to download files out of azure storage either individually or a whole folder. This script is about how I was able to do this with Powershell.

First we need to login to azure and get a storage context.  The StorageContext will require a key.

Add-AzureRmAccount -credential (get-credential) -tenantid yourid

$key = (get-azurermstorageAccountkey -resourcegroupname myresourcegroup -name mystorageaccountName | where-object{$Psitem.keyname -eq 'key1'}).value

$storageContext = New-AzureStorageContext -StorageAccountName "mystorage" -StorageAccountKey $key

Now that we have the storage context and key. Now we need to find the files that are in our AZURE File storage.

$content = get-azurestoragefile -storageaccountname "mystorage" -storageAccountkey $key

If we look at the contents of our $content variable we should see something similar to this:


Now that we have the content in a variable now we can begin the process of figuring out how to download each file. To start with to download a single file we need to use get-azureStorageFileContent 

$content = get-azurestoragefile -storageaccountname "mystorage" `

-storageAccountkey $key

get-azurestoragefilecontent -sharename "myshare" -path $content[0].uri.localpath `

-replace "$($content[0]",'' -destination "c:\temp\" -context $storageContext

After much trial and error I found that in the object you get back from Azure there are two different Object types that you must check for:


and the other type is:


By the class names you can see that one is a file and the other is a Directory.  With that in mind now I can put this in a function that recursively calls itself to get all the contents.

function Get-AzureFiles
param([string]$shareName = 'mystorage', [object]$storageContext, [string]$downloadFolder, [string]$path)
$content = get-azurestoragefile -sharename $sharename -Context $storagecontext -path $path| Get-AzureStorageFile

foreach($c in $content)
$Parentfolder = $c.uri.segments[($c.uri.segments.count -2)] -replace '/','\'

if(!(test-path $destination))
{mkdir $destination}
$p = $c.uri.LocalPath -replace "$($" ,''
if(Get-AzureStorageFile -ShareName $ -path $p -Context $storageContext )
if($ -is [Microsoft.WindowsAzure.Storage.File.FileDirectoryProperties])
$d = [Microsoft.WindowsAzure.Storage.File.CloudFileDirectory]::new($c.uri.AbsoluteUri)
#Get-AzureStorageFileContent -directory $d -ShareName $ -Destination "$destination$($" -Context $storageContext #-Path $p
$path = $d.Uri.LocalPath -replace "/$sharename/" , ""
$dest = $path -replace '/','\'
"$($ is a directory -- getting $downloadfolder\$dest files"
{mkdir "$downloadfolder\$dest"}
Get-AzureFiles -shareName $shareName -storageContext $storageContext -path $path -downloadFolder "$downloadFolder\$dest"
elseif($c -is [Microsoft.WindowsAzure.Storage.File.CloudFile])
Write-Output "downloading --- $destination$($"
$destination = (($c.Uri.LocalPath -replace "/$sharename/" , "") -replace '/','\')
$dest = "$downloadFolder\$destination"
$de = $dest -replace $, ""
if(!(test-path $de))
mkdir $de
if(!(test-path $dest))
{Get-AzureStorageFileContent -ShareName $ -Path $p -Destination $dest -Context $storageContext }# -WhatIf}
Write-Output "already downloaded --- $dest"

Now if we call the function with we’ll get a downloaded copy of all the folders from the location that you specify in azure to your local host:

get-AzureFiles -sharename “myShare” -storageContext $storageContext -downloadfolder “C:\temp\azurefiles”

There you go now you have your files in a directory from azure. Stay tuned for my next article where I’ll show you how to upload these same files to an Azure application (kudu).

I hope this helps someone
Until then keep Scripting


Finding 500,401 Errors in IIS logs

If you’ve  ever had to troubleshoot issues with IIS you’ll known that you are often drawn to looking at IIS Logs.    This post is about a script that I modified to search IIS logs for specific errors.

To begin with I found this article on StackOverFlow that got me started on this path.

To begin with we need to get the webadministration module so we can get the name of the website we want to get the log file for:

Import-Module WebAdministration

$site = Get-Item IIS:\Sites\$website
$id = $
$logdir = "$($\w3svc$id"

Now that we have the  The $logdir (log directory) we can now put the rest of the file name together by getting the date:

$File = "$logdir\u_ex$(((get-date).adddays(-$days)).ToString("yyMMdd")).log"

Assuming you are using a logfile per day the name of the log file is:

u_ex(yyMMdd)  which should be something similar to this: u_ex170824.log

Now that we have our log file we need to strip off unnecessary lines.  Specifically the First three lines that start with #S, #D, or #V which ends up being the Sofware, Version and Date items at the top of the log.

Then we’ll need to Build the columns based on the #fields value in the Log. That way each field in the results can be arranged into columns so we can sort our data based on what the item in the field is.

The additional column we’ll add is the name of the log file.

$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }

$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")

$Columns += 'LogFile'

$Count = $Columns.Length

Now that we have the column titles now it’s time to filter out the log and only get the lines that have the value we want.  In my case I was searching for the Error code of 500.

$Rows = $Log | where {$_ -like "*$errorType 0 0*"}

Now that we have all the data we want in the row’s variable we can now construct a table of Columns and rows.

$IISLog = New-Object System.Data.DataTable "IISLog"

foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
if($ColumnName -eq 'LogFile')
{$AddRow.$ColumnName = $file }
else {$AddRow.$ColumnName = $Row[$i]}


Full function is below:

  function get-ErrorLogs
  param($website = 'myWebSite', $errorType = '500',[int] $days =0)

    Import-Module WebAdministration

    $site = Get-Item IIS:\Sites\$website
    $id = $
    $logdir = "$($\w3svc$id"

    $File = "$logdir\u_ex$(((get-date).adddays(-$days)).ToString("yyMMdd")).log"
    $Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
    $Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
    $Columns += 'LogFile'
    $Count = $Columns.Length
    $Rows = $Log | where {$_ -like "*$errorType 0 0*"}
    $IISLog = New-Object System.Data.DataTable "IISLog"
    foreach ($Column in $Columns) {
      $NewColumn = New-Object System.Data.DataColumn $Column, ([string])
    foreach ($Row in $Rows) {
      $Row = $Row.Split(" ")
      $AddRow = $IISLog.newrow()
      for($i=0;$i -lt $Count; $i++) {
        $ColumnName = $Columns[$i]
        if($ColumnName -eq 'LogFile')
        {$AddRow.$ColumnName = $file }
        else {$AddRow.$ColumnName = $Row[$i]}


Now that I have this in a full function I can just call it like this:

get-ErrorLogs -website “Website2” -errorType 500 -days 4

And get results similar to this:


I hope this helps someone
Until then keep Scripting