AzureToolsBD09.psm1

<# Functions in this module
    Get-AzVmAsgAssociation
    Get-AzPeeringType
    Find-AzRoleFromAction
    Get-AzAsgMembership
#>



function Get-AzVmAsgAssociation {
  <#
  .SYNOPSIS
    This command shows the association between the Azure VMs and any ASG they may be associated with
  .DESCRIPTION
    Because it is difficult to see which VMs are associated with ASGs in the Azure Portal, This
    command retrieves the inforation about which VMS are associated with which ASG and displays
    the information regarding ASG name, Network Interface Name, NIC Id and VM Name
  .PARAMETER AsgName
    You can specify which ASGs to match, if you do not select any, all ASGs will be assumed
  .NOTES
    Created By: Brent Denny
    Created On: 26-Jun-2024
  .EXAMPLE
    Get-AzVmAsgAssociation -AsgName ASG1
    This will display all Azure VMs that are associated with ASG1
  .EXAMPLE
    Get-AzVmAsgAssociation
    This will display all Azure VMs that are associated all ASGs
  #>

  [CmdletBinding()]
  Param ([string]$AsgName = '')
  
  try {
    if ($AsgName -eq '') {$AllAzAsgs   = Get-AzApplicationSecurityGroup}
    else {$AllAzAsgs = Get-AzApplicationSecurityGroup -Name $AsgName -ErrorAction Stop}
  }
  catch {
    Write-Warning "No ASG with the name of $AsgName can be found"
    break
  }
  $AllAzNics   = Get-AzNetworkInterface
  $AllAzVms    = Get-AzVm

  $Results = @()
  foreach ($Asg in $AllAzAsgs) {
    foreach ($Interface in $AllAzNics) {
      $InterfaceAssociatedAsgs = $Interface.IpConfigurations.ApplicationSecurityGroupstext | ConvertFrom-Json
      if ($Asg.Id -in $InterfaceAssociatedAsgs.Id) {
        $Results += [PSCustomObject]@{
          AsgName = $Asg.Name
          NicName = $Interface.Name
          NicId   = $Interface.Id
          VMName  = ($AllAzVms | Where-Object {
            $_.NetworkProfile.NetworkInterfaces.id -contains $Interface.Id
          }).Name
        }
      }
    }
  }
  return $Results
}

