2

Now, before you throw out the super simple answer of Get-ChildItem -Recurse, here is my unique issue:

I am connecting remotely to Azure with Powershell, grabbing the site (slot) list, then looping over the sites and finding all images in the directory structure, using Kudu API. Since Kudu has no notion of recursion, I have to build my own recursive function to grab all images from the root directory, then recurse through all children and chidren's children, etc, to also find the image files in those directories.

Here is my code for connecting to Azure and grabbing the root directory:

function Get-AzureRmWebAppPublishingCredentials($resourceGroupName, $webAppName, $slotName = $null){
    if ([string]::IsNullOrWhiteSpace($slotName)){
        $resourceType = "Microsoft.Web/sites/config"
        $resourceName = "$webAppName/publishingcredentials"
    }
    else{
        $resourceType = "Microsoft.Web/sites/slots/config"
        $resourceName = "$webAppName/$slotName/publishingcredentials"
    }
    $publishingCredentials = Invoke-AzureRmResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force
        return $publishingCredentials
}


function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName, $slotName = $null){
    $publishingCredentials = Get-AzureRmWebAppPublishingCredentials $resourceGroupName $webAppName $slotName
    return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}

function Fill-MimeTypes(){
    return @("image/gif", "image/x-icon", "image/jpeg", "image/png", "image/tiff", "image/bmp")
}

$MimeTypes = Fill-MimeTypes
[System.Collections.ArrayList]$Directories = @()


#Login to Azure Account
Login-AzureRmAccount

#Get the Azure subscription
Select-AzureRmSubscription -SubscriptionName [Subscription Name]

#Get the resource group name
$resourceGroupName = [Resource Group Name]

#Get the WebApp name
$Resources = Find-AzureRmResource -ResourceType Microsoft.Web/sites -ResourceGroupNameContains $resourceGroupName

ForEach($Resource in $Resources)
{
    #Get the WebAppName
    $WebAppName = $Resource.Name

    #Now, get the publishing creds
    $publishingCredentialsHeader = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $WebAppName $null
    $ApiUrl = "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/"

    #Now get the list of files in the wwwroot directory
    $InitialList = Invoke-RestMethod -Uri $ApiUrl -Headers @{Authorization=$publishingCredentialsHeader} -Method GET -ContentType "application/json"

    ForEach($Item in $InitialList)
    {
        If($MimeTypes -Contains $Item.mime)       
        {
            #Add image file data to a collection
        }

        If ($Item.mime -eq "inode/directory")
        {
            #So, here is where I need to recurse...
            #The mime type of inode/directory means it's a directory ;)
            #I now need to call the Api again with the Url and get the contents of the current directory and rinse and repeat until done
            #But I cannot forget about the other directories in the root directory and their children.
        }
    }
}

How can I write the recursive function?

crackedcornjimmy
  • 1,972
  • 5
  • 26
  • 42
  • 1
    Up one.. Interested in how to do this without Get-ChildItem. A custom function could have extra parameters which would be very useful in the right situation – cet51 Aug 07 '17 at 16:22
  • 1
    You've demonstrated you know how to write and call functions, and you've written in comments for what you need to do - "*#Add image file data to a collection*" - I guess you know how to do this bit? "*#I now need to call the Api again with the Url*" - and this bit? So is your question really "explain recursion to me?" There are a bunch of recursive directory walk examples in various languages - https://stackoverflow.com/q/24783862/478656 or https://rosettacode.org/wiki/Walk_a_directory/Recursively - which do the recursive walk pattern one way or another – TessellatingHeckler Aug 07 '17 at 17:46
  • 1
    and the pattern is more or less what you wrote in your comments, e.g. function Recursive-Walk { Add files to collection; For each directory: Recursive-Walk $directory }; "*I cannot forget about the other directories in the root directory*" - you can't forget about them, they are held in the structure of the running code in the same way that `1..10 | foreach { process-number $_ }` doesn't forget about the numbers when it goes off to a function call – TessellatingHeckler Aug 07 '17 at 17:50

1 Answers1

2

I would write it as follows. Some is psuedo-code as I am not versed in PS syntax or keywords:

    function Collect-Files($apiUrl, $creds, $currentDir)
{
    $list = Invoke-RestMethod -Uri $apiUrl/$currentDir/ -Headers @{Authorization=$creds} -Method GET -ContentType "application/json"

    If($MATCHLIST -eq $null)
    {
        $MATCHLIST = @() #initialize array
    }


    ForEach($Item in $list)
    {
        If($MimeTypes -Contains $Item.mime)       
        {
            #Add image file data to a collection
            $MATCHLIST += $Item #add to array
        }

        If ($Item.mime -eq "inode/directory")
        {
            $nextDir = $Item.name
            $MATCHLIST = Collect-Files $apiUrl $creds $currentDir/$nextDir
        }
    }

    return ($MATCHLIST)
} 

Then, your previous code would call this function as follows:

    #Get the WebApp name
$Resources = Find-AzureRmResource -ResourceType Microsoft.Web/sites -ResourceGroupNameContains "Nav-Inventory"

ForEach($Resource in $Resources)
{
    #Get the WebAppName
    $WebAppName = $Resource.Name

    #Now, get the publishing creds
    $publishingCredentialsHeader = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $WebAppName $null
    $ApiUrl = "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/"

    #Now get the list of files in the wwwroot directory
    $InitialList = Invoke-RestMethod -Uri $ApiUrl -Headers @{Authorization=$publishingCredentialsHeader} -Method GET -ContentType "application/json"

    $MATCHES += Collect-Files $ApiUrl $publishingCredentialsHeader "wwwroot"
}

Basic recursion.

crackedcornjimmy
  • 1,972
  • 5
  • 26
  • 42
tpsands
  • 228
  • 1
  • 5