function Get-AzPeeringType {
  <#
  .SYNOPSIS
    Lists all of the Azure virtual networks peerings and determines their type
  .DESCRIPTION
    This cmdlet finds all of the virtual networks that have peerings and
    determines if the peering is a global or regional type of peering.
    It will also show Virtual Networks that do not have any peerings as
    well.
    There are restrictions on what you can do with a global peering
    so it is important to know which peering is what type.
    This command will prompt you if you need to login to Azure via a
    Connect-AzAccount command
    You cannot use Global VNet peering to communicate with VIPs of
    load balancers in another region. VIP communication requires source IP
    to be on the same VNet as the LB IP: Resources in one virtual network
    cannot communicate with the IP address of an Azure internal load balancer
    in the peered virtual network.
  .EXAMPLE
    Get-AzPeeringType
    This will show all peerings (Global, Regional and NoPeering).
  .EXAMPLE
    Get-AzPeeringType -PeeringFilter Global
    This will only show peerings of a Global type
  .EXAMPLE
    Get-AzPeeringType -PeeringFilter NoPeering
    This will only show VNets that do not have any peering configured
  .PARAMETER PeeringFilter
    This will filter the peerings so that either a single peering type
    is shown or all are shown. The values for the PeeringFilter are:
    Regional - Shows only Regional
    Global - Shows only Global
    All - Shows all types of peering
    NoPeering - Shows only VNets with no peerings
  .NOTES
    General notes
      Created by: Brent Denny
      Created on: 6 May 2020
      Last Modified: 1 Jul 2020
  #>

  [cmdletbinding()]
  Param(
    [ValidateSet('Regional','Global','All','NoPeering')]
    [string]$PeeringFilter = 'All'
  )
  try {Get-AzSubscription -ErrorAction Stop > $null}
  catch {Connect-AzAccount}
  try {
    $VNets = Get-AzVirtualNetwork -ErrorAction Stop
    foreach ($VNet in $VNets){
      if ($VNet.VirtualNetworkPeerings.Count -ge 1) {
        $Peerings = $VNet.VirtualNetworkPeerings
        foreach ($Peering in $Peerings) {
          $PeerID = $Peering.remotevirtualnetwork.Id
          $PeerName = $PeerID -replace '.+\/(.+)$','$1'
          $PeerVNetInfo = $VNets | Where-Object {$_.Id -eq $PeerID}
          $PeerVNetLocation = $PeerVNetInfo.Location
          if ($VNet.Location -eq $PeerVNetLocation) {$PeerType = 'Regional'}
          else {$PeerType = 'Global'}
          if ($PeeringFilter -eq $PeerType -or $PeeringFilter -eq 'All') {
            $Hash = [ordered]@{
              VNetName = $VNet.Name
              ResourceGroup = $VNet.ResourceGroupName
              VNetLocation = $VNet.Location
              PeeringVNet = $PeerName
              PeeringVNetLocation = $PeerVNetLocation
              PeeringType = $PeerType
              VNetID = $VNet.Id
            }
            New-Object -TypeName psobject -Property $Hash   
          }
        }
      }
      else {
        if ($PeeringFilter -eq 'NoPeering' -or $PeeringFilter -eq 'All') {
          $Hash = [ordered]@{
            VNetName = $VNet.Name
            ResourceGroup = $VNet.ResourceGroupName
            VNetLocation = $VNet.Location
            PeeringVNet = 'No Peerings'
            PeeringVNetLocation = 'N/A'
            PeeringType = 'N/A'
            VNetID = $VNet.Id
          }
          New-Object -TypeName psobject -Property $Hash    
        }       
      }  
    }
  }  
  catch { Write-Warning 'An error occured trying to access the Virtual Networks'}
}

function Find-AzRoleFromAction {
  <#
  .SYNOPSIS
    This command finds Azure Roles that contain an action or a devolved action
  .DESCRIPTION
    This command will find an Azure Role based on the Actions contained
    within it. It will also check the NotActions to make sure the requested
    action is not in this property. It will then devolve the action to include
    a wildcard and also a wildcard with the original action as follows:
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*
      Microsoft.Storage/storageAccounts/blobServices/containers/*/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/*
      Microsoft.Storage/storageAccounts/blobServices/*/delete
      Microsoft.Storage/storageAccounts/blobServices/*
      Microsoft.Storage/storageAccounts/*/delete
      Microsoft.Storage/storageAccounts/*
      Microsoft.Storage/*/delete
      Microsoft.Storage/*
    It will then show all of the roles that contain any of these actions
  .EXAMPLE
    Find-AzRoleFromAction -Action 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete'
   
    This will find all Roles that have this action and not have this action in the
    NotActions. If it cannot find the specific action, it will then devolove the
    action to be more broad in its search.
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*
      Microsoft.Storage/storageAccounts/blobServices/containers/*/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/*
      Microsoft.Storage/storageAccounts/blobServices/*/delete
      Microsoft.Storage/storageAccounts/blobServices/*
      Microsoft.Storage/storageAccounts/*/delete
      Microsoft.Storage/storageAccounts/*
      Microsoft.Storage/*/delete
      Microsoft.Storage/*
  .EXAMPLE
    Find-AzRoleFromAction -Action 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete' -DevolutionLevel 2
   
    This will find all Roles that have this action and not have this action in the
    NotActions. If it cannot find the specific action, it will then devolove the
    action to be more broad in its search but it will only devolve two levels.
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*
      Microsoft.Storage/storageAccounts/blobServices/containers/*/delete
      Microsoft.Storage/storageAccounts/blobServices/containers/*
  .PARAMETER Action
    This is the action that needs to be found within an existing Azure Role.
    The Actions are in this format:
    Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete
  .PARAMETER DevolutionLevel
    This instructs the command to only devole the permission a certian
    number of times, shown in example 2
  .NOTES
    Created By: Brent Denny
    Created on: 05-Aug-2024
  #>

  [cmdletbinding()]
  Param (
    [Parameter(Mandatory=$true)]
    [ValidatePattern('^[a-z]+(\.[a-z]+)+(\/.*)+$')]
    [string]$Action,
    [int]$DevolutionLevel = 0 
  )
  function Resolve-Actions {
    Param ($ResolveAction)
    $DataArray = $ResolveAction -split '\/'
    $MaxIndex = $DataArray.Count - 2
    $FirstPass = $true
    $Actions = foreach ($Index in ($MaxIndex..0)) {
      if ($FirstPass -eq $true) {
        $ResolveAction
        ($DataArray[0..$Index] -join '/') + '/*'
        $FirstPass = $false
      }
      else {
        ($DataArray[0..$Index] -join '/') + '/*' + "/$($DataArray[-1])"
        ($DataArray[0..$Index] -join '/') + '/*'
      }
    }
    return $Actions
  }

  $PossibleRoles = @()
  $DevolvedActions = Resolve-Actions -ResolveAction $Action
  $DevolutionCount = 0
  foreach ($DevolvedAction in $DevolvedActions) {
    $DevolutionCount++
    Write-Verbose $DevolvedAction
    $Role = Get-AzRoleDefinition | Where-Object {$_.Actions -contains $DevolvedAction -and $_.NotActions -notcontains $Action}
    if ($Role.Count -gt 0) {$PossibleRoles += $Role}
    if ($DevolutionLevel -ne 0 -and $DevolutionCount -eq ($DevolutionLevel * 2)) {break} 
  }
  return $PossibleRoles
}

function Get-AzAsgToVMMapping {
  [CmdletBinding()]
  Param ()

  try {Get-AzSubscription -ErrorAction Stop *> $null}
  catch {  
    try {Connect-AzAccount -ErrorAction Stop *> $null}
    catch {Write-Warning "Please download the AZ module from PowerShell Gallery before running this again"; break}
  }
  
  $AllASGs = Get-AzApplicationSecurityGroup
  $AllAzNics = Get-AzNetworkInterface
  $AllAzNics | Select-Object Name,@{n='VM';e={$_.VirtualMachine.ID}},@{n='ASG';e={$_.IpConfigurations.ApplicationSecurityGroups.ID}} 
}

function Get-AzAsgMembership {
  <#
  .SYNOPSIS
    This lists the computers and their NICs that are in an ASG
  .DESCRIPTION
    Listing an ASG membership is difficult, the only way via the GUI is to visit each computer and
    check to see if it is a member of an ASG, if you had 100's of VMs this would be impractical.
  .NOTES
    Created By: Brent Denny
    Created On: 7-Nov-2023
  .EXAMPLE
    Get-AzAsgMembership
    This command will list all of the ASGs and any VM that is a member of each ASG along with their NIC
  #>

  
  
  [CmdletBinding()]
  Param ()  
  
  try {Get-AzSubscription -ErrorAction Stop *> $null}
  catch {  
    try {Connect-AzAccount -ErrorAction Stop *> $null}
    catch {Write-Warning "Please download the AZ module from PowerShell Gallery before running this again"; break}
  }  
  
  $AzNics = Get-AzNetworkInterface
  $VMs    = Get-AzVM
  $ASGs   = Get-AzApplicationSecurityGroup 
  foreach ($ASG in $ASGs) {
    $MatchingNics = $AzNics | Where-Object {($_.IpConfigurations.ApplicationSecurityGroupsText | ConvertFrom-Json).ID -eq $ASG.Id}
    foreach ($MatchingNic in $MatchingNics) {
      $MatchingVM = $VMs | Where-Object {$_.NetworkProfile.NetworkInterfaces.Id -contains $MatchingNic.Id} 
      if ($MatchingVM) {
        [PSCustomObject][Ordered]@{
          ASG = $ASG.Name
          VM  = $MatchingVM.Name
          NIC = $MatchingNic.Name
        }
      } 
    } 
  }
}