├── AHUB.ps1 ├── ASCCoverageReport.ps1 ├── ASCPlaybookBlockIPonAppGW.ps1 ├── ActivityAlertBlockIPonAppGW.ps1 ├── AdvisorsRecommendations.ps1 ├── AzureEASubReporting.ps1 ├── AzureFWStartStop.ps1 ├── AzureFWStartStopv2.ps1 ├── AzureInventory.ps1 ├── AzurePIPInventory.ps1 ├── AzureSubsInventory.ps1 ├── CoreCheck.ps1 ├── DDoSThresholds.ps1 ├── DefenderforPricing.ps1 ├── ExemptionReport.ps1 ├── Export-AzLaSavedSearches.ps1 ├── Export-AzurePolicyResources.ps1 ├── FindPaaSIPApplyUDR.ps1 ├── Get-OIDCSAuditEvents.ps1 ├── IAMReport.ps1 ├── IgniteDownloaderV2.ps1 ├── ImperativeDeclareAzureResourceOwnerTags.ps1 ├── LogAlertBlockIPonAppGW.ps1 ├── MDEExtErrorReport.ps1 ├── MDESingleOnboard.ps1 ├── ModifyExistingASCPolicybyMG.ps1 ├── ModifyExistingASCPolicybyMGs.ps1 ├── O365CountryLookupSample.ps1 ├── README.md ├── ReIPNics.ps1 ├── Report AKSCluster Cores.ps1 ├── ReportOwnersandSetTag.ps1 ├── ResourceGraphPaaSACLs.ps1 ├── RotateKeys_v2.ps1 ├── SampleExportAllAPIs.ps1 ├── SampleExportPolicies.ps1 ├── SentinelAlertsReportOffline.ps1 ├── SetAzPolicyContainersSubSets.ps1 ├── SetCompanyASCPolicy.ps1 ├── StopShutdownVMs.ps1 ├── SubCoreCheck.ps1 ├── TableRetentionReport.ps1 ├── TimerSentinelASCSync.ps1 ├── Update-SQLBaseLineDBOwner.ps1 ├── UpdateAzSubsASCMDEforLinuxIntegration.ps1 ├── UpdateStorageATP.ps1 ├── VMConnectionFlows.ps1 ├── VMInsightsHealth-SetActionGroup.ps1 ├── VMInventoryV2.ps1 ├── VerifyZIAWebLogDatatoSentinelExample.ps1 ├── arccsesample.ps1 ├── continuousexportruleconfig.ps1 ├── exportwineventsources.ps1 ├── loguploader.ps1 ├── mapdrive.ps1 ├── remainprivateips.ps1 ├── rmd4sql.ps1 ├── updaterulesforplaybook.ps1 └── updatexml.ps1 /AHUB.ps1: -------------------------------------------------------------------------------- 1 | Login-AzureRmAccount 2 | 3 | $Sub = Get-AzureRmSubscription | Out-GridView -PassThru 4 | 5 | Set-AzureRmContext -Subscription $Sub.Name 6 | 7 | #Set var for total number of ahub lics qualified, future state logic to not exceed counting loop # stated 8 | $maxahublic = 100 9 | 10 | 11 | # Set the output location for the logs 12 | $outputFile = "C:\temp\ahubset.txt" 13 | 14 | #Set and apply 1st line of csv headers 15 | $vmstring = "VMName,NumberOfCores" 16 | $vmstring | Out-File $outputFile -append -force 17 | 18 | 19 | 20 | $vms = Get-AzureRmVM 21 | 22 | ForEach ($vm in $vms) 23 | { 24 | 25 | #Find VM Size 26 | $vmhw = (Get-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.name).hardwareProfile.VmSize 27 | 28 | #Find VM Cores in Size 29 | $vmhwcore = Get-AzureRMvmsize -location $VM.Location | ?{ $_.name -eq $vmhw } 30 | 31 | #trigger AHUB setting on VMs with Windows OS and with optimal desired core counts 8 or 16 32 | If ( $vm.OSProfile.WindowsConfiguration -ne $null -and $vmhwcore.NumberOfCores -eq 8 -or $vmhwcore.NumberOfCores -eq 16) 33 | { 34 | Write-host "$VM.name core match, setting AHUB" 35 | 36 | # Sets AHUB on VM 37 | $vm = Get-AzureRmVM -ResourceGroup $VM.ResourceGroupName -Name $VM.name 38 | $vm.LicenseType = "Windows_Server" 39 | Update-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -VM $vm 40 | 41 | # Write out VM line of data collected and place into csv 42 | $vmstring = "$($VM.name),$($vmhwcore.NumberOfCores)" 43 | $vmstring | Out-File $outputFile -append -force 44 | 45 | } 46 | 47 | } 48 | 49 | Write-Output "All VMs in Subscription checked" 50 | 51 | -------------------------------------------------------------------------------- /ASCCoverageReport.ps1: -------------------------------------------------------------------------------- 1 |  2 | ## REQS ## You will need Az.Security and Az modules installed 3 | # Created On: 4/20/2020 5:32 PM 4 | # Created By: Nathan Swift nate.swift@live.com 5 | # This script is as is and not supported by Microsoft 6 | # Microsoft does not assume any risk of data loss 7 | # Use it at your own risk 8 | ################################################################################ 9 | 10 | $subs = Get-AzSubscription 11 | 12 | $csvpath = "C:\temp\asccoveragereport.csv" 13 | 14 | # File check to overwrite existing asccoveragereport.csv 15 | $filecheck = Get-FileHash -Path $path 16 | 17 | If ($filecheck.Path -eq $path) 18 | { 19 | Remove-Item -Path $path 20 | Write-host "Removed Previous File" 21 | } 22 | else 23 | { 24 | Write-host "No Previous File Found" 25 | } 26 | 27 | # File check to overwrite existing asccoveragereport.csv 28 | $filecheck2 = Get-FileHash -Path $csvpath 29 | 30 | If ($filecheck2.Path -eq $csvpath) 31 | { 32 | Remove-Item -Path $csvpath 33 | Write-host "Removed Previous File" 34 | } 35 | else 36 | { 37 | Write-host "No Previous File Found" 38 | } 39 | 40 | 41 | $outputFile = $csvpath 42 | 43 | # write out headers of CSV 44 | $ascstring = "SubscriptionName,SubscriptionId,ServiceName,PricingTier,FreeTrialTimeRemaining" 45 | $ascstring | Out-File $outputFile -append -force 46 | 47 | # For each subscription lookup the ASC Pricing information and 48 | ForEach ($sub in $subs) { 49 | 50 | # Set the current subscription context 51 | Set-AzContext -Subscription $sub.Id 52 | 53 | # get the asc pricing information for services on subscription 54 | $azsecprices = Get-AzSecurityPricing 55 | 56 | # for each asc service within subscription write the information into report 57 | foreach ($azsecprice in $azsecprices) { 58 | 59 | #Generate the string of data for the asc service and pricing information 60 | $ascstring = "$($sub.Name),$($sub.Id),$($azsecprice.Name),$($azsecprice.PricingTier),$($azsecprice.FreeTrialRemainingTime)" 61 | 62 | #Write into and append into output file 63 | $ascstring | Out-File $outputFile -append -force 64 | 65 | } 66 | 67 | 68 | } -------------------------------------------------------------------------------- /ASCPlaybookBlockIPonAppGW.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This runbook recieves IPs to block in an data array from a Azure Security Center PlayBook it then applies them to NSG Rules and a NSG that is protecting the Application Gateway. 4 | 5 | .DESCRIPTION 6 | 7 | 8 | DEPENDENCIES 9 | - The runbook must be called from an Azure Security PlayBook where the public ips being sent are in an ARRAY, trigger is a Logic App to webhook. 10 | 11 | REQUIRED AUTOMATION ASSETS 12 | - An Automation connection asset called "AzureRunAsConnection" that is of type AzureRunAsConnection. 13 | - An Automation certificate asset called "AzureRunAsCertificate". 14 | 15 | .PARAMETER WebhookData 16 | Optional. (The user doesn't need to enter anything, but the service always passes an object.) 17 | This is the data that's sent in the webhook that's triggered from the Azure Security Playbook. 18 | 19 | .NOTES 20 | AUTHOR: Nathan Swift 21 | LASTEDIT: 2018-11-19 22 | #> 23 | 24 | [OutputType("PSAzureOperationResponse")] 25 | 26 | param 27 | ( 28 | [Parameter (Mandatory=$false)] 29 | [array] $WebhookData 30 | ) 31 | 32 | $ErrorActionPreference = "stop" 33 | 34 | $connectionName = "AzureRunAsConnection" 35 | try 36 | { 37 | # Get the connection "AzureRunAsConnection " 38 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 39 | 40 | "Logging in to Azure..." 41 | Add-AzureRmAccount ` 42 | -ServicePrincipal ` 43 | -TenantId $servicePrincipalConnection.TenantId ` 44 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 45 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 46 | } 47 | catch { 48 | if (!$servicePrincipalConnection) 49 | { 50 | $ErrorMessage = "Connection $connectionName not found." 51 | throw $ErrorMessage 52 | } else{ 53 | Write-Error -Message $_.Exception 54 | throw $_.Exception 55 | } 56 | } 57 | 58 | 59 | ##Manual Testing 60 | #$WebhookData = [array]("185.95.187.94", "103.120.176.139") 61 | 62 | #Static Variables 63 | $NSGname = "YOUR NSG NAME" 64 | $NSGrg = "YOUR NSG RESOURCE GROUP NAME" 65 | $i = 1 66 | 67 | #Take Webhook Data and taketody of Data in alert and convert JSON into PS Object 68 | Write-Host ($WebhookData) 69 | 70 | #obtain the NSG you want to add a rule to - Set your unique NSG name and ResourceGroupName above in Static Variables 71 | $NSG = Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg 72 | 73 | Foreach ($entry in $WebhookData){ 74 | $i++ 75 | Write-Host ("counter is :$i") 76 | Write-Host ("PIP is :$entry") 77 | 78 | #/32 CIDR to PIP for NSG rule 79 | $pipcidr = $entry+"/32" 80 | 81 | Write-Host ($pipcidr) 82 | 83 | #NSG Priority 84 | $priority = $NSG.SecurityRules.Priority.Count + 101 + $i 85 | 86 | #Add NSG Rule 87 | Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg | Add-AzureRmNetworkSecurityRuleConfig -Name "ascla_$entry" -Direction Inbound -Priority $priority -Access Deny -SourceAddressPrefix $pipcidr -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol '*' | Set-AzureRmNetworkSecurityGroup 88 | 89 | } 90 | -------------------------------------------------------------------------------- /ActivityAlertBlockIPonAppGW.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This runbook recieves IPs to block in an data array from a Azure Security Center PlayBook it then applies them to NSG Rules and a NSG that is protecting the Application Gateway. 4 | 5 | .DESCRIPTION 6 | 7 | 8 | DEPENDENCIES 9 | - The runbook must be called from an Azure Security PlayBook where the public ips being sent are in an ARRAY, trigger is a Logic App to webhook. 10 | 11 | REQUIRED AUTOMATION ASSETS 12 | - An Automation connection asset called "AzureRunAsConnection" that is of type AzureRunAsConnection. 13 | - An Automation certificate asset called "AzureRunAsCertificate". 14 | 15 | .PARAMETER WebhookData 16 | Optional. (The user doesn't need to enter anything, but the service always passes an object.) 17 | This is the data that's sent in the webhook that's triggered from the Azure Security Playbook. 18 | 19 | .NOTES 20 | AUTHOR: Nathan Swift 21 | LASTEDIT: 2018-11-19 22 | #> 23 | 24 | [OutputType("PSAzureOperationResponse")] 25 | 26 | param 27 | ( 28 | [Parameter (Mandatory=$false)] 29 | [object] $WebhookData 30 | ) 31 | 32 | $ErrorActionPreference = "stop" 33 | 34 | $connectionName = "AzureRunAsConnection" 35 | try 36 | { 37 | # Get the connection "AzureRunAsConnection " 38 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 39 | 40 | "Logging in to Azure..." 41 | Add-AzureRmAccount ` 42 | -ServicePrincipal ` 43 | -TenantId $servicePrincipalConnection.TenantId ` 44 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 45 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 46 | } 47 | catch { 48 | if (!$servicePrincipalConnection) 49 | { 50 | $ErrorMessage = "Connection $connectionName not found." 51 | throw $ErrorMessage 52 | } else{ 53 | Write-Error -Message $_.Exception 54 | throw $_.Exception 55 | } 56 | } 57 | 58 | 59 | ##Manual Testing 60 | #$WebhookData = Get-Content 'C:\temp\activitylog2.json' | Out-String | ConvertFrom-Json 61 | 62 | Write-Host ($WebhookData) 63 | 64 | #Take Webhook Data and take request body of Data in alert and convert JSON into PS Object 65 | $WebhookRequestBody = $WebhookData.RequestBody | ConvertFrom-Json 66 | 67 | #Take Webhook Data and taketody of Data in alert and convert JSON into PS Object 68 | Write-Host ($WebhookRequestBody) 69 | 70 | #Ensure Activity Log Alert from ASC pertains to applicationGateways 71 | $mgmturl = $WebhookRequestBody.data.context.activityLog.properties.managementURL -split "/" 72 | 73 | #Conditional check to make sure it matches to applicationGateway to execure NSG rules. 74 | if ($WebhookRequestBody.data.context.activityLog.properties.resourceType -eq "Networking" -and $mgmturl[9] -contains "applicationGateways"){ 75 | 76 | #store the Rows results of alert data into a variable 77 | $SourceIps = $WebhookRequestBody.data.context.activityLog.properties.sourceIPs 78 | 79 | #Create an Array from the string 80 | $SourceIps = $SourceIps -split "," 81 | 82 | #Take Webhook Data and taketody of Data in alert and convert JSON into PS Object 83 | Write-Host ($SourceIps) 84 | 85 | #Static Variables 86 | $NSGname = "YOUR NSG NAME" 87 | $NSGrg = "YOUR NSG RESOURCE GROUP NAME" 88 | $i = 1 89 | 90 | 91 | foreach ($pip in $SourceIps) { 92 | 93 | $i++ 94 | Write-Host ("counter is :$i") 95 | Write-Host ($pip) 96 | 97 | #/32 CIDR to PIP for NSG rule 98 | $pipcidr = $pip+"/32" 99 | 100 | Write-Host ($pipcidr) 101 | 102 | #obtain the NSG you want to add a rule to - Set you unique NSG anme and ResourceGroupName 103 | $NSG = Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg 104 | 105 | #Check the custom rules count and add to the next priority so oes not overlap with existing priority rule 106 | $priority = $NSG.SecurityRules.Priority.Count + 801 + $i 107 | 108 | $rulename = New-Guid 109 | Write-Host ($rulename) 110 | 111 | #Construct the NSG Rule based of the pity found and the PIP CIDR found above and apply the new rule to the NSG - Set you unique NSG anme and ResourceGroupName 112 | Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg | Add-AzureRmNetworkSecurityRuleConfig -Name "activitylogrb_$pip" -Direction Inbound -Priority $priority -Access Deny -SourceAddressPrefix $pipcidr -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol '*' | Set-AzureRmNetworkSecurityGroup 113 | } 114 | } 115 | Else 116 | { 117 | Write-Host ("Logic Conditions not matched") 118 | } 119 | -------------------------------------------------------------------------------- /AdvisorsRecommendations.ps1: -------------------------------------------------------------------------------- 1 | # using AzureRM Powershell 2 | # using ARMClient 3 | 4 | $subs = Get-AzureRmSubscription 5 | 6 | foreach ($sub in $subs){ 7 | 8 | $subid = $sub.id 9 | 10 | $subname = $sub.name 11 | 12 | armclient POST https://management.azure.com/subscriptions/$subid/providers/Microsoft.Advisor/generateRecommendations?api-version=2017-03-31 13 | 14 | armclient GET https://management.azure.com/subscriptions/$subid/providers/Microsoft.Advisor/recommendations?api-version=2017-03-31 | Out-File -FilePath C:\temp\armadvisor$subname.json 15 | 16 | } 17 | -------------------------------------------------------------------------------- /AzureEASubReporting.ps1: -------------------------------------------------------------------------------- 1 | 2 | ## Script is used to query https://consumption.azure.com/v1/enrollments/{enrollmentID}/usagedetails 3 | ## Script reaches out and returns active consumption for the current month and pushes a table for enterprise knowledge purposes and future configuration of Subs 4 | ## Can be used in subscription sprawl enviroments to see what Subs are consuming, organize how Subs are configured in Enterprise 5 | 6 | # EA Consumption API variables 7 | $EnrollmentNumber="..." 8 | $AccessKey="..." 9 | 10 | 11 | #Base URL: https://consumption.azure.com/v1/enrollments/{enrollmentID}/usagedetails 12 | # Create URL and Auth for invoking 13 | $BaseUrl = "https://consumption.azure.com/v1/enrollments/" 14 | 15 | $AuthHeaders = @{"authorization"="bearer $AccessKey"} 16 | 17 | $Url= $BaseUrl + $EnrollmentNumber + "/usagedetails" 18 | 19 | # Create an array for object data responses 20 | $dataarray = @() 21 | 22 | # Continue logic variable for DO 23 | $continuationToken = "" 24 | 25 | #Iterate untill API Call does not respond with a next link, or nextlink=null 26 | Do { 27 | 28 | # Call EA Consumption API 29 | $Response = Invoke-WebRequest $Url -Headers $AuthHeaders 30 | 31 | # Convert response from JSON to Table for a NextLink check variable 32 | $links = $Response.Content | ConvertFrom-Json 33 | 34 | # Convert response from JSON to Table for Object, peer into the data responses 35 | $responsedata = $Response.Content | ConvertFrom-Json | select-Object -Expand Data 36 | 37 | #Append data responses of object into array 38 | $dataarray += $responsedata 39 | 40 | # Time to check if there is more data API results batched at 1000, with a Next Link 41 | if ($Url = $links.nextLink) { 42 |         $continuationToken = ` 43 |             [System.Web.HttpUtility]::` 44 |             UrlDecode($links.NextLink.Split("=")[-1]) 45 |     } else { 46 |         $continuationToken = "" 47 |     } 48 | } until (!$continuationToken) 49 | 50 | # Let's get unique with the table 51 | $cleandataaarray = $dataarray | Select accountOwnerEmail,subscriptionGuid,subscriptionName -Unique 52 | -------------------------------------------------------------------------------- /AzureFWStartStop.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | An runbook which starts or stops the Azure Firewall 4 | 5 | .NOTES 6 | AUTHOR: Nathan Swift 7 | LASTEDIT: Novemeber 6, 2018 8 | #> 9 | 10 | Param 11 | ( 12 | [Parameter (Mandatory= $true)] 13 | [String] $process = "stop", 14 | 15 | [Parameter (Mandatory= $true)] 16 | [String] $azfwname = "azurefw", 17 | 18 | [Parameter (Mandatory= $true)] 19 | [String] $azfwrg = "rgNetworking", 20 | 21 | [Parameter (Mandatory= $false)] 22 | [String] $vnetname = "VNET-HUB", 23 | 24 | [Parameter (Mandatory= $false)] 25 | [String] $vnetrg = "rgNetworking", 26 | 27 | [Parameter (Mandatory= $false)] 28 | [String] $pipname = "azureFirewalls-ip", 29 | 30 | [Parameter (Mandatory= $false)] 31 | [String] $piprg = "rgNetworking" 32 | 33 | ) 34 | 35 | $connectionName = "AzureRunAsConnection" 36 | try 37 | { 38 | # Get the connection "AzureRunAsConnection " 39 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 40 | 41 | "Logging in to Azure..." 42 | Add-AzureRmAccount ` 43 | -ServicePrincipal ` 44 | -TenantId $servicePrincipalConnection.TenantId ` 45 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 46 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 47 | } 48 | catch { 49 | if (!$servicePrincipalConnection) 50 | { 51 | $ErrorMessage = "Connection $connectionName not found." 52 | throw $ErrorMessage 53 | } else{ 54 | Write-Error -Message $_.Exception 55 | throw $_.Exception 56 | } 57 | } 58 | 59 | Set-AzureRMContext -Subscription "YOUR SUBSCRIPTION NAME HERE" 60 | 61 | # Azure FW Object 62 | $azfw = Get-AzureRmFirewall -Name $azfwname -ResourceGroupName $azfwrg 63 | 64 | # Deallocate 65 | if ($process -match "stop"){ 66 | 67 | # Deallocate 68 | $azfw.Deallocate() 69 | Set-AzureRmFirewall -AzureFirewall $azfw 70 | Write-Output ("Azure Firewal $azfw.name Stopped") 71 | 72 | } 73 | 74 | #Reallocate 75 | Elseif ($process -match "start"){ 76 | 77 | $vnet = Get-AzureRmVirtualNetwork -Name $vnetname -ResourceGroupName $vnetrg 78 | $publicip = Get-AzureRmPublicIpAddress -Name $pipname -ResourceGroupName $piprg 79 | $azfw.Allocate($vnet,$publicip) 80 | Set-AzureRmFirewall -AzureFirewall $azfw 81 | Write-Output ("Azure Firewal $azfw.name Started") 82 | } 83 | 84 | Else { 85 | 86 | Write-Output ("Azure Firewal $azfw.name incorrect process parameter") 87 | 88 | } -------------------------------------------------------------------------------- /AzureFWStartStopv2.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | Created On: 2/15/2020 2:00 AM 4 | Created By: Nathan Swift nate.swift@live.com 5 | 6 | This script is as is and not supported by Microsoft 7 | Microsoft does not assume any risk of data loss 8 | 9 | Use it at your own risk 10 | 11 | NOTES: !! Be sure to add Tags on the Azure Resource RouteTables that need to be associated and disassociated to the subnets during the start and stop of Azure Firewall. 12 | 13 | Use the following tags subnet:vnetname,subnetname - ex subnet:Spoke-VNET,WEB 14 | 15 | REQUIREMENTS: Az.Network Module Version 4.1.0 - Tracking issue with latest Az.Network Module - https://github.com/Azure/azure-powershell/issues/13544 16 | 17 | #> 18 | 19 | Param 20 | ( 21 | [Parameter (Mandatory= $true)] 22 | [String] $process = "stop", 23 | 24 | [Parameter (Mandatory= $true)] 25 | [String] $azfwname = "DemoFirewall", 26 | 27 | [Parameter (Mandatory= $true)] 28 | [String] $azfwrg = "rgAzFwPremiumLab", 29 | 30 | [Parameter (Mandatory= $false)] 31 | [String] $vnetname = "FirewallVnet", 32 | 33 | [Parameter (Mandatory= $false)] 34 | [String] $vnetrg = "rgAzFwPremiumLab", 35 | 36 | [Parameter (Mandatory= $false)] 37 | [String] $pipname = "FirewallPublicIP", 38 | 39 | [Parameter (Mandatory= $false)] 40 | [String] $piprg = "rgAzFwPremiumLab" 41 | 42 | ) 43 | 44 | # SUed for Starting async jobs to start \ stop azure fw 45 | Enable-AzContextAutosave 46 | 47 | # Azure FW Object 48 | $azfw = Get-AzFirewall -Name $azfwname -ResourceGroupName $azfwrg 49 | 50 | # Deallocate 51 | if ($process -match "stop"){ 52 | 53 | 54 | # Deallocate 55 | $azfw.Deallocate() 56 | Start-Job {Set-AzFirewall -AzureFirewall $azfw} 57 | Write-Output ("Azure Firewal $azfw.name Stopped") 58 | 59 | # Find the RouteTables 60 | $tagudrs = Get-AzResource -ResourceType "Microsoft.Network/routeTables" -TagName 'subnet' 61 | 62 | #For each Route Table execute commands to disassociate the RouteTable from Subnet 63 | foreach ($tagudr in $tagudrs){ 64 | 65 | #Collect information for later lookup 66 | $vnetname = ($tagudr.Tags.subnet).Split(',')[0] 67 | $subnetname = ($tagudr.Tags.subnet).Split(',')[1] 68 | 69 | #Collect configuration information 70 | $vnet = Get-AzVirtualNetwork -Name $vnetname 71 | $subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $subnetname 72 | 73 | #disassociate from subnet config 74 | $subnet.RouteTable = $null 75 | 76 | #update the VNET and remove RouteTable from Subnet 77 | Set-AzVirtualNetwork -VirtualNetwork $vnet 78 | 79 | } 80 | 81 | } 82 | 83 | #Reallocate 84 | Elseif ($process -match "start"){ 85 | 86 | $vnet = Get-AzVirtualNetwork -Name $vnetname -ResourceGroupName $vnetrg 87 | $publicip = Get-AzPublicIpAddress -Name $pipname -ResourceGroupName $piprg 88 | $azfw.Allocate($vnet,$publicip) 89 | Start-Job {Set-AzFirewall -AzureFirewall $azfw} 90 | Write-Output ("Azure Firewal $azfw.name Started") 91 | 92 | # Find the RouteTables 93 | $tagudrs = Get-AzResource -ResourceType "Microsoft.Network/routeTables" -TagName 'subnet' 94 | 95 | #For each Route Table execute commands to disassociate the RouteTable from Subnet 96 | foreach ($tagudr in $tagudrs){ 97 | 98 | #Collect information for later lookup 99 | $vnetname = ($tagudr.Tags.subnet).Split(',')[0] 100 | $subnetname = ($tagudr.Tags.subnet).Split(',')[1] 101 | 102 | #Collect configuration information 103 | $vnet = Get-AzVirtualNetwork -Name $vnetname 104 | $subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $subnetname 105 | 106 | #update the VNET and add RouteTable to Subnet 107 | Set-AzVirtualNetworkSubnetConfig ` 108 | -VirtualNetwork $vnet ` 109 | -Name $subnetname ` 110 | -AddressPrefix $subnet.AddressPrefix ` 111 | -RouteTableId $tagudr.ResourceId | ` 112 | Set-AzVirtualNetwork 113 | 114 | } 115 | } 116 | 117 | Else { 118 | 119 | Write-Output ("Azure Firewal $azfw.name incorrect process parameter") 120 | 121 | } -------------------------------------------------------------------------------- /AzureInventory.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 6/13/2018 1:21 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | ################################################################################ 7 | 8 | <# Possible Futures: 9 | 10 | 1. Logic to handle multiple Nics and information on VM, especially for NVAs 11 | 2. Logic to handle disk detection count, size, and performance of disks attached to VM, 12 | 3. Provide some samples/Guidance for authentication using SPN, Scheduled Task, Stored Cred in Win, AutomationAccount 13 | 4. Other Resources change header string to accomodate L7LB, ELB PIPs 14 | 15 | #> 16 | 17 | # Path and filename for output data file being generated. 18 | $path = C:\temp\subsazuinventory.txt 19 | $csvpath = "C:\temp\azuinventory.csv" 20 | 21 | # Authenticate Piece 22 | Login-AzureRmAccount 23 | 24 | # Time Tracking Start 25 | $datetime = Get-Date 26 | Write-Host $datetime 27 | 28 | # File check to overwrite existing vm inventory collection 29 | $filecheck = Get-FileHash -Path $path 30 | 31 | If ($filecheck.Path -eq $path) 32 | { 33 | Remove-Item -Path $path 34 | Write-host "Removed Previous File" 35 | } 36 | else 37 | { 38 | Write-host "No Previous File Found" 39 | } 40 | 41 | # File check to overwrite existing vm inventory collection 42 | $filecheck2 = Get-FileHash -Path $csvpath 43 | 44 | If ($filecheck2.Path -eq $csvpath) 45 | { 46 | Remove-Item -Path $csvpath 47 | Write-host "Removed Previous File" 48 | } 49 | else 50 | { 51 | Write-host "No Previous File Found" 52 | } 53 | 54 | # Collect all subscriptions 55 | $subs = Get-AzureRmSubscription 56 | 57 | # Outputfile for vm inventory 58 | $outputFile = $path 59 | 60 | #Set and apply 1st line of csv headers 61 | $vmstring = "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 62 | $vmstring | Out-File $outputFile -append -force 63 | 64 | # Iterate through all subscriptions 65 | foreach($sub in $subs) { 66 | 67 | # Set the current Azure Subscription to pull information from 68 | Set-AzureRmContext -Subscription $sub.Name 69 | 70 | 71 | #Collect all VMs in current context Subscription 72 | $VMs = Get-AzureRmVM 73 | 74 | # Loop and iterate through all VMs to begin collecting data 75 | foreach($VM in $VMs) { 76 | 77 | # Type of Azure resource 78 | $Type = "VM" 79 | 80 | #Find VM OS Properties 81 | $image = $VM.Storageprofile.ImageReference 82 | 83 | #Find VM Size 84 | $vmhw = $VM.hardwareProfile.VmSize 85 | 86 | #Find VM Cores 87 | $vmhwcore = Get-AzureRMvmsize -location $VM.Location | ?{ $_.name -eq $vmhw } 88 | 89 | #Find VM Image Publisher 90 | $publisher = $image.publisher 91 | 92 | #If Publisher is NULL VM Image is Custom Maintained 93 | If ($publisher -eq $null) 94 | { 95 | $publisher = "CustomImage" 96 | } 97 | 98 | #Find VM Image Offer 99 | $offer = $image.offer 100 | 101 | #Find VM SKU 102 | $sku = $image.sku 103 | 104 | #Obtain Nic Configuration and obtain/use use the resourceid to get nic properties 105 | ## Only collecting the Primary or 1st NIC on VM, NVAs could have multiple nics, script logic will need to be introduced and csv format thought through for multiple entries in a column to handle multiple nics and store data however this logic is not baked in the script, possibly future release 106 | $VMnicid = $VM.NetworkProfile.NetworkInterfaces[0].Id 107 | $VMnicprop = Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties 108 | 109 | #Obtain MAC Address 110 | $VMAMC = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.macAddress 111 | 112 | #Obtain IP Address of Nic ## Stored even when VM is Stopped Deallocated, however MAC is not stored when Stopped Deallocated 113 | $VMprivip = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.ipConfigurations[0].properties.PrivateIPAddress 114 | 115 | #Obtain IP Address Allocation 116 | $VMprivipq = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.ipConfigurations[0].properties.privateIPAllocationMethod 117 | 118 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 119 | # Write out VM line of data collected and place into csv 120 | $vmstring = "$($sub.Name),$($VM.ResourceGroupName),$($Type),$($VM.name),$($VM.Location),$($VMAMC),$($VMprivip),$($VMprivipq),$($vmhwcore.NumberOfCores),$($vmhwcore.MemoryInMB),$($VM.HardwareProfile.VMSize),$($publisher),$($offer),$($sku)" 121 | 122 | #Write into and append into output file 123 | $vmstring | Out-File $outputFile -append -force 124 | 125 | } 126 | 127 | #Collect all LBs in current context Subscription 128 | $LBs = Get-AzureRmLoadBalancer 129 | 130 | # Loop and iterate through all LBss to begin collecting data 131 | foreach($LB in $LBs) { 132 | 133 | # Type of Azure resource 134 | $Type = "L4LB" 135 | 136 | #Obtain Private IP Address of LB 137 | $LBprivip = $LB.FrontendIpConfigurations.PrivateIpAddress 138 | 139 | #Obtain IP Address Allocation 140 | $LBprivipq = $LB.FrontendIpConfigurations.PrivateIpAllocationMethod 141 | 142 | $sku = $LBs[0].Sku.Name 143 | 144 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 145 | # Write out LB line of data collected and place into csv 146 | $lbstring = "$($sub.Name),$($LB.ResourceGroupName),$($Type),$($LB.name),$($LB.Location),,$($LBprivip),$($LBprivipq),,,,,,$($sku)" 147 | 148 | #Write into and append into output file 149 | $lbstring | Out-File $outputFile -append -force 150 | 151 | 152 | } 153 | 154 | 155 | } 156 | 157 | # Time Tracking Finished 158 | $datetime = Get-Date 159 | Write-Host $datetime 160 | 161 | # Once done import the data into excel 162 | 163 | $CSV = Import-Csv -Path $path 164 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation 165 | -------------------------------------------------------------------------------- /AzurePIPInventory.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 8/3/2018 1:21 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | ################################################################################ 7 | 8 | <# 9 | Current Services: 10 | 11 | PaaS SQL DB 12 | PaaS MySQL DB 13 | Cosmos DB 14 | Functions 15 | Storage Account 16 | 17 | #> 18 | 19 | <# Possible Futures: 20 | 21 | #> 22 | 23 | Function Get-DnsEntry($iphost) 24 | 25 | { 26 | 27 | If($ipHost -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") 28 | 29 | { 30 | 31 | [System.Net.Dns]::GetHostEntry($iphost).HostName 32 | 33 | } 34 | 35 | ElseIf( $ipHost -match "^.*\.\.*") 36 | 37 | { 38 | 39 | [System.Net.Dns]::GetHostEntry($iphost).AddressList[0].IPAddressToString 40 | 41 | } 42 | 43 | ELSE { Throw "Specify either an IP V4 address or a hostname" } 44 | 45 | } 46 | 47 | # Path and filename for output data file being generated. 48 | $path = "C:\temp\subspipinventory.txt" 49 | $csvpath = "C:\temp\subspipinventory.csv" 50 | 51 | # Authenticate Piece 52 | Login-AzureRmAccount 53 | 54 | # Time Tracking Start 55 | $datetime = Get-Date 56 | Write-Host $datetime 57 | 58 | # File check to overwrite existing vm inventory collection 59 | $filecheck = Get-FileHash -Path $path 60 | 61 | If ($filecheck.Path -eq $path) 62 | { 63 | Remove-Item -Path $path 64 | Write-host "Removed Previous File" 65 | } 66 | else 67 | { 68 | Write-host "No Previous File Found" 69 | } 70 | 71 | # File check to overwrite existing vm inventory collection 72 | $filecheck2 = Get-FileHash -Path $csvpath 73 | 74 | If ($filecheck2.Path -eq $csvpath) 75 | { 76 | Remove-Item -Path $csvpath 77 | Write-host "Removed Previous File" 78 | } 79 | else 80 | { 81 | Write-host "No Previous File Found" 82 | } 83 | 84 | # Collect all subscriptions 85 | $subs = Get-AzureRmSubscription 86 | 87 | # Outputfile for vm inventory 88 | $outputFile = $path 89 | 90 | #Set and apply 1st line of csv headers 91 | $vmstring = "Subscription,ResourceGroupName,Type,ResourceName,ResourcePIP" 92 | $vmstring | Out-File $outputFile -append -force 93 | 94 | # Iterate through all subscriptions 95 | foreach($sub in $subs) { 96 | 97 | # Set the current Azure Subscription to pull information from 98 | Set-AzureRmContext -Subscription $sub.Name 99 | 100 | 101 | #Collect all VMs in current context Subscription 102 | $Resources = Get-AzureRmResource 103 | 104 | # Loop and iterate through all VMs to begin collecting data 105 | foreach($Resource in $Resources) { 106 | 107 | # Type of Azure resource 108 | $Type = $Resource.resourcetype 109 | 110 | #If Publisher is NULL VM Image is Custom Maintained 111 | If ($Type -eq "Microsoft.Storage/storageAccounts") 112 | { 113 | 114 | $URL = $Resource.Name + ".blob.core.windows.net" 115 | 116 | $ResourcePIP = Get-DnsEntry $URL 117 | 118 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 119 | # Write out VM line of data collected and place into csv 120 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 121 | 122 | #Write into and append into output file 123 | $vmstring | Out-File $outputFile -append -force 124 | 125 | $URL = $Resource.Name + ".file.core.windows.net" 126 | 127 | $ResourcePIP = Get-DnsEntry $URL 128 | 129 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 130 | # Write out VM line of data collected and place into csv 131 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 132 | 133 | #Write into and append into output file 134 | $vmstring | Out-File $outputFile -append -force 135 | 136 | $URL = $Resource.Name + ".queue.core.windows.net" 137 | 138 | $ResourcePIP = Get-DnsEntry $URL 139 | 140 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 141 | # Write out VM line of data collected and place into csv 142 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 143 | 144 | #Write into and append into output file 145 | $vmstring | Out-File $outputFile -append -force 146 | 147 | $URL = $Resource.Name + ".table.core.windows.net" 148 | 149 | $ResourcePIP = Get-DnsEntry $URL 150 | 151 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 152 | # Write out VM line of data collected and place into csv 153 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 154 | 155 | #Write into and append into output file 156 | $vmstring | Out-File $outputFile -append -force 157 | 158 | } 159 | 160 | 161 | # RESEARCH https://docs.microsoft.com/en-us/azure/sql-database/sql-database-connectivity-architecture 162 | ElseIf ($Type -eq "Microsoft.Sql/servers") { 163 | 164 | $URL = $Resource.Name + ".database.windows.net" 165 | 166 | $ResourcePIP = Get-DnsEntry $URL 167 | 168 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 169 | # Write out VM line of data collected and place into csv 170 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 171 | 172 | #Write into and append into output file 173 | $vmstring | Out-File $outputFile -append -force 174 | 175 | } 176 | 177 | ElseIf ($Type -eq "Microsoft.DBforMySQL/servers") { 178 | 179 | $URL = $Resource.Name + ".mysql.database.azure.com" 180 | 181 | $ResourcePIP = Get-DnsEntry $URL 182 | 183 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 184 | # Write out VM line of data collected and place into csv 185 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 186 | 187 | #Write into and append into output file 188 | $vmstring | Out-File $outputFile -append -force 189 | 190 | } 191 | 192 | ElseIf ($Type -eq "Microsoft.Web/sites/functions") { 193 | 194 | $URL = $Resource.Name + ".azurewebsites.net" 195 | 196 | $ResourcePIP = Get-DnsEntry $URL 197 | 198 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 199 | # Write out VM line of data collected and place into csv 200 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 201 | 202 | #Write into and append into output file 203 | $vmstring | Out-File $outputFile -append -force 204 | 205 | } 206 | 207 | ElseIf ($Type -eq "Microsoft.DocumentDB/databaseAccounts") { 208 | 209 | $URL = $Resource.Name + "documents.azure.com" 210 | 211 | $ResourcePIP = Get-DnsEntry $URL 212 | 213 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 214 | # Write out VM line of data collected and place into csv 215 | $vmstring = "$($sub.Name),$($Resource.ResourceGroupName),$($Type),$($Resource.name),$($ResourcePIP)" 216 | 217 | #Write into and append into output file 218 | $vmstring | Out-File $outputFile -append -force 219 | 220 | } 221 | 222 | } 223 | 224 | 225 | } 226 | 227 | # Time Tracking Finished 228 | $datetime = Get-Date 229 | Write-Host $datetime 230 | 231 | # Once done import the data into excel 232 | 233 | $CSV = Import-Csv -Path $path 234 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation -------------------------------------------------------------------------------- /AzureSubsInventory.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 8/8/2018 11:00 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | ################################################################################ 7 | 8 | 9 | # Path and filename for output data file being generated. 10 | $path = "C:\temp\azuresubinventory.txt" 11 | $csvpath = "C:\temp\azuresubinventory.csv" 12 | 13 | # Authenticate Piece 14 | #Login-AzureRmAccount 15 | 16 | # Time Tracking Start 17 | $datetime = Get-Date 18 | Write-Host $datetime 19 | 20 | # File check to overwrite existing vm inventory collection 21 | $filecheck = Get-FileHash -Path $path 22 | 23 | If ($filecheck.Path -eq $path) 24 | { 25 | Remove-Item -Path $path 26 | Write-host "Removed Previous File" 27 | } 28 | else 29 | { 30 | Write-host "No Previous File Found" 31 | } 32 | 33 | # File check to overwrite existing vm inventory collection 34 | $filecheck2 = Get-FileHash -Path $csvpath 35 | 36 | If ($filecheck2.Path -eq $csvpath) 37 | { 38 | Remove-Item -Path $csvpath 39 | Write-host "Removed Previous File" 40 | } 41 | else 42 | { 43 | Write-host "No Previous File Found" 44 | } 45 | 46 | # Collect all subscriptions 47 | $subs = Get-AzureRmSubscription 48 | 49 | # Outputfile for vm inventory 50 | $outputFile = $path 51 | 52 | #Set and apply 1st line of csv headers 53 | $azusubstring = "SubscriptionName,SubscriptionGUID,SubscriptionSKU,SubscriptionOwners,TenantID,TenantName" 54 | $azusubstring | Out-File $outputFile -append -force 55 | 56 | # Iterate through all subscriptions 57 | foreach($sub in $subs) { 58 | 59 | # Set the current Azure Subscription to pull information from 60 | Set-AzureRmContext -Subscription $sub.Name 61 | 62 | $SubOwner = Get-AzureRmRoleAssignment | where-object {$_.RoleDefinitionName –eq “Owner” -and $_.SignInName -ne $null} | Select -First 3 63 | 64 | 65 | #FUTURE AREA TO CAPTURE 66 | $subsku = "TBD" 67 | 68 | #Obtain TenantName trying to find way to end and capture tenant name in error response header. 69 | #$URL = "https://management.azure.com/subscriptions/$sub" +"?api-version=2016-01-01" 70 | #$tenantname = try{Invoke-RestMethod -UseBasicParsing -Uri $URL} catch {$err=$_.Exception} 71 | #$err.Response.GetResponseHeader() 72 | $tenantname = "TBD" 73 | 74 | # "Subscription,ResourceGroup,Type,ResourceName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 75 | # Write out VM line of data collected and place into csv 76 | $azusubstring = "$($sub.Name),$($sub.Id),$($subsku),$($SubOwner.SignInName),$($sub.TenantId),$($tenantname)" 77 | 78 | #Write into and append into output file 79 | $azusubstring | Out-File $outputFile -append -force 80 | 81 | 82 | } 83 | 84 | 85 | 86 | # Time Tracking Finished 87 | $datetime = Get-Date 88 | Write-Host $datetime 89 | 90 | # Once done import the data into excel 91 | 92 | $CSV = Import-Csv -Path $path 93 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation -------------------------------------------------------------------------------- /CoreCheck.ps1: -------------------------------------------------------------------------------- 1 | ## Futures 2 | 3 | # Create as a Function to run 4 | # Remove Interactive GUI Prompts \ replace with prompts or params to pass 5 | # Allow Selection of Subscription to check against 6 | 7 | Login-AzureRmAccount 8 | 9 | $deployloc = (Get-AzureRmLocation | Where-Object Providers -contains 'Microsoft.Compute').Location | Out-GridView -PassThru 10 | ##Manual Select Var 11 | #$deployloc = "East US" 12 | 13 | $vmSize = Get-AzureRmVMSize -Location $deployloc | Out-GridView -PassThru | Select Name,NumberofCores 14 | ##Manual Select Var 15 | #$vmSize = "Standard_A8" 16 | 17 | do { 18 | write-host -nonewline "Enter number of VMs - " $vmSize "__:" 19 | $inputString = read-host 20 | $value = $inputString -as [Double] 21 | $ok = $value -ne $NULL 22 | if ( -not $ok ) { write-host "You must enter a numeric value" } 23 | } 24 | until ( $ok ) 25 | ##Manual Select Var 26 | #$value = "4" 27 | 28 | 29 | $vmFamSize = ($vmSize).Name 30 | 31 | $vmFamSizeCount = ($vmSize).NumberofCores 32 | 33 | $totalvmFamSizeCount = $vmFamSizeCount * $value 34 | 35 | 36 | # Define Hashtables for switch lookup rather than IF 37 | $vmFamSize = switch ($vmFamSize) 38 |     { 39 | Basic_A0 {"Basic A Family Cores"} 40 | Basic_A1 {"Basic A Family Cores"} 41 | Basic_A2 {"Basic A Family Cores"} 42 | Basic_A3 {"Basic A Family Cores"} 43 | Basic_A4 {"Basic A Family Cores"} 44 | Standard_A0 {"Standard A0-A7 Family Cores"} 45 | Standard_A1 {"Standard A0-A7 Family Cores"} 46 | Standard_A2 {"Standard A0-A7 Family Cores"} 47 | Standard_A3 {"Standard A0-A7 Family Cores"} 48 | Standard_A4 {"Standard A0-A7 Family Cores"} 49 | Standard_A5 {"Standard A0-A7 Family Cores"} 50 | Standard_A6 {"Standard A0-A7 Family Cores"} 51 | Standard_A7 {"Standard A0-A7 Family Cores"} 52 | Standard_A8 {"Standard A8-A11 Family Cores"} 53 | Standard_A9 {"Standard A8-A11 Family Cores"} 54 | Standard_A10 {"Standard A8-A11 Family Cores"} 55 | Standard_A11 {"Standard A8-A11 Family Cores"} 56 | Standard_A1_v2 {"Standard Av2 Family Cores"} 57 | Standard_A2_v2 {"Standard Av2 Family Cores"} 58 | Standard_A4_v2 {"Standard Av2 Family Cores"} 59 | Standard_A8_v2 {"Standard Av2 Family Cores"} 60 | Standard_A2m_v2 {"Standard Av2 Family Cores"} 61 | Standard_A4m_v2 {"Standard Av2 Family Cores"} 62 | Standard_A8m_v2 {"Standard Av2 Family Cores"} 63 | Standard_D1 {"Standard D Family Cores"} 64 | Standard_D2 {"Standard D Family Cores"} 65 | Standard_D3 {"Standard D Family Cores"} 66 | Standard_D4 {"Standard D Family Cores"} 67 | Standard_D11 {"Standard D Family Cores"} 68 | Standard_D12 {"Standard D Family Cores"} 69 | Standard_D13 {"Standard D Family Cores"} 70 | Standard_D14 {"Standard D Family Cores"} 71 | Standard_D1_v2 {"Standard Dv2 Family Cores"} 72 | Standard_D2_v2 {"Standard Dv2 Family Cores"} 73 | Standard_D3_v2 {"Standard Dv2 Family Cores"} 74 | Standard_D4_v2 {"Standard Dv2 Family Cores"} 75 | Standard_D5_v2 {"Standard Dv2 Family Cores"} 76 | Standard_D11_v2 {"Standard Dv2 Family Cores"} 77 | Standard_D12_v2 {"Standard Dv2 Family Cores"} 78 | Standard_D13_v2 {"Standard Dv2 Family Cores"} 79 | Standard_D14_v2 {"Standard Dv2 Family Cores"} 80 | Standard_D15_v2 {"Standard Dv2 Family Cores"} 81 | Standard_DS1 {"Standard DS Family Cores"} 82 | Standard_DS2 {"Standard DS Family Cores"} 83 | Standard_DS3 {"Standard DS Family Cores"} 84 | Standard_DS4 {"Standard DS Family Cores"} 85 | Standard_DS11 {"Standard DS Family Cores"} 86 | Standard_DS12 {"Standard DS Family Cores"} 87 | Standard_DS13 {"Standard DS Family Cores"} 88 | Standard_DS14 {"Standard DS Family Cores"} 89 | Standard_DS2_v2 {"Standard DSv2 Family Cores"} 90 | Standard_DS3_v2 {"Standard DSv2 Family Cores"} 91 | Standard_DS4_v2 {"Standard DSv2 Family Cores"} 92 | Standard_DS5_v2 {"Standard DSv2 Family Cores"} 93 | Standard_DS11_v2 {"Standard DSv2 Family Cores"} 94 | Standard_DS12_v2 {"Standard DSv2 Family Cores"} 95 | Standard_DS13_v2 {"Standard DSv2 Family Cores"} 96 | Standard_DS14_v2 {"Standard DSv2 Family Cores"} 97 | Standard_DS15_v2 {"Standard DSv2 Family Cores"} 98 | Standard_F1 {"Standard F Family Cores"} 99 | Standard_F2 {"Standard F Family Cores"} 100 | Standard_F4 {"Standard F Family Cores"} 101 | Standard_F8 {"Standard F Family Cores"} 102 | Standard_F16 {"Standard F Family Cores"} 103 | Standard_F1s {"Standard FS Family Cores"} 104 | Standard_F2s {"Standard FS Family Cores"} 105 | Standard_F4s {"Standard FS Family Cores"} 106 | Standard_F8s {"Standard FS Family Cores"} 107 | Standard_F16s {"Standard FS Family Cores"} 108 | Standard_G1 {"Standard G Family Cores"} 109 | Standard_G2 {"Standard G Family Cores"} 110 | Standard_G3 {"Standard G Family Cores"} 111 | Standard_G4 {"Standard G Family Cores"} 112 | Standard_G5 {"Standard G Family Cores"} 113 | Standard_GS1 {"Standard GS Family Cores"} 114 | Standard_GS2 {"Standard GS Family Cores"} 115 | Standard_GS3 {"Standard GS Family Cores"} 116 | Standard_GS4 {"Standard GS Family Cores"} 117 | Standard_GS5 {"Standard GS Family Cores"} 118 | Standard_H8 {"Standard H Family Cores"} 119 | Standard_H16 {"Standard H Family Cores"} 120 | Standard_H8m {"Standard H Family Cores"} 121 | Standard_H16m {"Standard H Family Cores"} 122 | Standard_H16r {"Standard H Family Cores"} 123 | Standard_H16mr {"Standard H Family Cores"} 124 | Standard_L4s {"Standard LS Family Cores"} 125 | Standard_L8s {"Standard LS Family Cores"} 126 | Standard_L16s {"Standard LS Family Cores"} 127 | Standard_L32s {"Standard LS Family Cores"} 128 | Standard_NC6 {"Standard NC Family Cores"} 129 | Standard_NC12 {"Standard NC Family Cores"} 130 | Standard_NC24 {"Standard NC Family Cores"} 131 | Standard_NC24r {"Standard NC Family Cores"} 132 | Standard_NV6 {"Standard NV Family Cores"} 133 | Standard_NV12 {"Standard NV Family Cores"} 134 | Standard_NV24 {"Standard NV Family Cores"} 135 | default {"Unknown Please Investigate"} 136 | } 137 | 138 | 139 | $currentusage = Get-AzureRmVMUsage -Location $deployloc 140 | 141 | $currentusage = $currentusage | where-object {$vmFamSize -Like $_.Name.LocalizedValue} 142 | 143 | $currentusagecount = $currentusage.Limit - $currentusage.CurrentValue 144 | 145 | if ($totalvmFamSizeCount -le $currentusagecount){ 146 | Write-Host -ForegroundColor Green "We will use" $totalvmFamSizeCount "out of avaliable" $currentusagecount "Cores" 147 | $newtotal = $currentusagecount - $totalvmFamSizeCount 148 | Write-Host " " 149 | Write-Host -ForegroundColor Green "We are good to go after deployment" $newtotal $currentusage.Name.LocalizedValue "remain" 150 | } 151 | Else { 152 | $newtotal = $currentusagecount - $totalvmFamSizeCount 153 | Write-Host -ForegroundColor Red $currentusage.Name.LocalizedValue "required exceeds cores avaliable, submit a ticket to increase cores at a minimium of" $newtotal 154 | } -------------------------------------------------------------------------------- /DDoSThresholds.ps1: -------------------------------------------------------------------------------- 1 | # Outputfile for ddos thresholds 2 | 3 | $path = "C:\temp\ddosthresholds.txt" 4 | $csvpath = "C:\temp\ddosthresholds.csv" 5 | $outputFile = $path 6 | 7 | #Set and apply 1st line of csv headers 8 | $string = "PublicIpName,SYN-PPSThreshold,TCP-PPSThreshold,UDP-PPSThreshold,AzureSubscription,AttachedType,ResourceAttachedName,PublicIpResourceId" 9 | $string | Out-File $outputFile -append -force 10 | 11 | # get all Azure subscriptions to check 12 | $Subs = Get-AzSubscription 13 | 14 | # For each subscription check Public Ips and Metrics 15 | foreach($sub in $subs){ 16 | 17 | #Set Azure Subscription Contect to current subscription in loop 18 | Set-AzContext -Subscription $sub.Id 19 | 20 | # get all Pulic IP Addresses 21 | $PIPs = Get-AzPublicIpAddress 22 | 23 | #Check for each public IP for DDoS Metrics and record 24 | foreach($PIP in $PIPs){ 25 | 26 | # Check for SYN,TCP,UDP Thresholds in Azure Metrics 27 | $syntrigger = (Get-AzMetric -ResourceId $PIP.Id -MetricName DDoSTriggerSYNPackets).Data 28 | $tcptrigger = (Get-AzMetric -ResourceId $PIP.Id -MetricName DDoSTriggerTCPPackets).Data 29 | $udptrigger = (Get-AzMetric -ResourceId $PIP.Id -MetricName DDoSTriggerUDPPackets).Data 30 | 31 | # Check IF the metric has data if so then write and record maximium threshold. ## NOTE you could add other fields like TimeStamp, Minimum, Average, Total, Count - not sure these contain data ? 32 | if($syntrigger -ne $null){ 33 | 34 | Write-Host $PIP.Name " SYN Packets to Trigger" -ForegroundColor Cyan 35 | 36 | #grab the latest entry from last hour at array item 59 37 | Write-Host "Max " $syntrigger[59].Maximum 38 | 39 | } 40 | 41 | if($tcptrigger -ne $null){ 42 | 43 | Write-Host $PIP.Name " TCP Packets to Trigger" -ForegroundColor Yellow 44 | 45 | #grab the latest entry from last hour at array item 59 46 | Write-Host "Max " $tcptrigger[59].Maximum 47 | 48 | } 49 | 50 | if($tcptrigger -ne $null){ 51 | 52 | Write-Host $PIP.Name " UDP Packets to Trigger" -ForegroundColor Green 53 | 54 | #grab the latest entry from last hour at array item 59 55 | Write-Host "Max " $udptrigger[59].Maximum 56 | 57 | #Write into and append into output file 58 | $string = "$($PIP.Name),$($syntrigger[59].Maximum),$($tcptrigger[59].Maximum),$($udptrigger[59].Maximum),$($Sub.Name),$(($PIP.IpConfiguration.Id).Split('/')[7]),$(($PIP.IpConfiguration.Id).Split('/')[8]),$($PIP.Id)" 59 | $string | Out-File $outputFile -append -force 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | # Once done import the data into excel 67 | 68 | $CSV = Import-Csv -Path $path 69 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation 70 | -------------------------------------------------------------------------------- /DefenderforPricing.ps1: -------------------------------------------------------------------------------- 1 |  2 | $Free = 'Free' 3 | $Standard = 'Standard' 4 | 5 | $subs = Get-AzSubscription 6 | 7 | foreach($sub in $subs){ 8 | 9 | # set a particular subscription context to search for VMs 10 | Set-AzContext -Subscription $Sub.Id 11 | 12 | Get-AzSecurityPricing -Name CloudPosture 13 | # Set-AzSecurityPricing -Name CloudPosture -PricingTier $Free 14 | 15 | # Set-AzSecurityPricing -Name VirtualMachines -PricingTier $ 16 | # Set-AzSecurityPricing -Name SqlServers -PricingTier $ 17 | # Set-AzSecurityPricing -Name AppServices -PricingTier $ 18 | # Set-AzSecurityPricing -Name StorageAccounts -PricingTier $ 19 | # Set-AzSecurityPricing -Name SqlServerVirtualMachines -PricingTier $ 20 | # Set-AzSecurityPricing -Name KeyVaults -PricingTier $ 21 | # Set-AzSecurityPricing -Name Dns -PricingTier $ 22 | # Set-AzSecurityPricing -Name Arm -PricingTier $ 23 | # Set-AzSecurityPricing -Name OpenSourceRelationalDatabases -PricingTier $ 24 | # Set-AzSecurityPricing -Name CosmosDbs -PricingTier $ 25 | # Set-AzSecurityPricing -Name Containers -PricingTier $ 26 | } -------------------------------------------------------------------------------- /ExemptionReport.ps1: -------------------------------------------------------------------------------- 1 | ## REQS ## You will need ARMClient, AZ CLI, Az, and Az.Security modules installed 2 | 3 | # Variables to report exemptions on 4 | 5 | #Login into Azure enviroment 6 | Login-AzAccount 7 | ARMClient.exe azlogin 8 | 9 | #Path for report file 10 | $csvpath = "C:\temp\exemptionreport.csv" 11 | 12 | # File check to overwrite existing exemptioncoveragereport.csv 13 | $filecheck = Get-FileHash -Path $path 14 | 15 | If ($filecheck.Path -eq $path) 16 | { 17 | Remove-Item -Path $path 18 | Write-host "Removed Previous File" 19 | } 20 | else 21 | { 22 | Write-host "No Previous File Found" 23 | } 24 | 25 | # File check to overwrite existing asccoveragereport.csv 26 | $filecheck2 = Get-FileHash -Path $csvpath 27 | 28 | If ($filecheck2.Path -eq $csvpath) 29 | { 30 | Remove-Item -Path $csvpath 31 | Write-host "Removed Previous File" 32 | } 33 | else 34 | { 35 | Write-host "No Previous File Found" 36 | } 37 | 38 | 39 | $outputFile = $csvpath 40 | 41 | # write out headers of CSV 42 | $exempstring = "SubscriptionName,SubscriptionId,ResourceName,ResourceType,ExmeptionName,Category,Notes,PolicyDefIds,CreatedBy,CreatedOn,ExpiresOn" 43 | $exempstring | Out-File $outputFile -append -force 44 | 45 | # gather all subscriptions 46 | $subs = Get-AzSubscription 47 | 48 | # For each subscription set contect and invoke REST GET policyExemptions API 49 | Foreach ($sub in $subs){ 50 | 51 | # Set subscription context 52 | Set-AzContext -SubscriptionId $subid 53 | 54 | #Subscription Id 55 | $subid = $sub.Id 56 | 57 | 58 | # ARM Call URL invoke REST GET policyExemptions API 59 | $armcall = "/subscriptions/" + $subid + "/providers/Microsoft.Authorization/policyExemptions?api-version=2020-07-01-preview" 60 | 61 | # Make ARM Client call for GET policyExemptions API 62 | $exemptions = armclient GET $armcall 63 | 64 | # convert the exemptions from JSON into table lists 65 | $exemptionaudits = $exemptions | ConvertFrom-Json 66 | 67 | # Format exemptions table lists values 68 | $exemptionaudits = $exemptionaudits.value 69 | 70 | # fore each table exemption item 71 | foreach ($exemptionaudit in $exemptionaudits) { 72 | 73 | # generate a variable for the Provider Type 74 | $providertype = $exemptionaudit.Id.split(“/”)[6] 75 | 76 | # generate a variable for the Resource name 77 | $resourcename = $exemptionaudit.Id.split(“/”)[8] 78 | 79 | #generate the table list entry into the report 80 | #"SubscriptionName,SubscriptionId,ResourceName,ResourceType,ExmeptionName,Category,Notes,PolicyDefIds,CreatedBy,CreatedOn,ExpiresOn" 81 | $exempstring = "$($sub.Name),$($subid),$($resourcename),$($providertype),$($exemptionaudit.properties.displayName),$($exemptionaudit.properties.exemptionCategory),$($exemptionaudit.properties.description),$($exemptionaudit.properties.policyDefinitionReferenceIds),$($exemptionaudit.systemData.createdBy),$($exemptionaudit.systemData.createdAt),$($exemptionaudit.properties.expiresOn)" 82 | 83 | #Write into and append into output file 84 | $exempstring | Out-File $outputFile -append -force 85 | 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /Export-AzLaSavedSearches.ps1: -------------------------------------------------------------------------------- 1 | Login-AzAccount 2 | 3 | # define variables 4 | $rgname = "YOUR RG NAME" 5 | $workspacename = "YOUR WORKSPACE NAME" 6 | $LogFilePath = "c:\temp\kql\" 7 | 8 | 9 | # get all saved searches 10 | $savedsearches = Get-AzOperationalInsightsSavedSearch -ResourceGroupName $rgname -WorkspaceName $workspacename 11 | 12 | # run through each saved search 13 | foreach ($savedsearch in $savedsearches.Value) { 14 | 15 | # obtain the KQL query data 16 | $savedkql = $savedsearch.Properties.Query 17 | 18 | #generate a filename using Category and Saved Search query name 19 | $filename = $savedsearch.Properties.Category + '_-_' + $savedsearch.Properties.DisplayName 20 | 21 | $LogFile = $LogFilePath + $filename + ".txt" 22 | 23 | try { 24 | # output saved search kql to a new file 25 | $run = Write-Output $savedkql | Out-File -FilePath $LogFile -ErrorAction Stop 26 | } 27 | 28 | # sometimes saved searches may have a illegal character for a windows file path name 29 | catch { 30 | 31 | #genrate a new guid name 32 | $newguid = New-Guid 33 | 34 | #filename will be guid now because it had a illegal char throwing an error we want to ensure the kql query is saved 35 | $filename = $savedsearch.Properties.Category + '_-_' + $newguid 36 | $LogFile = $LogFilePath + $filename + ".txt" 37 | 38 | # output saved search kql to a new file 39 | $run = Write-Output $savedkql | Out-File -FilePath $LogFile 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Export-AzurePolicyResources.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | A script to export all the azure policy resources for use with offline and github 4 | 5 | .NOTES 6 | AUTHOR: Nathan Swift 7 | LASTEDIT: Jan 16, 2021 8 | FUTURES: 9 | 1. export initiatives and assignments 10 | 2. integrate and push into github repo 11 | 3. generate markdown and category 12 | 4. convert targeted builtins into customs 13 | 5. general error handling and folder \ file overwrite 14 | #> 15 | 16 | #Inspired from: https://docs.microsoft.com/en-us/azure/governance/policy/how-to/export-resources 17 | 18 | # place to export locally 19 | $path = 'C:\temp\policyexports\policies\' 20 | 21 | #collect all custom policy defintions, could place a # before | where to collect all definitions 22 | $policydefs = Get-AzPolicyDefinition | Where-Object {$_.Properties.PolicyType -match 'Custom' } 23 | 24 | 25 | ## Future sections 26 | #$initiatives = Get-AzPolicySetDefinition 27 | #$assignments = Get-AzPolicyAssignment 28 | 29 | # loop through to export 30 | foreach ($policydef in $policydefs){ 31 | 32 | <# 33 | .ERROR HANDLING NEEDED: 34 | 1. New-Item : The given path's format is not supported 35 | 2. Out-File : Cannot perform operation because the wildcard path 36 | #> 37 | 38 | # unique policy display name and guid for folder creation 39 | $folderpath = $path + $policydef.Properties.DisplayName + '_' + $policydef.Name 40 | 41 | #create folder 42 | New-Item $folderpath -itemtype directory 43 | 44 | #export and create azure policy in proper path 45 | $filepath = $folderpath + '\policy.json' 46 | $policydef | ConvertTo-Json -Depth 10 | Out-File -FilePath $filepath 47 | 48 | ## Future use with category and markdown file generation 49 | #$policydefs[0].Properties.Metadata.category 50 | 51 | } -------------------------------------------------------------------------------- /FindPaaSIPApplyUDR.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 10/30/2018 5:21 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | ################################################################################ 7 | 8 | <# 9 | Current Services: 10 | 11 | Storage Account 12 | PaaS SQL DB 13 | DataLakeGen 14 | 15 | #> 16 | 17 | <# Possible Futures: 18 | 19 | #> 20 | ## Subscription name here 21 | $Sub = "Subscription Name Here" 22 | 23 | # Authenticate Piece 24 | Login-AzureRmAccount 25 | 26 | # Set the current Azure Subscription to pull information from 27 | Set-AzureRmContext -Subscription $Sub 28 | 29 | $datetimestart = Get-Date 30 | 31 | # This function will look up DNS hostname passed into it and respond and return the ipv4 response value 32 | Function Get-DnsEntry($iphost) 33 | 34 | { 35 | 36 | If($ipHost -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") 37 | 38 | { 39 | 40 | [System.Net.Dns]::GetHostEntry($iphost).HostName 41 | 42 | } 43 | 44 | ElseIf( $ipHost -match "^.*\.\.*") 45 | 46 | { 47 | 48 | [System.Net.Dns]::GetHostEntry($iphost).AddressList[0].IPAddressToString 49 | 50 | } 51 | 52 | ELSE { Throw "Specify either an IP V4 address or a hostname" } 53 | 54 | } 55 | 56 | 57 | { 58 | 59 | 60 | 61 | } 62 | 63 | #Collect all resources in current context Subscription 64 | $Resources = Get-AzureRmResource -ExpandProperties 65 | 66 | # Loop and iterate through all Azure Resources to begin collecting data 67 | foreach ($Resource in $Resources) { 68 | 69 | # Type of Azure resource 70 | $RGName = $Resource.ResourceGroupName 71 | $Name = $Resource.name 72 | $Type = $Resource.resourcetype 73 | 74 | # Set variable $URL to NOTSET to prevent azure services that do not contain public enpoint from being executed against 75 | $URL = "NOTSET" 76 | 77 | try { 78 | 79 | switch -Regex ($Type) { 80 | ## Storage Accounts 81 | "Microsoft.Storage/storageAccounts$" { 82 | #storage accounts are weird, so we have to wire the output up differently 83 | "blob", "file", "queue", "table" | ForEach-Object { 84 | $URL = "$Name.$_.core.windows.net" 85 | $RouteAdd = Get-DnsEntry $URL 86 | $RouteAdd = $RouteAdd+"/32" 87 | Get-AzureRmRouteTable -ResourceGroupName "rgSampleRT" -Name "Sample-RT" | Add-AzureRmRouteConfig -Name $URL -AddressPrefix $RouteAdd -NextHopType "Internet" | Set-AzureRmRouteTable 88 | }; break 89 | } 90 | ## SQL 91 | "Microsoft.Sql/servers$" { $URL = $Resource.Properties.fullyQualifiedDomainName 92 | $RouteAdd = Get-DnsEntry $URL 93 | $RouteAdd = $RouteAdd+"/32" 94 | Get-AzureRmRouteTable -ResourceGroupName "rgSampleRT" -Name "Sample-RT" | Add-AzureRmRouteConfig -Name $URL -AddressPrefix $RouteAdd -NextHopType "Internet" | Set-AzureRmRouteTable 95 | ; break } 96 | ## DataLake 97 | "Microsoft.DataLakeStore/accounts$" { $URL = $Resource.Properties.endpoint 98 | $RouteAdd = Get-DnsEntry $URL 99 | $RouteAdd = $RouteAdd+"/32" 100 | Get-AzureRmRouteTable -ResourceGroupName "rgSampleRT" -Name "Sample-RT" | Add-AzureRmRouteConfig -Name $URL -AddressPrefix $RouteAdd -NextHopType "Internet" | Set-AzureRmRouteTable 101 | ; break } 102 | } 103 | } Catch [Exception] { 104 | Write-Error "An error occurred: $_" 105 | } 106 | } #foreach($Resource in $Resources) 107 | 108 | # Time Tracking Finished 109 | $datetimeend = Get-Date 110 | Write-Host "Started on $datetimestart" 111 | Write-Host "Finished on $datetimeend" 112 | -------------------------------------------------------------------------------- /Get-OIDCSAuditEvents.ps1: -------------------------------------------------------------------------------- 1 | # variables to authenticate and generate OCI Access Token 2 | $b64clientidsecret = "BASE64 CLIENTID:CLIENTKEY" 3 | $IDCS = "idcs-XXXXXXXXXXXX.identity.oraclecloud.com" 4 | $siguri = "https://" + $IDCS + "/oauth2/v1/token" 5 | 6 | # variables to Invoke and search for OCIAudit_CL latest table entry \ and HTTP DATA Collector API 7 | $workspaceID = "LA WORKSPACEID" 8 | $CustomerId = "LA WORKSPACEID" 9 | $workspaceKey = "LA WORKSPACEKEY" 10 | $SharedKey = "LA WORKSPACEKEY" 11 | $LogType = "IDCSAudit_CL" 12 | $timeStampField = "timestamp" 13 | 14 | # Form the Header and Body Request to obtain OCI Access Token 15 | $sigHeaders = @{ 16 | 'Authorization' = 'Basic ' + $b64clientidsecret 17 | 'Content-Type' = 'application/x-www-form-urlencoded;charset=UTF-8' 18 | } 19 | 20 | $sigBody = "grant_type=client_credentials&scope=urn:opc:idm:__myscopes__" 21 | 22 | # Invoke to obtain OCI access token 23 | $sig = (Invoke-RestMethod -Uri $siguri -Method POST -Headers $sigHeaders -Body $sigBody -Verbose).access_token 24 | 25 | # Invoke and search for OCIAudit_CL latest table entry if found pass datetime value, if not pass a time generated from 5 days ago. 26 | $starttime = (Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceID -Query "union isfuzzy=true (IDCSAudit_CL | summarize arg_max(TimeGenerated , TimeGenerated ) |project TimeGenerated ) | summarize arg_max(TimeGenerated , TimeGenerated ) | project TimeGenerated" -ErrorAction SilentlyContinue).Results.TimeGenerated 27 | 28 | # Conditional check if OCIAudit_CL table does not exist need to prime and pump, set starttime a day ago 29 | If (!$starttime) { 30 | 31 | #some additional if \ then to spot check if OCIAudit_CL table exists ? 32 | $starttime = (Get-date).AddDays(-1).ToString('yyyy-MM-ddThh:mm:ssZ') #.ffffZ 33 | 34 | } 35 | 36 | 37 | # HTTP DATA Collector Functions 38 | 39 | # Create the function to create the authorization signature 40 | Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource) 41 | { 42 | $xHeaders = "x-ms-date:" + $date 43 | $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource 44 | 45 | $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) 46 | $keyBytes = [Convert]::FromBase64String($sharedKey) 47 | 48 | $sha256 = New-Object System.Security.Cryptography.HMACSHA256 49 | $sha256.Key = $keyBytes 50 | $calculatedHash = $sha256.ComputeHash($bytesToHash) 51 | $encodedHash = [Convert]::ToBase64String($calculatedHash) 52 | $authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash 53 | return $authorization 54 | } 55 | 56 | 57 | # Create the function to create and post the request 58 | Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType) 59 | { 60 | $method = "POST" 61 | $contentType = "application/json" 62 | $resource = "/api/logs" 63 | $rfc1123date = [DateTime]::UtcNow.ToString("r") 64 | $contentLength = $body.Length 65 | $signature = Build-Signature ` 66 | -customerId $customerId ` 67 | -sharedKey $sharedKey ` 68 | -date $rfc1123date ` 69 | -contentLength $contentLength ` 70 | -method $method ` 71 | -contentType $contentType ` 72 | -resource $resource 73 | $uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01" 74 | 75 | $headers = @{ 76 | "Authorization" = $signature; 77 | "Log-Type" = $logType; 78 | "x-ms-date" = $rfc1123date; 79 | "time-generated-field" = $TimeStampField; 80 | } 81 | 82 | $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing 83 | return $response.StatusCode 84 | 85 | } 86 | 87 | # Invoke to OCI AuditEvents using start time from tableresult 88 | $headers = @{ 89 | 'Authorization' = 'Bearer ' + $sig 90 | 'Content-Type' = 'application/scim+json' 91 | } 92 | 93 | # get the datetime from current run and format for OCI API Call to end the lookup range 94 | $endtime = (Get-date -UFormat %Y-%m-%dT%R:%SZ) 95 | 96 | 97 | # Create the URI needed to invoke AuditEvents in OCI | BASE URI for testing #$audituri = "https://" + $IDCS + "/admin/v1/AuditEvents" 98 | $audituri = "https://" + $IDCS + "/admin/v1/AuditEvents" + "?sortBy=timestamp&sortOrder=descending&filter=timestamp ge " + '"' + $starttime + '"' + " and timestamp le " + '"' + $endtime + '"' 99 | 100 | # Invoke OCI AuditEvents API to obtain results 101 | $auditresults = (Invoke-WebRequest -Uri $audituri -Method GET -Headers $headers).Content | ConvertFrom-Json 102 | 103 | # define loop end upper boundary of pagination in this call 104 | $loopend = $auditresults.totalResults 105 | 106 | # define the index start at result 1 for loop 107 | $counter = 1 108 | 109 | # Conditional check are the total results less than the pagination (defualtr 50 results per query, using index to start at), do we need to loop ?, default is 50 items per page but can be changed to 1000 in URI | count=1000 110 | if ($loopend -le $auditresults.itemsPerPage) { 111 | 112 | # format the the first OCI AuditEvents API call into a JSON body 113 | $jsonbody = $auditresults.Resources | ConvertTo-Json 114 | 115 | # Use for testing uncomment below to writeout files to ensure while loop for pagination is working correctly. BE SURE TO COMMENT OUT Post-LogAnalyticsData line when tshooting. 116 | #$jsonbody | Out-File -FilePath c:\temp\jsonindex$counter.json 117 | 118 | # Send the JSONBody results to Azure Sentinel 119 | Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($jsonbody)) -logType $logType 120 | 121 | } else { 122 | 123 | # Conditional chekc found that there are more results that per query result, leading to pagination. We will loop through and update the StartIndex each time to get the next batch of 50 results untill finished 124 | do { 125 | 126 | # Create the URI needed to invoke AuditEvents in OCI using the StartIndex=$counter 127 | $audituri = "https://" + $IDCS + "/admin/v1/AuditEvents?startIndex=" + $counter + "&sortBy=timestamp&sortOrder=descending&filter=timestamp ge " + '"' + $starttime + '"' + " and timestamp le " + '"' + $endtime + '"' 128 | 129 | # Invoke OCI AuditEvents API to obtain results 130 | $auditresults = (Invoke-WebRequest -Uri $audituri -Method GET -Headers $headers).Content | ConvertFrom-Json 131 | 132 | # format the the first OCI AuditEvents API call into a JSON body 133 | $jsonbody = $auditresults.Resources | ConvertTo-Json 134 | 135 | # Use for testing uncomment below to writeout files to ensure while loop for pagination is working correctly. BE SURE TO COMMENT OUT Post-LogAnalyticsData line when tshooting. 136 | #$jsonbody | Out-File -FilePath c:\temp\jsonindex$counter.json 137 | 138 | # Send the JSONBody results to Azure Sentinel 139 | Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($jsonbody)) -logType $logType 140 | 141 | # update pagination counter for the next set of results (defualt is 50 unless specified in URI to count=xxxx where xxxx is max value of 1000) 142 | $counter = $counter + $auditresults.itemsPerPage 143 | 144 | # Continue the loop untill we are greater than the total AuditEvents pagination results 145 | } while ($counter -lt $loopend) 146 | } -------------------------------------------------------------------------------- /IAMReport.ps1: -------------------------------------------------------------------------------- 1 | ## this is more a snippet than a PowerShell Runbook for Automation accounts, but could be easily modified. 2 | 3 | ## Foreach \ foreach is slow, reworking next version of script to open up parallelism and also utilize Switch rather than If 4 | 5 | #Authenticate into ARM 6 | 7 | #Authenticate into AAD 8 | $scriptdatetime = get-date 9 | Write-host $scriptdatetime 10 | 11 | $date = Get-Date -format R 12 | $filename = "IAMReport.csv" 13 | 14 | 15 | # Azure Storage Account Name, Container Name, and Storage Key 16 | ## Could rewrite to call a secret from KeyVault, store key in Automation Variable 17 | $StorageAccountName = '' 18 | 19 | $ContainerName = '' 20 | 21 | $StorageAccountKey = '' 22 | 23 | 24 | 25 | $ctx = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey 26 | 27 | $IAMReportFile = Get-AzureStorageBlob -Container $containername -Blob $filename -context $ctx| Get-AzureStorageBlobContent -Force 28 | 29 | #$statsObj = New-Object PSCustomObject 30 | 31 | $Subs = Get-AzureRmSubscription 32 | 33 | 34 | foreach ($Sub in $Subs ) { 35 | 36 | #In case we need global subscription properties 37 | #$SubIdValue = $Sub.SubscriptionId 38 | #$SubNameValue = $Sub.SubscriptionName 39 | #$TenantTrustIdValue = $Sub.TenantId 40 | 41 | 42 | $TenantId = $Sub.TenantId 43 | $SubscriptionName = $Sub.SubscriptionName 44 | 45 | $iams = Get-AzureRmRoleAssignment 46 | 47 | foreach ($iam in $iams ) { 48 | 49 | $statsObj = New-Object PSCustomObject 50 | 51 | $statsObj | add-member -NotePropertyName DisplayName -NotePropertyValue $iam.DisplayName 52 | $statsObj | add-member -NotePropertyName SignInName -NotePropertyValue $iam.SignInName 53 | $statsObj | add-member -NotePropertyName Type -NotePropertyValue $iam.ObjectType 54 | $statsObj | add-member -NotePropertyName RoleDefinition -NotePropertyValue $iam.RoleDefinitionName 55 | $statsObj | add-member -NotePropertyName TenantId -NotePropertyValue $TenantId 56 | 57 | 58 | 59 | $iamscopesplit = ($iam.Scope) -split '/' 60 | 61 | $iamscopesplintcount = [regex]::matches($iam.scope,"/").count 62 | 63 | If ($iamscopesplintcount -eq 2){ 64 | 65 | $subid = $iamscopesplit[2] 66 | $subname = (Get-AzureRmSubscription -SubscriptionId $subid).SubscriptionName 67 | 68 | #$iamscopename = "/" + $iamscopesplit[1] + "/" + $subname 69 | $statsObj | add-member -NotePropertyName Scope -NotePropertyValue $iam.Scope 70 | $statsObj | add-member -NotePropertyName ScopeType -NotePropertyValue "Subscription" 71 | $statsObj | add-member -NotePropertyName SubscriptionName -NotePropertyValue $subname 72 | $statsObj | add-member -NotePropertyName Provider -NotePropertyValue "none" 73 | $statsObj | add-member -NotePropertyName TimeStamp -NotePropertyValue $date 74 | 75 | } 76 | ElseIf ($iamscopesplintcount -eq 4) { 77 | 78 | $subid = $iamscopesplit[2] 79 | $subname = (Get-AzureRmSubscription -SubscriptionId $subid).SubscriptionName 80 | 81 | #$iamscopename = "/" + $iamscopesplit[1] + "/" + $subname + "/" + $iamscopesplit[3] + "/" + $iamscopesplit[4] 82 | $statsObj | add-member -NotePropertyName Scope -NotePropertyValue $iam.Scope 83 | $statsObj | add-member -NotePropertyName ScopeType -NotePropertyValue "ResourceGroup" 84 | $statsObj | add-member -NotePropertyName SubscriptionName -NotePropertyValue $subname 85 | $statsObj | add-member -NotePropertyName Provider -NotePropertyValue "none" 86 | $statsObj | add-member -NotePropertyName TimeStamp -NotePropertyValue $date 87 | } 88 | ElseIf ($iamscopesplintcount -eq 8) { 89 | 90 | $subid = $iamscopesplit[2] 91 | $subname = (Get-AzureRmSubscription -SubscriptionId $subid).SubscriptionName 92 | 93 | #$iamscopename = "/" + $iamscopesplit[1] + "/" + $subname + "/" + $iamscopesplit[3] + "/" + $iamscopesplit[4] + "/" + $iamscopesplit[5] + "/" + $iamscopesplit[6] + "/" + $iamscopesplit[7] + "/" + $iamscopesplit[8] 94 | $statsObj | add-member -NotePropertyName Scope -NotePropertyValue $iam.Scope 95 | $statsObj | add-member -NotePropertyName ScopeType -NotePropertyValue "Resource" 96 | $statsObj | add-member -NotePropertyName SubscriptionName -NotePropertyValue $subname 97 | $statsObj | add-member -NotePropertyName Provider -NotePropertyValue $iamscopesplit[6] 98 | $statsObj | add-member -NotePropertyName TimeStamp -NotePropertyValue $date 99 | } 100 | Else { 101 | $statsObj | add-member -NotePropertyName Scope -NotePropertyValue $iam.Scope + "Unknown Count - " + $iamscopesplintcount 102 | } 103 | ##Build out for RoleAssigmentId translation if desired 104 | #$iamroleassignsplit = ($iam.RoleAssignmentId) -split '/' 105 | #$iamroleassignsplitcount = [regex]::matches($iam.RoleAssignmentId,"/").count 106 | 107 | $statsObj | Export-Csv $filename -NoTypeInformation -Delimiter "," -append 108 | 109 | $file = gci $filename 110 | 111 | Set-AzureStorageBlobContent -File $file.FullName -Container $ContainerName -Blob $filename -Context $ctx -Force 112 | } 113 | 114 | 115 | 116 | } 117 | 118 | $scriptdatetime = get-date 119 | Write-host $scriptdatetime 120 | -------------------------------------------------------------------------------- /IgniteDownloaderV2.ps1: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | ## Author: David das Neves 3 | ## Date: 09.11.2019 4 | ## Modified: Nathan Swift 5 | ## Date: 11.11.2019 6 | ## Description: Filtering of Ignite session files with OutGridView 7 | ## Downloading with 10 downloads in parallel including resume functionality 8 | ######################################################################################### 9 | 10 | # Start PowerShell as admin 11 | #via code: Start-Process powershell -Verb runAs 12 | 13 | 14 | # have a look at VSCode to switch between pwsh versions 15 | 16 | #################################################################################### 17 | ## Needs only to be run once 18 | #################################################################################### 19 | 20 | #region prep 21 | 22 | #Path to download all the Ignite videos 23 | $downloadPath = "C:\Ignite\" #you can modify that 24 | 25 | #Creating Path 26 | if (!(Test-Path $downloadPath)) { 27 | New-Item $downloadPath -ItemType Directory 28 | } 29 | 30 | #Installing PowerShell 7 - add to path! 31 | #Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI -Preview" 32 | 33 | <# not included, yet - still some issues 34 | ## Installing OutGridView to pwsh v7 35 | pwsh-preview -c "Install-Module Microsoft.PowerShell.GraphicalTools" -Force 36 | #> 37 | 38 | #endregion 39 | 40 | ############################################################################################################# 41 | ## If you want to create a new download job - execute the code starting from here with PowerShell version 5 42 | ############################################################################################################# 43 | 44 | #region LoadingAllSessions 45 | 46 | #Load all sessions - only from online because some download URIs are still being added 47 | #Retrieving all Ignite sessions 48 | $allSessions = (Invoke-RestMethod 'https://api-myignite.techcommunity.microsoft.com/api/session/all') | Where-Object { $_.downloadVideoLink } | Select-Object sessionCode, title, topic, speakerNames, level, description, downloadVideoLink, slideDeck 49 | 50 | #endregion 51 | 52 | #Picking sessions with a GridView and serializing the sessions to be downloaded to file for resume. 53 | #region creatingADownloadJob 54 | 55 | #Picking sessions for download 56 | $sessionsToDownload = $allSessions | Out-GridView -PassThru 57 | <#IMPORTANT: You can also work 58 | - with CTRL+A for selecting all 59 | - with CTRL+MouseClick for selecting several sessions 60 | - with Shift+Mouseclick for selecting several sessions in a row 61 | #> 62 | 63 | # OutGridView is not yet available in pwsh v7 #SHAME 64 | # Context: https://devblogs.microsoft.com/powershell/the-next-release-of-powershell-powershell-7/ 65 | 66 | #Serializing the sessions for download to possibly continue after interruption 67 | $sessionsToDownload | Export-Clixml (Join-Path -Path $downloadPath -Childpath "sessionsToDownload.clixml") 68 | 69 | #endregion 70 | 71 | ################################################################################################################ 72 | ## If you want to start or continue a download job - execute the code starting from here in PowerShell version 7 73 | ################################################################################################################ 74 | 75 | #region StartOrResumeADownloadJob 76 | 77 | #Now continue with the new PowerShell version 78 | #Opening up PowerShell 7 - check VSCode to have a better control on versions 79 | # pwsh-preview #Starting PowerShell Preview / Version 7 80 | 81 | #Path to download all the Ignite videos 82 | $downloadPath = "C:\Ignite\" #you can modify that 83 | 84 | #Deserializing sessions for download 85 | $sessionsToDownload = Import-Clixml (Join-Path -Path $downloadPath -Childpath "sessionsToDownload.clixml") 86 | 87 | #Total number of sessions in the download job 88 | $numberOfSessionsToDownload = $($sessionsToDownload.title.Count) 89 | 90 | Write-Host "You are about to download $numberOfSessionsToDownload sessions in total." 91 | #$ErrorActionPreference = 'SilentlyContinue' #already downloaded objects would create an error 92 | 93 | #Cleaning titles and extending the PSCustomObject with a downloadPath 94 | $sessionsToDownloadCleanedAndExtended = $sessionsToDownload | Select-Object *, @{ Name = 'downloadPath'; Expression = { Join-Path -Path $downloadPath -ChildPath ($_.sessionCode + "_" + $($_.title).Substring(0, [Math]::Min(50, $($_.title).length) ) + ".mp4").Replace('?', '').Replace(':', '') } } 95 | $sessionsToDownloadCleanedAndExtendedslidedeck = $sessionsToDownload | Select-Object *, @{ Name = 'downloadPath'; Expression = { Join-Path -Path $downloadPath -ChildPath ($_.sessionCode + "_" + $($_.title).Substring(0, [Math]::Min(50, $($_.title).length) ) + ".pptx").Replace('?', '').Replace(':', '') } } 96 | 97 | 98 | #Download all sessions including sessionCode and title (max length 50) with 10 downloads in parallel 99 | #The downloding files are printed out 100 | $sessionsToDownloadCleanedAndExtendedslidedeck | Foreach-Object -Parallel { Write-Host $_.downloadPath; Invoke-WebRequest $($_.slideDeck) -Resume -OutFile $_.downloadPath} -ThrottleLimit 10 101 | $sessionsToDownloadCleanedAndExtended | Foreach-Object -Parallel { Write-Host $_.downloadPath; Invoke-WebRequest $($_.downloadVideoLink) -Resume -OutFile $_.downloadPath} -ThrottleLimit 10 102 | 103 | #$sessionsToDownloadCleanedAndExtended 104 | #endregion -------------------------------------------------------------------------------- /ImperativeDeclareAzureResourceOwnerTags.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | .DESCRIPTION 4 | An runbook which finds the deployment owner of a Azure resource and applies an owner Tag to the Azure resource gets all the ARM resources using the Run As Account (Service Principal) 5 | 6 | .NOTES 7 | AUTHOR: Nathan Swift 8 | LASTEDIT: Dec 21, 2017 9 | #> 10 | 11 | $connectionName = "AzureRunAsConnection" 12 | try 13 | { 14 | # Get the connection "AzureRunAsConnection " 15 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 16 | 17 | "Logging in to Azure..." 18 | Add-AzureRmAccount ` 19 | -ServicePrincipal ` 20 | -TenantId $servicePrincipalConnection.TenantId ` 21 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 22 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 23 | } 24 | catch { 25 | if (!$servicePrincipalConnection) 26 | { 27 | $ErrorMessage = "Connection $connectionName not found." 28 | throw $ErrorMessage 29 | } else{ 30 | Write-Error -Message $_.Exception 31 | throw $_.Exception 32 | } 33 | } 34 | 35 | 36 | # Obtain resource objects 37 | ##Needs## Filter out existing Azure Resource objects that already have Owner Tag assigned 38 | $azurerescs = Get-AzureRmResource | Where-Object ResourceGroupName -contains rgBBOPS ## < Filter to apply on a RG 39 | 40 | # Execute through each resource object and apply a owner tag based on who deployed the resource 41 | ##Needs## Rewrite to use parallelization using either: PS WorkFlow OR Start-Job > https://github.com/Azure/azure-powershell/releases/tag/v4.4.0-September2017 42 | foreach ($azureresc in $azurerescs){ 43 | 44 | #var for operation name to filter on 45 | $azurerescop = $azureresc.ResourceType + "/write" 46 | 47 | #find operations where resource was created in past 90 days # Activity Log only goes back 90 days unless stored in a db of some sort for historical \ archival purposes 48 | $azurerescvalues = Get-AzureRmLog -StartTime (Get-Date).AddDays(-89) -ResourceId $azureresc.ResourceId -DetailedOutput -Status Succeeded | Where-Object OperationName -contains $azurerescop 49 | 50 | #sometimes the operations logs have multiple entries pick the latest record 51 | ##Needs## If or Switch Logic to determine User identity vs SPN or somthing else. 52 | $owner = $azurerescvalues[0].Caller 53 | 54 | If ($owner -eq $null) 55 | { 56 | write-host "Owner equals null" 57 | } Else { 58 | 59 | 60 | #Obtain existing tags and apply existing Tags plus Owner Tag 61 | $tags = (Get-AzureRmResource -ResourceId $azureresc.ResourceId).Tags 62 | $tags += @{Owner=$owner} 63 | Set-AzureRmResource -ResourceId $azureresc.ResourceId -Tag $tags -Force 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /LogAlertBlockIPonAppGW.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | 5 | .DESCRIPTION 6 | 7 | 8 | DEPENDENCIES 9 | - The runbook must be called from an Azure Log Analytics alert via a webhook. 10 | 11 | REQUIRED AUTOMATION ASSETS 12 | - An Automation connection asset called "AzureRunAsConnection" that is of type AzureRunAsConnection. 13 | - An Automation certificate asset called "AzureRunAsCertificate". 14 | 15 | .PARAMETER WebhookData 16 | Optional. (The user doesn't need to enter anything, but the service always passes an object.) 17 | This is the data that's sent in the webhook that's triggered from the alert. 18 | 19 | .NOTES 20 | AUTHOR: Nathan Swift 21 | LASTEDIT: 2018-11-18 22 | #> 23 | 24 | [OutputType("PSAzureOperationResponse")] 25 | 26 | param 27 | ( 28 | [Parameter (Mandatory=$false)] 29 | [object] $WebhookData 30 | ) 31 | 32 | $ErrorActionPreference = "stop" 33 | 34 | $connectionName = "AzureRunAsConnection" 35 | try 36 | { 37 | # Get the connection "AzureRunAsConnection " 38 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 39 | 40 | "Logging in to Azure..." 41 | Add-AzureRmAccount ` 42 | -ServicePrincipal ` 43 | -TenantId $servicePrincipalConnection.TenantId ` 44 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 45 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 46 | } 47 | catch { 48 | if (!$servicePrincipalConnection) 49 | { 50 | $ErrorMessage = "Connection $connectionName not found." 51 | throw $ErrorMessage 52 | } else{ 53 | Write-Error -Message $_.Exception 54 | throw $_.Exception 55 | } 56 | } 57 | 58 | 59 | ##Manual Testing 60 | #$WebhookData = Get-Content 'C:\temp\alertip14.json' | Out-String | ConvertFrom-Json 61 | 62 | #Static Variables 63 | $NSGname = "YOUR NSG NAME" 64 | $NSGrg = "YOUR NSG RESOURCE GROUP NAME" 65 | [array]$pipentries = {} 66 | 67 | #Take Webhook Data and taketody of Data in alert and convert JSON into PS Object 68 | $WebhookRequestBody = $WebhookData.RequestBody | ConvertFrom-Json 69 | 70 | 71 | 72 | #store the Rows results of alert data into a variable 73 | $rows = $WebhookRequestBody.data.SearchResult.tables.rows | select -Unique 74 | 75 | # Search in each Row Object 76 | foreach ($row in $rows){ 77 | 78 | # Search in each row 79 | ForEach($item in $row) { 80 | # Match for public ip address, data is not consistent in alert row objects 81 | $pip = $item -match "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" 82 | 83 | # If match occurs then write the pip into an array 84 | if ($pip -eq $true){ 85 | Write-Host $item 86 | $pipentries = $pipentries += $item 87 | } 88 | } 89 | } 90 | 91 | # make the array unique entries only and remove the first null entry 92 | $pipentries = $pipentries | Select -Unique 93 | $pipentries = $pipentries -ne $pipentries[0] 94 | 95 | # loop through each pip and create a NSG rule 96 | foreach ($sourceip in $pipentries){ 97 | Write-Host ($sourceip) 98 | 99 | 100 | #/32 CIDR to PIP for NSG rule 101 | $pipcidr = $sourceip+"/32" 102 | 103 | Write-Host ($pipcidr) 104 | 105 | #obtain the NSG you want to add a rule to - Set you unique NSG anme and ResourceGroupName 106 | $NSG = Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg 107 | 108 | #Check the custom rules count and add to the next priority so oes not overlap with existing priority rule 109 | $priority = $NSG.SecurityRules.Priority.Count + 101 110 | 111 | #Construct the NSG Rule based of the pip found and the PIP CIDR found above and apply the new rule to the NSG - Set you unique NSG anme and ResourceGroupName 112 | Get-AzureRmNetworkSecurityGroup -Name $NSGname -ResourceGroupName $NSGrg | Add-AzureRmNetworkSecurityRuleConfig -Name "logrb_$sourceip" -Direction Inbound -Priority $priority -Access Deny -SourceAddressPrefix $pipcidr -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol '*' | Set-AzureRmNetworkSecurityGroup 113 | 114 | } 115 | -------------------------------------------------------------------------------- /MDEExtErrorReport.ps1: -------------------------------------------------------------------------------- 1 | # Outputfile for MDE extension error report thresholds 2 | 3 | $path = "C:\temp\mdeextreport.txt" 4 | $csvpath = "C:\temp\mdeextreport.csv" 5 | $outputFile = $path 6 | 7 | #Set and apply 1st line of csv headers 8 | # FUTURE # ,VM Extension Detailed Errors 9 | $string = "Subscription,VM Name,Location,VM Extension,OS,VM Extension Status,Error Code,Error Description,VM Extension Error, VM Extension Error Details" 10 | $string | Out-File $outputFile -append -force 11 | 12 | # get all subscriptions 13 | $Subs = Get-AzSubscription 14 | 15 | 16 | # for each subscription get all the VMs and their status 17 | foreach($sub in $subs){ 18 | 19 | # set a particular subscription context to search for VMs 20 | Set-AzContext -Subscription $Sub.Id 21 | 22 | # get all the VMs in Subscription including status 23 | $vms = Get-AzVM 24 | 25 | #For each VM check for MDE.windows or MDE.Linux extension and if in a error state record information to csv 26 | foreach($vm in $vms){ 27 | 28 | #get particular extension details of VM 29 | $vme = Get-AzVm -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status 30 | 31 | $loc = 'Azure' 32 | 33 | # conditional check for VM if it has MDE extension 34 | if($vme.Extensions.Name -match "MDE"){ 35 | 36 | # set variable for status level of MDE extension 37 | $level = ($vme.Extensions | where-Object {($_.Name -match "MDE")}).Statuses.level 38 | 39 | # if the mde extension is not successful then generate a entry in the report for the vm and informnationa round mde extension 40 | if($level -ne "Info"){ 41 | 42 | # future build for sub status detailed errors, is in a list, would need to foreach and join into a single string with a unique delimiter 43 | $SubMessages = ($vme.Extensions | where-Object {($_.Name -match "MDE")}).Substatuses.message 44 | 45 | $modstring = $SubMessages.Split('') 46 | $modstring = $modstring.Split("'`n'") 47 | $modstring = $modstring.Split("'`t'") 48 | $modstring = $modstring.Split("'`r'") 49 | $modstring = $modstring.Split('`,') 50 | 51 | $code = (($vme.Extensions | where-Object {($_.Name -match "MDE")}).Statuses.message).split(' ')[16] 52 | 53 | # Define Hashtables for switch lookup rather than IF 54 | $codedesc = switch ($code) 55 | { 56 |        1 {"ERR_INTERNAL"} 57 | 2 {"ERR_INVALID_ARGUMENTS"} 58 | 3 {"ERR_INSUFFICIENT_PRIVILAGES"} 59 | 4 {"ERR_NO_INTERNET_CONNECTIVITY"} 60 | 5 {"ERR_CONFLICTING_APPS"} 61 | 10 {"ERR_UNSUPPORTED_DISTRO"} 62 | 11 {"ERR_UNSUPPORTED_VERSION"} 63 | 12 {"ERR_INSUFFICIENT_REQUIREMENTS"} 64 | 20 {"ERR_MDE_NOT_INSTALLED"} 65 | 21 {"ERR_INSTALLATION_FAILED"} 66 | 22 {"ERR_UNINSTALLATION_FAILED"} 67 | 23 {"ERR_FAILED_DEPENDENCY"} 68 | 24 {"ERR_FAILED_REPO_SETUP"} 69 | 25 {"ERR_INVALID_CHANNEL"} 70 | 26 {"ERR_FAILED_REPO_CLEANUP"} 71 | 30 {"ERR_ONBOARDING_NOT_FOUND"} 72 | 31 {"ERR_ONBOARDING_FAILED"} 73 | 40 {"ERR_TAG_NOT_SUPPORTED"} 74 | 41 {"ERR_PARAMETER_SET_FAILED"} 75 | default {"ERR_UNKNOWNCODE"} 76 | } 77 | 78 | #generate entry for report 79 | $string = "$($sub.Name),$($vme.Name), $($loc), $(($vme.Extensions | where-Object {($_.Name -match "MDE")}).Name), $($vm.StorageProfile.ImageReference.sku), $(($vme.Extensions | where-Object {($_.Name -match "MDE")}).Statuses.level), $($code), $($codedesc), $(($vme.Extensions | where-Object {($_.Name -match "MDE")}).Statuses.Message), $($modstring)" 80 | 81 | #output entry into report 82 | $string | Out-File $outputFile -append -force 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | $arcs = Get-AzConnectedMachine 91 | 92 | foreach($arc in $arcs){ 93 | 94 | #get particular extension details of VM 95 | $vme = Get-AzConnectedMachine -ResourceGroupName $arc.ResourceGroupName -Name $arc.Name 96 | 97 | $loc = 'Arc' 98 | 99 | $vmeext = Get-AzConnectedMachineExtension -Machinename $arc.Name -ResourceGroupName $arc.ResourceGroupName 100 | 101 | # conditional check for VM if it has MDE extension 102 | if($vmeext.Name -match "MDE"){ 103 | 104 | # set variable for status level of MDE extension 105 | $level = ($vmeext | where-Object {($_.Name -match "MDE")}).statuslevel 106 | 107 | # if the mde extension is not successful then generate a entry in the report for the vm and informnationa round mde extension 108 | if($level -ne "Info"){ 109 | 110 | # future build for sub status detailed errors, is in a list, would need to foreach and join into a single string with a unique delimiter 111 | $SubMessages = (($vmeext | where-Object {($_.Name -match "MDE")}).StatusMessage) 112 | 113 | $modstring = $SubMessages.Split('') 114 | $modstring = $modstring.Split("'`n'") 115 | $modstring = $modstring.Split("'`t'") 116 | $modstring = $modstring.Split("'`r'") 117 | $modstring = $modstring.Split('`,') 118 | 119 | $code = (($vmeext | where-Object {($_.Name -match "MDE")}).StatusMessage).split(' ')[19] 120 | 121 | # Define Hashtables for switch lookup rather than IF 122 | $codedesc = switch ($code) 123 | { 124 |        1 {"ERR_INTERNAL"} 125 | 2 {"ERR_INVALID_ARGUMENTS"} 126 | 3 {"ERR_INSUFFICIENT_PRIVILAGES"} 127 | 4 {"ERR_NO_INTERNET_CONNECTIVITY"} 128 | 5 {"ERR_CONFLICTING_APPS"} 129 | 10 {"ERR_UNSUPPORTED_DISTRO"} 130 | 11 {"ERR_UNSUPPORTED_VERSION"} 131 | 12 {"ERR_INSUFFICIENT_REQUIREMENTS"} 132 | 20 {"ERR_MDE_NOT_INSTALLED"} 133 | 21 {"ERR_INSTALLATION_FAILED"} 134 | 22 {"ERR_UNINSTALLATION_FAILED"} 135 | 23 {"ERR_FAILED_DEPENDENCY"} 136 | 24 {"ERR_FAILED_REPO_SETUP"} 137 | 25 {"ERR_INVALID_CHANNEL"} 138 | 26 {"ERR_FAILED_REPO_CLEANUP"} 139 | 30 {"ERR_ONBOARDING_NOT_FOUND"} 140 | 31 {"ERR_ONBOARDING_FAILED"} 141 | 40 {"ERR_TAG_NOT_SUPPORTED"} 142 | 41 {"ERR_PARAMETER_SET_FAILED"} 143 | default {"ERR_UNKNOWNCODE"} 144 | } 145 | 146 | #generate entry for report 147 | $string = "$($sub.Name),$($vme.Name), $($loc), $(($vmeext | where-Object {($_.Name -match "MDE")}).name), $($arc.OSSku), $(($vmeext | where-Object {($_.Name -match "MDE")}).statuslevel), $($code), $($codedesc), $((($vmeext | where-Object {($_.Name -match "MDE")}).StatusMessage).split('/')[0]), $($modstring)" 148 | 149 | #output entry into report 150 | $string | Out-File $outputFile -append -force 151 | 152 | } 153 | 154 | } 155 | 156 | } 157 | 158 | } 159 | 160 | # Once done import the data into excel 161 | 162 | $CSV = Import-Csv -Path $path 163 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation 164 | -------------------------------------------------------------------------------- /MDESingleOnboard.ps1: -------------------------------------------------------------------------------- 1 | #PRE REQs - ARMCLient, Azure CLI, Azure PowerShell 2 | 3 | # Login in 4 | Connect-AzAccount 5 | armclient login 6 | 7 | #Your AAD Tenant ID if your login is associated to many tenants 8 | armclient token {YOUR AAD TENANT ID} 9 | 10 | # URL to get the base 64 encoded package 11 | $url1 = "https://management.azure.com/subscriptions/{SUBID}/providers/Microsoft.Security/mdeOnboardings?api-version=2021-10-01-preview" 12 | 13 | 14 | # PUT Api to setup continuous export rule to send to Storage 15 | armclient GET $url1 16 | 17 | # URL and payload for MDE Onboard for a Single Windows Azure VM 18 | $url2 = "https://management.azure.com/{RESOURCEID}/extensions/MDE.Windows?api-version=2015-06-15" 19 | 20 | $payload2 = "{'name': 'MDE.Windows', 'id': '{RESOURCEID}/extensions/MDE.Windows', 'type': 'Microsoft.compute/virtualMachines/extensions', 'location': '{VMLOCATION}', 'properties': {'autoUpgradeMinorVersion': true, 'publisher': 'Microsoft.Azure.AzureDefenderForServers','type': 'MDE.Windows','typeHandlerVersion': '1.0','settings': {'azureResourceId': '{RESOURCEID}','vNextEnabled': 'true'},'protectedSettings': {'defenderForEndpointOnboardingScript': '{BASE64PACKAGE}'}}}" 21 | 22 | 23 | # PUT Api to MDE onboard a VM 24 | armclient PUT $url2 $payload2 25 | -------------------------------------------------------------------------------- /ModifyExistingASCPolicybyMG.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 3/22/2020 4:48 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | # PreReqs Need PS 7, Az Modules 7 | ################################################################################ 8 | 9 | $managementGroupName = "SwiftOrg" 10 | 11 | #$mgascassignmentname = "ASC Default" 12 | 13 | $subascassignmentname = "ASC Default" 14 | 15 | # get a specific management group defined above 16 | $azmgmt = Get-AzManagementGroup -GroupName $managementGroupName -Expand 17 | 18 | # get the assigned subscriptions to the managaement group 19 | $azmgmtsubs = $azmgmt.Children 20 | 21 | # run through all subscriptions under Management group 22 | $azmgmtsubs | ForEach-Object -ThrottleLimit 10 -Parallel { 23 | 24 | # Matching to ensure the subscription is not "Access to Azure Active Directory" those subscriptions are non billable\deploayble into 25 | if ($_.DisplayName -ne "Access to Azure Active Directory"){ 26 | 27 | # Get policy Assignments from each subscription 28 | $policyassignsub = Get-AzPolicyAssignment -Scope $_.Id 29 | 30 | # Matching condition of policy assignment resourcename and subid not being null, mgmt assignments do not have SubscriptionId in object 31 | if ($policyassignsub.ResourceName -contains "SecurityCenterBuiltIn" -and $policyassignsub.SubscriptionId -ne $null ) { 32 | 33 | ## Used below for Testing ensure no false positives in above if matching 34 | #Write-Host $policyassignsub.Name 35 | #Write-Host $policyassignsub.ResourceId[0] 36 | 37 | ## Remove the following policy assignemnt from subscription. 38 | Remove-AzPolicyAssignment -Id $policyassignsub.ResourceId[0] 39 | 40 | } 41 | 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /ModifyExistingASCPolicybyMGs.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 3/22/2020 6:03 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | # PreReqs Need PS 7, Az Modules 7 | ################################################################################ 8 | 9 | #$managementGroupName = "SwiftOrg" 10 | 11 | #$mgascassignmentname = "ASC Default" 12 | 13 | #comment the below line out if using cloud shell 14 | #$outfilepath = "C:\users\subs.txt" 15 | 16 | #comment out the below line if using local powershell (will detect in later release) 17 | $outfilepath = "~/clouddrive/asc_pol_removed.txt" 18 | 19 | $subascassignmentname = "ASC Default" 20 | 21 | # gets all management groups from authorized login 22 | $azuremgmts = Get-AzManagementGroup 23 | 24 | #loop through each management group 25 | foreach ($azuremgmt in $azuremgmts){ 26 | 27 | if ($azuremgmt.DisplayName -notmatch "Tenant Root Group"){ 28 | 29 | $managementGroupName = $azuremgmt.Name 30 | $azmgmt = Get-AzManagementGroup -GroupName $managementGroupName -Expand 31 | 32 | # get the assigned subscriptions to the managaement group 33 | $azmgmtsubs = $azmgmt.Children 34 | 35 | # run through all subscriptions under Management group 36 | $azmgmtsubs | ForEach-Object -ThrottleLimit 10 -Parallel { 37 | 38 | # Matching to ensure the subscription is not "Access to Azure Active Directory" those subscriptions are non billable\deploayble into 39 | if ($_.DisplayName -notmatch "Access to Azure Active Directory"){ 40 | 41 | # Get policy Assignments from each subscription 42 | 43 | Write-Host $_.Type 44 | Write-Host $_.Id 45 | Write-Host $_.Name 46 | Write-Host $_.DisplayName 47 | 48 | $policyassignsub = Get-AzPolicyAssignment -Scope $_.Id 49 | 50 | # Matching condition of policy assignment resourcename and subid not being null, mgmt assignments do not have SubscriptionId in object 51 | if ($policyassignsub.ResourceName -contains "SecurityCenterBuiltIn" -and $policyassignsub.SubscriptionId -ne $null ) { 52 | 53 | ## Used below for Testing ensure no false positives in above if matching 54 | #Write-Host $policyassignsub.Name 55 | #Write-Host $policyassignsub.ResourceId[0] 56 | 57 | ## Remove the following policy assignemnt from subscription. 58 | Remove-AzPolicyAssignment -Id $policyassignsub.ResourceId[0] 59 | $policyassignsub.Name | out-file $outfilepath -append 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /O365CountryLookupSample.ps1: -------------------------------------------------------------------------------- 1 | Login-AzAccount 2 | Set-AzContext -Subscription "YOUR SUBSCRIPTION HERE" 3 | 4 | # Replace with your Workspace ID 5 | $CustomerId = "" 6 | 7 | # Replace with your Primary Key 8 | $SharedKey = "" 9 | 10 | # Specify the name of the record type that you'll be creating for the custom Table 11 | $LogType = "EnrichedO365Log" 12 | 13 | # You can use an optional field to specify the timestamp from the data. If the time field is not specified, Azure Monitor assumes the time is the message ingestion time 14 | $TimeStampField = "" 15 | 16 | # Create the function to create the authorization signature 17 | Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource) 18 | { 19 | $xHeaders = "x-ms-date:" + $date 20 | $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource 21 | 22 | $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash) 23 | $keyBytes = [Convert]::FromBase64String($sharedKey) 24 | 25 | $sha256 = New-Object System.Security.Cryptography.HMACSHA256 26 | $sha256.Key = $keyBytes 27 | $calculatedHash = $sha256.ComputeHash($bytesToHash) 28 | $encodedHash = [Convert]::ToBase64String($calculatedHash) 29 | $authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash 30 | return $authorization 31 | } 32 | 33 | # Create the function to create and post the request 34 | Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType) 35 | { 36 | $method = "POST" 37 | $contentType = "application/json" 38 | $resource = "/api/logs" 39 | $rfc1123date = [DateTime]::UtcNow.ToString("r") 40 | $contentLength = $body.Length 41 | $signature = Build-Signature ` 42 | -customerId $customerId ` 43 | -sharedKey $sharedKey ` 44 | -date $rfc1123date ` 45 | -contentLength $contentLength ` 46 | -method $method ` 47 | -contentType $contentType ` 48 | -resource $resource 49 | $uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01" 50 | 51 | $headers = @{ 52 | "Authorization" = $signature; 53 | "Log-Type" = $logType; 54 | "x-ms-date" = $rfc1123date; 55 | "time-generated-field" = $TimeStampField; 56 | } 57 | 58 | $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing 59 | return $response.StatusCode 60 | 61 | } 62 | 63 | 64 | #Start Scan for O365 POP3 or MAPI Logins within Last 4 Hours 65 | $tspan = (New-TimeSpan -Hours 4) 66 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $CustomerId -Query "OfficeActivity | where OfficeWorkload == 'Exchange' and Operation == 'MailboxLogin' | where ClientInfoString contains 'POP3' or ClientInfoString contains 'IMAP4' | distinct UserId, ClientIP, Operation, ResultStatus, ClientInfoString" # -Timespan $tspan 67 | 68 | #$queryResults.Results | ConvertTo-Json 69 | 70 | # Loop through Query Results 71 | foreach($result in $queryResults.Results) { 72 | 73 | # Obtain the CountryofOrigin from Public IP Calling a API 74 | $CIP = $result.ClientIP 75 | $whatispipjson = Invoke-WebRequest -UseBasicParsing -Uri http://ip-api.com/json/$CIP 76 | $whatispip = $whatispipjson.Content | ConvertFrom-Json 77 | $country = $whatispip.country 78 | 79 | # If the CountryofOrigin is not the United States then enrich the PS Object with the Country infromation and port into a Log Analytics Custom Table for Sentinel Alerting 80 | if ($country -notmatch "United States") { 81 | 82 | # Add CountryofOrigin Name and Value into PS Object 83 | $result = Add-Member -InputObject $result -MemberType NoteProperty -Name CountryofOrigin -Value $country -PassThru -Force 84 | 85 | $json = $result | ConvertTo-Json 86 | 87 | # Submit the data to the API endpoint 88 | Post-LogAnalyticsData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell-Scrips 2 | PowerShell Scripts, Snippets, bare minmium ideas 3 | -------------------------------------------------------------------------------- /ReIPNics.ps1: -------------------------------------------------------------------------------- 1 | Login-AzureRmAccount 2 | 3 | Set-AzureRMContext -Subscription "" 4 | 5 | 6 | # Your Resource Group where the nics need to be changed 7 | $RGnics = "" 8 | 9 | # Your Resource Group where the existing VNET with new Ip Address Space is 10 | $RGVNET = "" 11 | 12 | # Your VNET name with new Ip Address Space is 13 | $VNETname = "" 14 | 15 | # Your VNET and new Subnet to change Nics to with new address space 16 | $newsubnetname = "" 17 | 18 | # Your Nic Ip config Name it is typically "ipconfig1" 19 | $Nicipname = "ipconfig1" 20 | 21 | #Obtains Nics to change 22 | $Nics = Get-AzureRmNetworkInterface -ResourceGroupName $RGnics 23 | 24 | #Obtain VNET PS Object 25 | $VNET = Get-AzureRmVirtualNetwork -Name $VNETName -ResourceGroupName $RG 26 | 27 | #Obtains Subnet PS Object 28 | $newsubnet = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $VNET -Name $newsubnetname 29 | 30 | # Loop to change the NICS 31 | foreach ($Nic in $Nics){ 32 | 33 | #Change the PS object of SubnetID to new one 34 | $Nic.IpConfigurations[0].Subnet.Id = $newsubnet.id 35 | 36 | #Save PS Object Changes to the Nic 37 | Set-AzureRmNetworkInterface -NetworkInterface $Nic 38 | 39 | } 40 | 41 | $LBs = Get-AzureRmLoadBalancer -ResourceGroupName $RGVNET 42 | 43 | ## Future build for ILB reIPs 44 | # Loop to change the ILB FrontEnd 45 | #foreach ($LB in $LBs){ 46 | 47 | #Change the PS object of SubnetID to new one 48 | #$LB.FrontEndIpConfigurations[0].Subnet.Id = $newsubnet.id 49 | 50 | #Save PS Object Changes to the Nic 51 | #Set-AzureRmLoadBalancerFrontendIpConfig -LoadBalancer $LB -Name $LB.Name -Subnet $newsubnet 52 | 53 | 54 | #} 55 | -------------------------------------------------------------------------------- /Report AKSCluster Cores.ps1: -------------------------------------------------------------------------------- 1 | Get-AzVMSize -Location "Central US" 2 | 3 | $subs = Get-AzSubscription 4 | 5 | #$csvrawreq = Invoke-WebRequest -Uri "https://raw.githubusercontent.com/swiftsolves-msft/kql/main/lookup/vmsizeinfo.csv" -OutFile vmsizeinfo.csv 6 | 7 | #$vmsizeinfo = Import-Csv vmsizeinfo.csv 8 | 9 | $outputFile = "akscorereport.csv" 10 | 11 | #$string = "$($sub.Name),$($akscluster.name),$($VM.Location),$($akscluster.ResourceGroupName),$($aksvmsize),$($akscores)" 12 | 13 | $stringstart = "Subscription,AKSName,Location,ResourceGroupName,AKSSize,NumberOfCores" 14 | $stringstart | Out-File $outputFile -append -force 15 | 16 | foreach ($sub in $subs){ 17 | 18 | Set-AzContext -Subscription $sub.id 19 | 20 | $aksclusters = Get-AzAksCluster 21 | 22 | foreach ($akscluster in $aksclusters) { 23 | 24 | $aksvmsize = $akscluster.AgentPoolProfiles.VmSize 25 | 26 | $akscores = switch ($aksvmsize){ 27 | 28 | Standard_B1ls {1} 29 | Standard_B1ms {1} 30 | Standard_B1s {1} 31 | Standard_B2ms {2} 32 | Standard_B2s {2} 33 | Standard_B4ms {4} 34 | Standard_B8ms {8} 35 | Standard_B12ms {12} 36 | Standard_B16ms {16} 37 | Standard_B20ms {20} 38 | Standard_DS1_v2 {1} 39 | Standard_DS2_v2 {2} 40 | Standard_DS3_v2 {4} 41 | Standard_DS4_v2 {8} 42 | Standard_DS5_v2 {16} 43 | Standard_DS11-1_v2 {2} 44 | Standard_DS11_v2 {2} 45 | Standard_DS12-1_v2 {4} 46 | Standard_DS12-2_v2 {4} 47 | Standard_DS12_v2 {4} 48 | Standard_DS13-2_v2 {8} 49 | Standard_DS13-4_v2 {8} 50 | Standard_DS13_v2 {8} 51 | Standard_DS14-4_v2 {16} 52 | Standard_DS14-8_v2 {16} 53 | Standard_DS14_v2 {16} 54 | Standard_DS15_v2 {20} 55 | Standard_DS2_v2_Promo {2} 56 | Standard_DS3_v2_Promo {4} 57 | Standard_DS4_v2_Promo {8} 58 | Standard_DS5_v2_Promo {16} 59 | Standard_DS11_v2_Promo {2} 60 | Standard_DS12_v2_Promo {4} 61 | Standard_DS13_v2_Promo {8} 62 | Standard_DS14_v2_Promo {16} 63 | Standard_F1s {1} 64 | Standard_F2s {2} 65 | Standard_F4s {4} 66 | Standard_F8s {8} 67 | Standard_F16s {16} 68 | Standard_D2s_v3 {2} 69 | Standard_D4s_v3 {4} 70 | Standard_D8s_v3 {8} 71 | Standard_D16s_v3 {16} 72 | Standard_D32s_v3 {32} 73 | Standard_D2a_v4 {2} 74 | Standard_D4a_v4 {4} 75 | Standard_D8a_v4 {8} 76 | Standard_D16a_v4 {16} 77 | Standard_D32a_v4 {32} 78 | Standard_D48a_v4 {48} 79 | Standard_D64a_v4 {64} 80 | Standard_D96a_v4 {96} 81 | Standard_D2as_v4 {2} 82 | Standard_D4as_v4 {4} 83 | Standard_D8as_v4 {8} 84 | Standard_D16as_v4 {16} 85 | Standard_D32as_v4 {32} 86 | Standard_D48as_v4 {48} 87 | Standard_D64as_v4 {64} 88 | Standard_D96as_v4 {96} 89 | Standard_E2a_v4 {2} 90 | Standard_E4a_v4 {4} 91 | Standard_E8a_v4 {8} 92 | Standard_E16a_v4 {16} 93 | Standard_E20a_v4 {20} 94 | Standard_E32a_v4 {32} 95 | Standard_E48a_v4 {48} 96 | Standard_E64a_v4 {64} 97 | Standard_E96a_v4 {96} 98 | Standard_E2as_v4 {2} 99 | Standard_E4-2as_v4 {4} 100 | Standard_E4as_v4 {4} 101 | Standard_E8-2as_v4 {8} 102 | Standard_E8-4as_v4 {8} 103 | Standard_E8as_v4 {8} 104 | Standard_E16-4as_v4 {16} 105 | Standard_E16-8as_v4 {16} 106 | Standard_E16as_v4 {16} 107 | Standard_E20as_v4 {20} 108 | Standard_E32-8as_v4 {32} 109 | Standard_E32-16as_v4 {32} 110 | Standard_E32as_v4 {32} 111 | Standard_E48as_v4 {48} 112 | Standard_E64-16as_v4 {64} 113 | Standard_E64-32as_v4 {64} 114 | Standard_E64as_v4 {64} 115 | Standard_E96-24as_v4 {96} 116 | Standard_E96-48as_v4 {96} 117 | Standard_E96as_v4 {96} 118 | Standard_E96ias_v4 {96} 119 | Standard_D1_v2 {1} 120 | Standard_D2_v2 {2} 121 | Standard_D3_v2 {4} 122 | Standard_D4_v2 {8} 123 | Standard_D5_v2 {16} 124 | Standard_D11_v2 {2} 125 | Standard_D12_v2 {4} 126 | Standard_D13_v2 {8} 127 | Standard_D14_v2 {16} 128 | Standard_D15_v2 {20} 129 | Standard_D2_v2_Promo {2} 130 | Standard_D3_v2_Promo {4} 131 | Standard_D4_v2_Promo {8} 132 | Standard_D5_v2_Promo {16} 133 | Standard_D11_v2_Promo {2} 134 | Standard_D12_v2_Promo {4} 135 | Standard_D13_v2_Promo {8} 136 | Standard_D14_v2_Promo {16} 137 | Standard_F1 {1} 138 | Standard_F2 {2} 139 | Standard_F4 {4} 140 | Standard_F8 {8} 141 | Standard_F16 {16} 142 | Standard_A1_v2 {1} 143 | Standard_A2m_v2 {2} 144 | Standard_A2_v2 {2} 145 | Standard_A4m_v2 {4} 146 | Standard_A4_v2 {4} 147 | Standard_A8m_v2 {8} 148 | Standard_A8_v2 {8} 149 | Standard_D2_v3 {2} 150 | Standard_D4_v3 {4} 151 | Standard_D8_v3 {8} 152 | Standard_D16_v3 {16} 153 | Standard_D32_v3 {32} 154 | Standard_D48_v3 {48} 155 | Standard_D64_v3 {64} 156 | Standard_D48s_v3 {48} 157 | Standard_D64s_v3 {64} 158 | Standard_E2_v3 {2} 159 | Standard_E4_v3 {4} 160 | Standard_E8_v3 {8} 161 | Standard_E16_v3 {16} 162 | Standard_E20_v3 {20} 163 | Standard_E32_v3 {32} 164 | Standard_E48_v3 {48} 165 | Standard_E64_v3 {64} 166 | Standard_E2s_v3 {2} 167 | Standard_E4-2s_v3 {4} 168 | Standard_E4s_v3 {4} 169 | Standard_E8-2s_v3 {8} 170 | Standard_E8-4s_v3 {8} 171 | Standard_E8s_v3 {8} 172 | Standard_E16-4s_v3 {16} 173 | Standard_E16-8s_v3 {16} 174 | Standard_E16s_v3 {16} 175 | Standard_E20s_v3 {20} 176 | Standard_E32-8s_v3 {32} 177 | Standard_E32-16s_v3 {32} 178 | Standard_E32s_v3 {32} 179 | Standard_E48s_v3 {48} 180 | Standard_E64-16s_v3 {64} 181 | Standard_E64-32s_v3 {64} 182 | Standard_E64s_v3 {64} 183 | Standard_A0 {1} 184 | Standard_A1 {1} 185 | Standard_A2 {2} 186 | Standard_A3 {4} 187 | Standard_A5 {2} 188 | Standard_A4 {8} 189 | Standard_A6 {4} 190 | Standard_A7 {8} 191 | Basic_A0 {1} 192 | Basic_A1 {1} 193 | Basic_A2 {2} 194 | Basic_A3 {4} 195 | Basic_A4 {8} 196 | Standard_E64i_v3 {64} 197 | Standard_E64is_v3 {64} 198 | Standard_M208ms_v2 {208} 199 | Standard_M208s_v2 {208} 200 | Standard_M416-208s_v2 {416} 201 | Standard_M416s_v2 {416} 202 | Standard_M416-208ms_v2 {416} 203 | Standard_M416ms_v2 {416} 204 | Standard_H8 {8} 205 | Standard_H8_Promo {8} 206 | Standard_H16 {16} 207 | Standard_H16_Promo {16} 208 | Standard_H8m {8} 209 | Standard_H8m_Promo {8} 210 | Standard_H16m {16} 211 | Standard_H16m_Promo {16} 212 | Standard_H16r {16} 213 | Standard_H16r_Promo {16} 214 | Standard_H16mr {16} 215 | Standard_H16mr_Promo {16} 216 | Standard_D1 {1} 217 | Standard_D2 {2} 218 | Standard_D3 {4} 219 | Standard_D4 {8} 220 | Standard_D11 {2} 221 | Standard_D12 {4} 222 | Standard_D13 {8} 223 | Standard_D14 {16} 224 | Standard_NV6 {6} 225 | Standard_NV12 {12} 226 | Standard_NV24 {24} 227 | Standard_NV6_Promo {6} 228 | Standard_NV12_Promo {12} 229 | Standard_NV24_Promo {24} 230 | Standard_NC4as_T4_v3 {4} 231 | Standard_NC8as_T4_v3 {8} 232 | Standard_NC16as_T4_v3 {16} 233 | Standard_NC64as_T4_v3 {64} 234 | Standard_NV6s_v2 {6} 235 | Standard_NV12s_v2 {12} 236 | Standard_NV24s_v2 {24} 237 | Standard_NV12s_v3 {12} 238 | Standard_NV24s_v3 {24} 239 | Standard_NV48s_v3 {48} 240 | Standard_HB120rs_v2 {120} 241 | Standard_D2ds_v4 {2} 242 | Standard_D4ds_v4 {4} 243 | Standard_D8ds_v4 {8} 244 | Standard_D16ds_v4 {16} 245 | Standard_D32ds_v4 {32} 246 | Standard_D48ds_v4 {48} 247 | Standard_D64ds_v4 {64} 248 | Standard_D2ds_v5 {2} 249 | Standard_D4ds_v5 {4} 250 | Standard_D8ds_v5 {8} 251 | Standard_D16ds_v5 {16} 252 | Standard_D32ds_v5 {32} 253 | Standard_D48ds_v5 {48} 254 | Standard_D64ds_v5 {64} 255 | Standard_D96ds_v5 {96} 256 | Standard_D2d_v4 {2} 257 | Standard_D4d_v4 {4} 258 | Standard_D8d_v4 {8} 259 | Standard_D16d_v4 {16} 260 | Standard_D32d_v4 {32} 261 | Standard_D48d_v4 {48} 262 | Standard_D64d_v4 {64} 263 | Standard_D2d_v5 {2} 264 | Standard_D4d_v5 {4} 265 | Standard_D8d_v5 {8} 266 | Standard_D16d_v5 {16} 267 | Standard_D32d_v5 {32} 268 | Standard_D48d_v5 {48} 269 | Standard_D64d_v5 {64} 270 | Standard_D96d_v5 {96} 271 | Standard_D2s_v4 {2} 272 | Standard_D4s_v4 {4} 273 | Standard_D8s_v4 {8} 274 | Standard_D16s_v4 {16} 275 | Standard_D32s_v4 {32} 276 | Standard_D48s_v4 {48} 277 | Standard_D64s_v4 {64} 278 | Standard_D2s_v5 {2} 279 | Standard_D4s_v5 {4} 280 | Standard_D8s_v5 {8} 281 | Standard_D16s_v5 {16} 282 | Standard_D32s_v5 {32} 283 | Standard_D48s_v5 {48} 284 | Standard_D64s_v5 {64} 285 | Standard_D96s_v5 {96} 286 | Standard_D2_v4 {2} 287 | Standard_D4_v4 {4} 288 | Standard_D8_v4 {8} 289 | Standard_D16_v4 {16} 290 | Standard_D32_v4 {32} 291 | Standard_D48_v4 {48} 292 | Standard_D64_v4 {64} 293 | Standard_D2_v5 {2} 294 | Standard_D4_v5 {4} 295 | Standard_D8_v5 {8} 296 | Standard_D16_v5 {16} 297 | Standard_D32_v5 {32} 298 | Standard_D48_v5 {48} 299 | Standard_D64_v5 {64} 300 | Standard_D96_v5 {96} 301 | Standard_E2ds_v4 {2} 302 | Standard_E4-2ds_v4 {4} 303 | Standard_E4ds_v4 {4} 304 | Standard_E8-2ds_v4 {8} 305 | Standard_E8-4ds_v4 {8} 306 | Standard_E8ds_v4 {8} 307 | Standard_E16-4ds_v4 {16} 308 | Standard_E16-8ds_v4 {16} 309 | Standard_E16ds_v4 {16} 310 | Standard_E20ds_v4 {20} 311 | Standard_E32-8ds_v4 {32} 312 | Standard_E32-16ds_v4 {32} 313 | Standard_E32ds_v4 {32} 314 | Standard_E48ds_v4 {48} 315 | Standard_E64-16ds_v4 {64} 316 | Standard_E64-32ds_v4 {64} 317 | Standard_E64ds_v4 {64} 318 | Standard_E80ids_v4 {80} 319 | Standard_E2ds_v5 {2} 320 | Standard_E4-2ds_v5 {4} 321 | Standard_E4ds_v5 {4} 322 | Standard_E8-2ds_v5 {8} 323 | Standard_E8-4ds_v5 {8} 324 | Standard_E8ds_v5 {8} 325 | Standard_E16-4ds_v5 {16} 326 | Standard_E16-8ds_v5 {16} 327 | Standard_E16ds_v5 {16} 328 | Standard_E20ds_v5 {20} 329 | Standard_E32-8ds_v5 {32} 330 | Standard_E32-16ds_v5 {32} 331 | Standard_E32ds_v5 {32} 332 | Standard_E48ds_v5 {48} 333 | Standard_E64-16ds_v5 {64} 334 | Standard_E64-32ds_v5 {64} 335 | Standard_E64ds_v5 {64} 336 | Standard_E96-24ds_v5 {96} 337 | Standard_E96-48ds_v5 {96} 338 | Standard_E96ds_v5 {96} 339 | Standard_E104ids_v5 {104} 340 | Standard_E2d_v4 {2} 341 | Standard_E4d_v4 {4} 342 | Standard_E8d_v4 {8} 343 | Standard_E16d_v4 {16} 344 | Standard_E20d_v4 {20} 345 | Standard_E32d_v4 {32} 346 | Standard_E48d_v4 {48} 347 | Standard_E64d_v4 {64} 348 | Standard_E2d_v5 {2} 349 | Standard_E4d_v5 {4} 350 | Standard_E8d_v5 {8} 351 | Standard_E16d_v5 {16} 352 | Standard_E20d_v5 {20} 353 | Standard_E32d_v5 {32} 354 | Standard_E48d_v5 {48} 355 | Standard_E64d_v5 {64} 356 | Standard_E96d_v5 {96} 357 | Standard_E104id_v5 {104} 358 | Standard_E2s_v4 {2} 359 | Standard_E4-2s_v4 {4} 360 | Standard_E4s_v4 {4} 361 | Standard_E8-2s_v4 {8} 362 | Standard_E8-4s_v4 {8} 363 | Standard_E8s_v4 {8} 364 | Standard_E16-4s_v4 {16} 365 | Standard_E16-8s_v4 {16} 366 | Standard_E16s_v4 {16} 367 | Standard_E20s_v4 {20} 368 | Standard_E32-8s_v4 {32} 369 | Standard_E32-16s_v4 {32} 370 | Standard_E32s_v4 {32} 371 | Standard_E48s_v4 {48} 372 | Standard_E64-16s_v4 {64} 373 | Standard_E64-32s_v4 {64} 374 | Standard_E64s_v4 {64} 375 | Standard_E80is_v4 {80} 376 | Standard_E2s_v5 {2} 377 | Standard_E4-2s_v5 {4} 378 | Standard_E4s_v5 {4} 379 | Standard_E8-2s_v5 {8} 380 | Standard_E8-4s_v5 {8} 381 | Standard_E8s_v5 {8} 382 | Standard_E16-4s_v5 {16} 383 | Standard_E16-8s_v5 {16} 384 | Standard_E16s_v5 {16} 385 | Standard_E20s_v5 {20} 386 | Standard_E32-8s_v5 {32} 387 | Standard_E32-16s_v5 {32} 388 | Standard_E32s_v5 {32} 389 | Standard_E48s_v5 {48} 390 | Standard_E64-16s_v5 {64} 391 | Standard_E64-32s_v5 {64} 392 | Standard_E64s_v5 {64} 393 | Standard_E96-24s_v5 {96} 394 | Standard_E96-48s_v5 {96} 395 | Standard_E96s_v5 {96} 396 | Standard_E104is_v5 {104} 397 | Standard_E2_v4 {2} 398 | Standard_E4_v4 {4} 399 | Standard_E8_v4 {8} 400 | Standard_E16_v4 {16} 401 | Standard_E20_v4 {20} 402 | Standard_E32_v4 {32} 403 | Standard_E48_v4 {48} 404 | Standard_E64_v4 {64} 405 | Standard_E2_v5 {2} 406 | Standard_E4_v5 {4} 407 | Standard_E8_v5 {8} 408 | Standard_E16_v5 {16} 409 | Standard_E20_v5 {20} 410 | Standard_E32_v5 {32} 411 | Standard_E48_v5 {48} 412 | Standard_E64_v5 {64} 413 | Standard_E96_v5 {96} 414 | Standard_E104i_v5 {104} 415 | Standard_F2s_v2 {2} 416 | Standard_F4s_v2 {4} 417 | Standard_F8s_v2 {8} 418 | Standard_F16s_v2 {16} 419 | Standard_F32s_v2 {32} 420 | Standard_F48s_v2 {48} 421 | Standard_F64s_v2 {64} 422 | Standard_F72s_v2 {72} 423 | Standard_NC6s_v2 {6} 424 | Standard_NC12s_v2 {12} 425 | Standard_NC24rs_v2 {24} 426 | Standard_NC24s_v2 {24} 427 | Standard_NC6 {6} 428 | Standard_NC12 {12} 429 | Standard_NC24 {24} 430 | Standard_NC24r {24} 431 | Standard_NC6_Promo {6} 432 | Standard_NC12_Promo {12} 433 | Standard_NC24_Promo {24} 434 | Standard_NC24r_Promo {24} 435 | Standard_DS1 {1} 436 | Standard_DS2 {2} 437 | Standard_DS3 {4} 438 | Standard_DS4 {8} 439 | Standard_DS11 {2} 440 | Standard_DS12 {4} 441 | Standard_DS13 {8} 442 | Standard_DS14 {16} 443 | Standard_L8s_v2 {8} 444 | Standard_L16s_v2 {16} 445 | Standard_L32s_v2 {32} 446 | Standard_L48s_v2 {48} 447 | Standard_L64s_v2 {64} 448 | Standard_L80s_v2 {80} 449 | Standard_M64 {64} 450 | Standard_M64m {64} 451 | Standard_M128 {128} 452 | Standard_M128m {128} 453 | Standard_M8-2ms {8} 454 | Standard_M8-4ms {8} 455 | Standard_M8ms {8} 456 | Standard_M16-4ms {16} 457 | Standard_M16-8ms {16} 458 | Standard_M16ms {16} 459 | Standard_M32-8ms {32} 460 | Standard_M32-16ms {32} 461 | Standard_M32ls {32} 462 | Standard_M32ms {32} 463 | Standard_M32ts {32} 464 | Standard_M64-16ms {64} 465 | Standard_M64-32ms {64} 466 | Standard_M64ls {64} 467 | Standard_M64ms {64} 468 | Standard_M64s {64} 469 | Standard_M128-32ms {128} 470 | Standard_M128-64ms {128} 471 | Standard_M128ms {128} 472 | Standard_M128s {128} 473 | Standard_M32ms_v2 {32} 474 | Standard_M64ms_v2 {64} 475 | Standard_M64s_v2 {64} 476 | Standard_M128ms_v2 {128} 477 | Standard_M128s_v2 {128} 478 | Standard_M192ims_v2 {192} 479 | Standard_M192is_v2 {192} 480 | Standard_M32dms_v2 {32} 481 | Standard_M64dms_v2 {64} 482 | Standard_M64ds_v2 {64} 483 | Standard_M128dms_v2 {128} 484 | Standard_M128ds_v2 {128} 485 | Standard_M192idms_v2 {192} 486 | Standard_M192ids_v2 {192} 487 | Standard_DC8_v2 {8} 488 | Standard_DC1s_v2 {1} 489 | Standard_DC2s_v2 {2} 490 | Standard_DC4s_v2 {4} 491 | Standard_ND40rs_v2 {40} 492 | Standard_FX4mds {4} 493 | Standard_FX12mds {12} 494 | Standard_FX24mds {24} 495 | Standard_FX36mds {36} 496 | Standard_FX48mds {48} 497 | Standard_NP10s {10} 498 | Standard_NP20s {20} 499 | Standard_NP40s {40} 500 | Standard_D2as_v5 {2} 501 | Standard_D4as_v5 {4} 502 | Standard_D8as_v5 {8} 503 | Standard_D16as_v5 {16} 504 | Standard_D32as_v5 {32} 505 | Standard_D48as_v5 {48} 506 | Standard_D64as_v5 {64} 507 | Standard_D96as_v5 {96} 508 | Standard_E2as_v5 {2} 509 | Standard_E4-2as_v5 {4} 510 | Standard_E4as_v5 {4} 511 | Standard_E8-2as_v5 {8} 512 | Standard_E8-4as_v5 {8} 513 | Standard_E8as_v5 {8} 514 | Standard_E16-4as_v5 {16} 515 | Standard_E16-8as_v5 {16} 516 | Standard_E16as_v5 {16} 517 | Standard_E32-8as_v5 {32} 518 | Standard_E32-16as_v5 {32} 519 | Standard_E32as_v5 {32} 520 | Standard_E48as_v5 {48} 521 | Standard_E64-16as_v5 {64} 522 | Standard_E64-32as_v5 {64} 523 | Standard_E64as_v5 {64} 524 | Standard_E96-24as_v5 {96} 525 | Standard_E96-48as_v5 {96} 526 | Standard_E96as_v5 {96} 527 | Standard_E112ias_v5 {112} 528 | Standard_D2ads_v5 {2} 529 | Standard_D4ads_v5 {4} 530 | Standard_D8ads_v5 {8} 531 | Standard_D16ads_v5 {16} 532 | Standard_D32ads_v5 {32} 533 | Standard_D48ads_v5 {48} 534 | Standard_D64ads_v5 {64} 535 | Standard_D96ads_v5 {96} 536 | Standard_E2ads_v5 {2} 537 | Standard_E4-2ads_v5 {4} 538 | Standard_E4ads_v5 {4} 539 | Standard_E8-2ads_v5 {8} 540 | Standard_E8-4ads_v5 {8} 541 | Standard_E8ads_v5 {8} 542 | Standard_E16-4ads_v5 {16} 543 | Standard_E16-8ads_v5 {16} 544 | Standard_E16ads_v5 {16} 545 | Standard_E20ads_v5 {20} 546 | Standard_E32-8ads_v5 {32} 547 | Standard_E32-16ads_v5 {32} 548 | Standard_E32ads_v5 {32} 549 | Standard_E48ads_v5 {48} 550 | Standard_E64-16ads_v5 {64} 551 | Standard_E64-32ads_v5 {64} 552 | Standard_E64ads_v5 {64} 553 | Standard_E96-24ads_v5 {96} 554 | Standard_E96-48ads_v5 {96} 555 | Standard_E96ads_v5 {96} 556 | Standard_E112iads_v5 {112} 557 | Standard_HC44-16rs {44} 558 | Standard_HC44-32rs {44} 559 | Standard_HC44rs {44} 560 | Standard_ND6s {6} 561 | Standard_ND12s {12} 562 | Standard_ND24rs {24} 563 | Standard_ND24s {24} 564 | Standard_DC2s {2} 565 | Standard_DC4s {4} 566 | Standard_HB120-16rs_v3 {120} 567 | Standard_HB120-32rs_v3 {120} 568 | Standard_HB120-64rs_v3 {120} 569 | Standard_HB120-96rs_v3 {120} 570 | Standard_HB120rs_v3 {120} 571 | Standard_NV4as_v4 {4} 572 | Standard_NV8as_v4 {8} 573 | Standard_NV16as_v4 {16} 574 | Standard_NV32as_v4 {32} 575 | Standard_NC6s_v3 {6} 576 | Standard_NC12s_v3 {12} 577 | Standard_NC24rs_v3 {24} 578 | Standard_NC24s_v3 {24} 579 | Standard_PB6s {6} 580 | Standard_HB60-15rs {60} 581 | Standard_HB60-30rs {60} 582 | Standard_HB60-45rs {60} 583 | Standard_HB60rs {60} 584 | Standard_ND96asr_v4 {96} 585 | Standard_ND40s_v3 {40} 586 | default {$null} 587 | 588 | } 589 | 590 | 591 | $string = "$($sub.Name),$($akscluster.name),$($VM.Location),$($akscluster.ResourceGroupName),$($aksvmsize),$($akscores)" 592 | 593 | $string | Out-File $outputFile -append -force 594 | 595 | } 596 | 597 | } -------------------------------------------------------------------------------- /ReportOwnersandSetTag.ps1: -------------------------------------------------------------------------------- 1 | Login-AzAccount 2 | 3 | # Time Tracking Start 4 | $datetime = Get-Date 5 | Write-Host $datetime 6 | 7 | $logs = Get-AzLog -StartTime (Get-Date).AddDays(-89) 8 | 9 | $path = "C:\temp\loginventory.txt" 10 | $csvpath = "C:\temp\loginventory.csv" 11 | 12 | # File check to overwrite existing vm inventory collection 13 | $filecheck = Get-FileHash -Path $path 14 | 15 | If ($filecheck.Path -eq $path) 16 | { 17 | Remove-Item -Path $path 18 | Write-host "Removed Previous File" 19 | } 20 | else 21 | { 22 | Write-host "No Previous File Found" 23 | } 24 | 25 | # File check to overwrite existing vm inventory collection 26 | $filecheck2 = Get-FileHash -Path $csvpath 27 | 28 | If ($filecheck2.Path -eq $csvpath) 29 | { 30 | Remove-Item -Path $csvpath 31 | Write-host "Removed Previous File" 32 | } 33 | else 34 | { 35 | Write-host "No Previous File Found" 36 | } 37 | 38 | 39 | $outputFile = $path 40 | 41 | $logstring = "Owner,Type,Kind,ResourceName,Date,ResourceId" 42 | $logstring | Out-File $outputFile -append -force 43 | 44 | foreach ($log in $logs) { 45 | 46 | if ($log.OperationName.Value -like "*/write") { 47 | 48 | $resname = ($log.ResourceId) -split '/' 49 | $logstring = "$($log.caller),$($log.ResourceProviderName.Value),$($resname[7]),$($resname[8]),$($log.EventTimestamp),$($log.ResourceId)" 50 | 51 | #Write into and append into output file 52 | $logstring | Out-File $outputFile -append -force 53 | 54 | ## Remove # comments to apply owner tags 55 | #$tags = (Get-AzResource -ResourceId $log.ResourceId).Tags 56 | #$tags += @{owner=$log.caller} 57 | #Set-AzResource -ResourceId $log.ResourceId -Tag $tags -Force 58 | 59 | } 60 | 61 | } 62 | 63 | # Time Tracking Finished 64 | $datetime = Get-Date 65 | Write-Host $datetime 66 | 67 | # Once done import the data into excel 68 | 69 | $CSV = Import-Csv -Path $path 70 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation -------------------------------------------------------------------------------- /ResourceGraphPaaSACLs.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 12/5/2018 1:21 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | # Requirements: AzurePS 6.3.0 or Higher, Module: AzureRM.ResourceGraph - https://docs.microsoft.com/en-us/azure/governance/resource-graph/first-query-powershell 7 | 8 | ################################################################################ 9 | 10 | <# 11 | Current Services: 12 | 13 | Storage Account 14 | KeyVault 15 | 16 | #> 17 | 18 | <# Possible Futures: 19 | 20 | Cleaner output string data and Out-File 21 | 22 | Azure SQL Database 23 | Azure SQLDW 24 | Azure PostgreSQL 25 | Azure MySQL 26 | Azure CosmosDB 27 | Azure Service Bus 28 | Azure EventHubs 29 | Azure Data Lake Storage Gen1 30 | 31 | #> 32 | 33 | # Path and filename for output data file being generated. 34 | $path = "C:\temp\PaaSACLsinventory.txt" 35 | $csvpath = "C:\temp\PaaSACLsinventory.csv" 36 | 37 | # Authenticate Piece 38 | Login-AzureRmAccount 39 | 40 | # Time Tracking Start 41 | $datetime = Get-Date 42 | Write-Host $datetime 43 | 44 | # File check to overwrite existing vm inventory collection 45 | $filecheck = Get-FileHash -Path $path 46 | 47 | If ($filecheck.Path -eq $path) 48 | { 49 | Remove-Item -Path $path 50 | Write-host "Removed Previous File" 51 | } 52 | else 53 | { 54 | Write-host "No Previous File Found" 55 | } 56 | 57 | # File check to overwrite existing vm inventory collection 58 | $filecheck2 = Get-FileHash -Path $csvpath 59 | 60 | If ($filecheck2.Path -eq $csvpath) 61 | { 62 | Remove-Item -Path $csvpath 63 | Write-host "Removed Previous File" 64 | } 65 | else 66 | { 67 | Write-host "No Previous File Found" 68 | } 69 | 70 | 71 | # Outputfile for PaaS ACL inventory 72 | $outputFile = $path 73 | 74 | #Set and apply 1st line of csv headers 75 | $string = "Name,Type,PaaS-ACL" 76 | $string | Out-File $outputFile -append -force 77 | 78 | # Graph Search Storage Account 79 | $stores = Search-AzureRmGraph -Query "where type contains 'storageaccounts' | order by name asc" | Select name, type, properties 80 | 81 | # Iterate through each storage account and output into file 82 | foreach ($store in $stores){ 83 | 84 | 85 | $string = "$($Store.name),$($store.type),$($store.properties.networkAcls.defaultAction)" 86 | 87 | $string | Out-File $outputFile -append -force 88 | 89 | } 90 | 91 | # Graph Search KeyVaults 92 | $kvs = Search-AzureRmGraph -Query "where type contains 'keyvault' | order by name asc" | Select name, type, aliases 93 | 94 | # Iterate through each KeyVault and output into file 95 | foreach ($kv in $kvs){ 96 | 97 | if ($kv.aliases.'Microsoft.KeyVault/vaults/networkAcls.defaultAction' -contains 'deny'){ 98 | 99 | $kvacl = "Deny" 100 | 101 | } Else { 102 | 103 | $kvacl = "Allow" 104 | 105 | } 106 | 107 | $string = "$($kv.name),$($kv.type),$($kvacl)" 108 | 109 | $string | Out-File $outputFile -append -force 110 | 111 | } 112 | 113 | # Time Tracking Finished 114 | $datetime = Get-Date 115 | Write-Host $datetime 116 | 117 | # Once done import the data into excel 118 | 119 | $CSV = Import-Csv -Path $path 120 | $CSV | Export-Csv -Path $csvpath -NoTypeInformation -------------------------------------------------------------------------------- /RotateKeys_v2.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | Runbook Rotates Storages Keys in Azure and stores them in keyvault 4 | 5 | .NOTES 6 | AUTHOR: Azure Automation Team 7 | LASTEDIT: Mar 14, 2016 8 | #> 9 | 10 | $connectionName = "AzureRunAsConnection" 11 | try 12 | { 13 | # Get the connection "AzureRunAsConnection " 14 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 15 | 16 | "Logging in to Azure..." 17 | Add-AzureRmAccount ` 18 | -ServicePrincipal ` 19 | -TenantId $servicePrincipalConnection.TenantId ` 20 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 21 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 22 | } 23 | catch { 24 | if (!$servicePrincipalConnection) 25 | { 26 | $ErrorMessage = "Connection $connectionName not found." 27 | throw $ErrorMessage 28 | } else{ 29 | Write-Error -Message $_.Exception 30 | throw $_.Exception 31 | } 32 | } 33 | 34 | 35 | $storageaccounts = Get-AzureRMStorageAccount 36 | 37 | foreach ($storageaccount in $storageaccounts) { 38 | 39 | New-AzureRmStorageAccountKey -ResourceGroupName $storageaccount.resourcegroupname -Name $storageaccount.storageaccountname -KeyName key1 40 | 41 | $storageaccountkey = ((Get-AzureRmStorageAccountKey -ResourceGroupName $storageaccount.resourcegroupname -Name $storageaccount.storageaccountname)[0]).value 42 | 43 | $secretvalue = ConvertTo-SecureString $storageaccountkey -AsPlainText -Force 44 | $storagename = $storageaccount.storageaccountname 45 | $Secretname = $storagename + "-Key1" 46 | 47 | $secret = Set-AzureKeyVaultSecret -VaultName 'keyvaultname' -Name $Secretname -SecretValue $secretvalue 48 | 49 | 50 | #Secondary Key 51 | #New-AzureRmStorageAccountKey -ResourceGroupName $storageaccount.resourcegroupname -Name $storageaccount.storageaccountname -KeyName key2 52 | #$storageaccountkey = ((Get-AzureRmStorageAccountKey -ResourceGroupName $storageaccount.resourcegroupname -Name $storageaccount.storageaccountname)[1]).value 53 | #$secretvalue = ConvertTo-SecureString $storageaccountkey -AsPlainText -Force 54 | #$Secretname = $storagename + "-Key2" 55 | #$secret2 = Set-AzureKeyVaultSecret -VaultName 'keyvaultname' -Name $Secretname -SecretValue $secretvalue 56 | 57 | } 58 | -------------------------------------------------------------------------------- /SampleExportAllAPIs.ps1: -------------------------------------------------------------------------------- 1 |  2 | $ApiMgmtContext = New-AzureRmApiManagementContext -ResourceGroupName "rgSWIDMEOAPImgmt" -ServiceName "apiservicegt6wqoby66zhk" 3 | 4 | $APIs = Get-AzureRmApiManagementApi -Context $ApiMgmtContext 5 | 6 | foreach ($API in $APIs) { 7 | 8 | $wadlpath = "C:\APIs\specifications\" + $API.Name + ".wadl" 9 | 10 | Export-AzureRmApiManagementApi -Context $ApiMgmtContext -ApiId $API.ApiId -SpecificationFormat "Wadl" -SaveAs $wadlpath 11 | 12 | $jsonpath = "C:\APIs\specifications\" + $API.Name + ".json" 13 | 14 | Export-AzureRmApiManagementApi -Context $ApiMgmtContext -ApiId $API.ApiId -SpecificationFormat "swagger" -SaveAs $jsonpath 15 | 16 | } -------------------------------------------------------------------------------- /SampleExportPolicies.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | # Link: https://docs.microsoft.com/en-us/powershell/resourcemanager/azurerm.apimanagement/v3.2.0/get-azurermapimanagementpolicy 4 | # https://docs.microsoft.com/en-us/powershell/resourcemanager/azurerm.apimanagement/v3.3.0/set-azurermapimanagementpolicy 5 | #Obtain API Context 6 | $ApiMgmtContext = New-AzureRmApiManagementContext -ResourceGroupName "rgSWIDMEOAPImgmt" -ServiceName "apiservicegt6wqoby66zhk" 7 | 8 | #Export Tenant Policy 9 | Get-AzureRmApiManagementPolicy -Context $APIMgmtContext -SaveAs "C:\APIs\policies\tenantpolicy.xml" 10 | 11 | # Work on iteration Logic for exporting Product scope policy 12 | #Get-AzureRmApiManagementPolicy -Context $APIMgmtContext -ProductId "0123456789" 13 | 14 | # Iteration logic for APIs exporting API-Scope Policy 15 | $APIs = Get-AzureRmApiManagementApi -Context $ApiMgmtContext 16 | 17 | foreach ($API in $APIs) { 18 | 19 | $xmlpath = "C:\APIs\policies\" + $API.Name + "_apiscope.xml" #Work on naming path with $API.Apiname 20 | 21 | Get-AzureRmApiManagementPolicy -Context $APIMgmtContext -ApiId $API.ApiId -SaveAs $xmlpath 22 | 23 | } 24 | 25 | # Work on iteration Logic for operation scope policy 26 | #Get-AzureRmApiManagementPolicy -Context $APImContext -ApiId "9876543210" -OperationId "777" -SaveAs -------------------------------------------------------------------------------- /SentinelAlertsReportOffline.ps1: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | ## Author: Nathan Swift 3 | ## Date: 02.11.2020 4 | ## Description: Downloads Sentinel Rules and generates a report 5 | ## Needs PS Core 6 or 7 and Module powershell-yaml 6 | # Install-Module -Name powershell-yaml 7 | ######################################################################################### 8 | 9 | # download the Azure Sentinel Github zip 10 | Invoke-WebRequest -Uri "https://github.com/Azure/Azure-Sentinel/archive/master.zip" -OutFile C:\temp\sentinel.zip 11 | 12 | # Extract the Sentinel Github .zip 13 | Expand-Archive -Path C:\temp\sentinel.zip -DestinationPath c:\temp\sentinel -Force 14 | 15 | # New Report 16 | $filename = "SentinelAlertsReport.csv" 17 | 18 | # base path variable 19 | $basepath = "C:\temp\sentinel\Azure-Sentinel-master\Detections" 20 | 21 | # Get all the detection .ayml rules 22 | $files = Get-ChildItem -Path C:\temp\sentinel\Azure-Sentinel-master\Detections -Recurse -File -Include "*.yaml" 23 | 24 | # for testing 25 | #$files = Get-ChildItem -Path C:\temp\sentinel\Azure-Sentinel-master\Detections 26 | 27 | # For each yaml rule get unique field from the rule and import them into the csv file. 28 | foreach ($file in $files){ 29 | 30 | # obtain the unique file's path and https:// link 31 | $pathfile = $basepath + "\" + $file.Directory.Name + "\" + $file.Name 32 | $linkpath = "https://github.com/Azure/Azure-Sentinel/tree/master/Detections/" + $file.Directory.Name + "/" + $file.Name 33 | 34 | # obtain detection rule yaml and convert from yaml for extracting certain columns and reporting 35 | $rule = [pscustomobject](Get-Content $pathfile -Raw | ConvertFrom-Yaml) 36 | 37 | # Create a new null object to collect detection information into 38 | $reportObj = New-Object PSCustomObject 39 | 40 | # string value 41 | $reportObj | Add-Member -NotePropertyName Name -NotePropertyValue $rule.Name 42 | $reportObj | Add-Member -NotePropertyName Severity -NotePropertyValue $rule.Severity 43 | 44 | ## string array values 45 | $reportObj | Add-Member -Type NoteProperty -Name Tactics -value ($rule.tactics -join '/') 46 | $reportObj | Add-Member -Type NoteProperty -Name Connectors -value ($rule.requiredDataConnectors.connectorId -join '/') 47 | $reportObj | Add-Member -Type NoteProperty -Name Logs -value ($rule.requiredDataConnectors.dataTypes -join '/') 48 | $reportObj | Add-Member -Type NoteProperty -Name Techniques -value ($rule.relevantTechniques -join '/') 49 | 50 | # string value 51 | $reportObj | Add-Member -NotePropertyName Description -NotePropertyValue $rule.description 52 | $reportObj | Add-Member -NotePropertyName Link -NotePropertyValue $linkpath 53 | 54 | # write out unique detection and collected properties into .csv report 55 | $reportObj | Export-Csv C:\temp\$filename -NoTypeInformation -Delimiter "," -append 56 | 57 | } 58 | -------------------------------------------------------------------------------- /SetAzPolicyContainersSubSets.ps1: -------------------------------------------------------------------------------- 1 | # get all Azure subscriptions to check 2 | $Subs = Get-AzSubscription 3 | 4 | # For each subscription check Public Ips and Metrics 5 | foreach($sub in $subs){ 6 | 7 | #Set Azure Subscription Contect to current subscription in loop 8 | Set-AzContext -Subscription $sub.Id 9 | 10 | # Check for Policy Assignment: 'Defender for Containers provisioning AKS Security Profile' using Defintion: Configure Azure Kubernetes Service clusters to enable Defender profile 11 | $result1 = Get-AzPolicyAssignment -Name 'Defender for Containers provisioning AKS Security Profile' 12 | 13 | If($result1 -eq $null){ 14 | 15 | # Policy Name: Configure Azure Kubernetes Service clusters to enable Defender profile 16 | $Policy = Get-AzPolicyDefinition -Name '64def556-fbad-4622-930e-72d1d5589bf5' 17 | New-AzPolicyAssignment -Name 'Defender for Containers provisioning AKS Security Profile' -PolicyDefinition $Policy -Scope "/subscriptions/$($sub.Id)" -IdentityType 'SystemAssigned' -Location 'eastus' 18 | 19 | } 20 | 21 | # Check for Policy Assignment: 'Defender for Containers provisioning ARC k8s Enabled' using Defintion: [Preview]: Configure Azure Arc enabled Kubernetes clusters to install Microsoft Defender for Cloud extension 22 | $result2 = Get-AzPolicyAssignment -Name 'Defender for Containers provisioning ARC k8s Enabled' 23 | 24 | If($result2 -eq $null){ 25 | 26 | # Policy Name: [Preview]: Configure Azure Arc enabled Kubernetes clusters to install Microsoft Defender for Cloud extension 27 | $Policy = Get-AzPolicyDefinition -Name '0adc5395-9169-4b9b-8687-af838d69410a' 28 | New-AzPolicyAssignment -Name 'Defender for Containers provisioning ARC k8s Enabled' -PolicyDefinition $Policy -Scope "/subscriptions/$($sub.Id)" -IdentityType 'SystemAssigned' -Location 'eastus' 29 | 30 | 31 | } 32 | 33 | # Check for Policy Assignment: 'Defender for Containers provisioning Azure Policy Addon for Kub' using Defintion: Deploy Azure Policy Add-on to Azure Kubernetes Service clusters 34 | $result3 = Get-AzPolicyAssignment -Name 'Defender for Containers provisioning Azure Policy Addon for Kub' 35 | 36 | If($result3 -eq $null){ 37 | 38 | # Policy Name: Deploy Azure Policy Add-on to Azure Kubernetes Service clusters 39 | $Policy = Get-AzPolicyDefinition -Name '708b60a6-d253-4fe0-9114-4be4c00f012c' 40 | New-AzPolicyAssignment -Name 'Defender for Containers provisioning Azure Policy Addon for Kub' -PolicyDefinition $Policy -Scope "/subscriptions/$($sub.Id)" -IdentityType 'SystemAssigned' -Location 'eastus' 41 | 42 | 43 | } 44 | 45 | # Check for Policy Assignment: 'Defender for Containers provisioning Policy extension for Arc-e' using Defintion: [Preview]: Configure Azure Arc enabled Kubernetes clusters to install the Azure Policy extension 46 | $result4 = Get-AzPolicyAssignment -Name 'Defender for Containers provisioning Policy extension for Arc-e' 47 | 48 | If($result4 -eq $null){ 49 | 50 | # Policy Name: [Preview]: Configure Azure Arc enabled Kubernetes clusters to install the Azure Policy extension 51 | $Policy = Get-AzPolicyDefinition -Name 'a8eff44f-8c92-45c3-a3fb-9880802d67a7' 52 | New-AzPolicyAssignment -Name 'Defender for Containers provisioning Policy extension for Arc-e' -PolicyDefinition $Policy -Scope "/subscriptions/$($sub.Id)" -IdentityType 'SystemAssigned' -Location 'eastus' 53 | 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /SetCompanyASCPolicy.ps1: -------------------------------------------------------------------------------- 1 | # See https://github.com/Javanite/Azure-Security-Center for PS Module being used in script. 2 | #Set Global ASC Policy Decisions 3 | 4 | $secemail = "security@customdomain.com,user@customdomain.com" 5 | $secphone = "1112223333" 6 | $secnotify = "true" 7 | $ownernotify = "true" 8 | $datacollect = "on" 9 | 10 | 11 | #Download file and place in C:\Temp 12 | 13 | New-Item -ItemType Directory -Path C:\Temp -ErrorAction SilentlyContinue 14 | 15 | cd C:\Temp 16 | 17 | 18 | #Download the ASC PS Module and Functions 19 | Invoke-WebRequest "https://raw.githubusercontent.com/Javanite/Azure-Security-Center/master/Azure-Security-Center/Azure-Security-Center.psm1" -OutFile C:\Temp\Azure-Security-Center.psm1 20 | 21 | #Import the ASC module, store module file in C:\temp\ 22 | Import-Module "C:\temp\Azure-Security-Center.psm1" 23 | 24 | #Prompt Azure Owner for credetials to login and select azure subscriptions 25 | #Get-ASCCredential - Used in the older version of module no longer defined, now uses Login-ASC 26 | 27 | Login-ASC 28 | 29 | #Loop through sunbscriptions on same AAD tenant and apply ASC Policy settings defined in variables 30 | (Get-AzureRmSubscription).subscriptionId | foreach { 31 |         Set-Variable -Name asc_subscriptionId -Value $_ -Scope Global 32 |         #$ASCpolicyname = Get-ASCPolicy | select -First 1 33 | Set-ASCPolicy -PolicyName default -JSON (Build-ASCJSON -Type Policy -PolicyName default -AllOn -SecurityContactEmail $secemail -SecurityContactPhone $secphone -SecurityContactNotificationsOn $secnotify -SecurityContactSendToAdminOn $ownernotify -DataCollection $datacollect ) 34 |     } 35 | 36 | #Launch ASC in portal.azure.com for confirmation of setting changes. 37 | $Browser=new-object -com internetexplorer.application 38 | 39 | $Browser.navigate2("https://portal.azure.com/#blade/Microsoft_Azure_Security/SecurityMenuBlade/1") 40 | 41 | $Browser.visible=$true 42 | -------------------------------------------------------------------------------- /StopShutdownVMs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .DESCRIPTION 4 | 5 | Runbook Checks for VMs in Shutdown state from a OS and Stops \ Deallocates them in Azure 6 | 7 | .NOTES 8 | 9 | AUTHOR: Nathan Swift 10 | 11 | LASTEDIT: 2/5/2018 12 | #> 13 | 14 | $connectionName = "AzureRunAsConnection" 15 | 16 | try 17 | 18 | { 19 | # Get the connection "AzureRunAsConnection " 20 | $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName 21 | 22 | "Logging in to Azure..." 23 | 24 | Add-AzureRmAccount ` 25 | -ServicePrincipal ` 26 | -TenantId $servicePrincipalConnection.TenantId ` 27 | -ApplicationId $servicePrincipalConnection.ApplicationId ` 28 | -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 29 | } 30 | 31 | catch { 32 | if (!$servicePrincipalConnection) 33 | { 34 | $ErrorMessage = "Connection $connectionName not found." 35 | throw $ErrorMessage 36 | } else{ 37 | Write-Error -Message $_.Exception 38 | throw $_.Exception 39 | } 40 | } 41 | 42 | # Set Azure Subscription Context 43 | $SubName = "Yoursubscriptionnamehere" 44 | Set-AzureRmContext -Subscription $SubName 45 | 46 | # Collect VMs in a state of Shutdown but not Stopped 47 | $VMs = Get-AzureRmVM -Status | Where-Object {$_.PowerState -contains "VM stopped"} 48 | 49 | #Run through and stop VMs that were in a OS shutdown state 50 | foreach ($VM in $VMs) { 51 | 52 | Stop-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -Force 53 | 54 | } -------------------------------------------------------------------------------- /SubCoreCheck.ps1: -------------------------------------------------------------------------------- 1 | Login-AzureRmAccount 2 | 3 | $Sub = Get-AzureRmSubscription | Out-GridView -PassThru 4 | 5 | Set-AzureRmContext -Subscription $Sub.Name 6 | 7 | $outputFile = "C:\temp\"+$Sub.Name+".txt" 8 | 9 | #Get all VMs in Subscription 10 | $VMs = Get-AzureRmVM 11 | 12 | #Set and apply 1st line of csv headers 13 | $vmstring = "ResourceGroup,VMName,Location,NumberOfCores,VMSize,Publisher,Offer,Sku" 14 | $vmstring | Out-File $outputFile -append -force 15 | 16 | # Loop and iterate through all VMs 17 | foreach($VM in $VMs) { 18 | 19 | #Find VM OS Properties 20 | $resource = (Get-AzureRmResource -ResourceName $VM.Name -ResourceType "Microsoft.Compute/virtualMachines" -ResourceGroupName $VM.ResourceGroupName -ApiVersion 2017-03-30).properties.storageProfile | Select imageReference 21 | 22 | #Find VM Size 23 | $vmhw = (Get-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.name).hardwareProfile.VmSize 24 | 25 | #Find VM Cores 26 | $vmhwcore = Get-AzureRMvmsize -location $VM.Location | ?{ $_.name -eq $vmhw } 27 | 28 | #Find VM Image Publisher 29 | $publisher = $resource.psobject.properties.value.publisher 30 | 31 | #Find VM Image Offer 32 | $offer = $resource.psobject.properties.value.offer 33 | 34 | #Find VM SKU 35 | $sku = $resource.psobject.properties.value.sku 36 | 37 | # $vmstring = "$VM.name ,$VM.ResourceGroupName,$VM.Location,$VM.HardwareProfile.VMSize,$publisher,$offer,$sku,$vmhwcore.NumberOfCores,$Sub.Name" 38 | 39 | # Write out VM line of data collected and place into csv 40 | $vmstring = "$($VM.ResourceGroupName),$($VM.name),$($VM.Location),$($vmhwcore.NumberOfCores),$($VM.HardwareProfile.VMSize),$($publisher),$($offer),$($sku)" 41 | 42 | $vmstring | Out-File $outputFile -append -force 43 | 44 | } 45 | 46 | # Once done imort the data into excel -------------------------------------------------------------------------------- /TableRetentionReport.ps1: -------------------------------------------------------------------------------- 1 | ## REQS ## You will need ARMClient, AZ CLI, and Az modules installed 2 | 3 | # Variables for Workspace to report on 4 | $subid = "" 5 | $workspaceid = "" 6 | $workspacerg = "" 7 | $workspacename = "" 8 | 9 | # Authentication time across PS and ARMClient 10 | 11 | Login-AzAccount 12 | 13 | Set-AzContext -SubscriptionId $subid 14 | 15 | ARMClient.exe azlogin 16 | 17 | # Create a report hash table 18 | $report = $null 19 | $report = @{} 20 | 21 | # get distinct tables where data is stored in Log Analytics 22 | $tables = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceid -Query "union withsource = tt * | distinct tt" 23 | 24 | $tables = $tables.Results.tt 25 | 26 | #Obtain Log ANalytics retention setting at workspace 27 | $works = Get-AzOperationalInsightsWorkspace -ResourceGroupName $workspacerg -Name $workspacename 28 | $worksret = $works.retentionInDays 29 | 30 | # Iterate through the differnt tables and report on their retention settings 31 | foreach ($table in $tables){ 32 | 33 | # Using ARM to find the data table properties 34 | $armcall = "/subscriptions/" + $subid + "/resourceGroups/" + $workspacerg + "/providers/Microsoft.OperationalInsights/workspaces/" + $workspacename + "/Tables/" + $table + "?api-version=2017-04-26-preview" 35 | 36 | $answer = armclient GET $armcall 37 | 38 | # conversion to manipulate strings easier 39 | $answer = $answer | ConvertFrom-Json 40 | 41 | # conditional check the data table has a unique retention set 42 | if ($answer.properties.retentionInDays -ge 1){ 43 | 44 | Write-host "True" 45 | Write-Host $answer.properties.retentionInDays 46 | Write-Host $table 47 | $report.Add( $table, $answer.properties.retentionInDays ) 48 | 49 | } 50 | 51 | # If not uniue then the data table is getting retention from the workspace setting 52 | Elseif ($answer.properties.retentionInDays -le 1) { 53 | 54 | Write-host "False" 55 | Write-Host $worksret 56 | Write-Host $table 57 | $report.Add( $table, $worksret ) 58 | 59 | } 60 | 61 | } 62 | 63 | $report -------------------------------------------------------------------------------- /TimerSentinelASCSync.ps1: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | ## Author: Nathan Swift 3 | ## Date: 02.13.2020 4 | ## Description: This script acts as a sync engine between Sentinel Incidents that are closed and then dismissing the matched ASC Alert. 5 | ## Needs PS Core 6 or 7, Az.Security and AzSentinel modules 6 | # Install-Module -Name Az.Security 7 | # Install-Module -Name AzSentinel 8 | ######################################################################################### 9 | 10 | #Some authentication to Azure to occur, Function as MSI or Automation Account RunAs 11 | 12 | #Set timing filter 13 | $filterDate = (Get-Date).AddDays(-1) 14 | 15 | #get a list of closed incidents within timeframe 16 | $incidents = Get-AzSentinelIncident -WorkspaceName SwiftEnvLogs | Where-Object {$_.Status -eq "Closed" -and $_.lastUpdatedTimeUtc -ge $filterDate -and $_.relatedAlertProductNames -contains "Azure Security Center"} 17 | 18 | #get a list of open ASC Alerts within time frame 19 | $alerts = Get-AzSecurityAlert | Where-Object {$_.State -eq "Active" -and $_.ReportedTimeUtc -ge $filterDate} 20 | 21 | # loop through closed incidents and compare against ASC alerts and find a match 22 | ForEach ($incident in $incidents){ 23 | 24 | # Room for improvement use Switch instead of nested foreach loops 25 | Foreach ($alert in $alerts){ 26 | 27 | ## Matching condition titles must match, timestamps on the second must match 28 | if ($incident.title -eq $alert.AlertDisplayName -and $incident.endTimeUtc.ToString("yyyyMMddTHHmmss") -match $alert.DetectedTimeUtc.ToString("yyyyMMddTHHmmss")){ 29 | 30 | ## Used to tshoot and verify Match Condition logic above in If statement 31 | #Write-Host "TitleMatchfound" 32 | #Write-Host "Sentinel" 33 | #Write-Host $incident.title 34 | #Write-Host $incident.endTimeUtc 35 | #Write-Host "ASC" 36 | #Write-Host $alert.AlertDisplayName 37 | #Write-Host $alert.DetectedTimeUtc 38 | #Write-Host $alert.InstanceId 39 | #Write-Host $incident.caseNumber 40 | 41 | #Dismiss the ASC Alert 42 | Set-AzSecurityAlert -ResourceId $alert.Id -ActionType Dismiss 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Update-SQLBaseLineDBOwner.ps1: -------------------------------------------------------------------------------- 1 | # Example as is script, please test before using 2 | 3 | $ruleid = "VA1258" 4 | $exemptdata = @( 'wallstreetkid', 'wizkid', 'sampler') 5 | 6 | # Point to the local function created for a wrapper for Express Configuration of SQL DB Vulnerability Assessment 7 | ## .psm1 wrapper can be found here: https://learn.microsoft.com/en-us/azure/defender-for-cloud/express-configuration-sql-commands 8 | Import-Module .\SqlVulnerabilityAssessmentCommands.psm1 9 | 10 | Connect-AzAccount 11 | 12 | $Subs = Get-AzSubscription 13 | 14 | # Loop through all subscriptions 15 | Foreach ($Sub in $Subs) { 16 | 17 | # Set the subscription context 18 | Set-AzContext -Subscription $Sub.Id 19 | 20 | # Get all SQL servers and databases 21 | $SQLs = Get-AzResource -ResourceType "Microsoft.Sql/servers/databases" 22 | 23 | # Loop through all SQL servers and databases 24 | foreach ($SQL in $SQLs) { 25 | 26 | # in the results of the Get-AzResource command the SQL server and database are concatenated with a /, we split them here 27 | $serverName = $SQL.Name.Split("/")[0] 28 | $databaseName = $SQL.Name.Split("/")[1] 29 | 30 | # try for classic baseline assessment, in classic there is a storage account referenced, if the sql server\db is in express it will error and we move to the catch 31 | try { 32 | 33 | # using a -ErrorAction Stop to catch the error and move to the catch block 34 | Set-AzSqlDatabaseVulnerabilityAssessmentRuleBaseline -ServerName $serverName -DatabaseName $databaseName -RuleId $ruleid -ResourceGroupName $sql.ResourceGroupName -BaselineResult $exemptdata -ErrorAction Stop 35 | 36 | } 37 | 38 | # if you are using a express configuration for the baseline then an erro occurs stating a lack of a storage account, we then try the PS Wrapper commands we imported earlier to update the baseline exempt 39 | catch { 40 | Set-SqlVulnerabilityAssessmentBaseline -SubscriptionId $Sub.Id -ResourceGroupName $sql.ResourceGroupName -ServerName $serverName -DatabaseName $databaseName -Body '{ 41 | "properties": { 42 | "latestScan": false, 43 | "results": { 44 | "VA1258": [ 45 | [ 46 | "wallstreetkid" 47 | ], 48 | [ 49 | "wizkid" 50 | ], 51 | [ 52 | "sampler" 53 | ] 54 | ] 55 | } 56 | } 57 | }' 58 | } 59 | 60 | } 61 | 62 | # Untested with SQL MI and databases, remove commentsfor testing and possible further refinement 63 | <# 64 | 65 | $SQLMIs = Get-AzResource -ResourceType "Microsoft.Sql/managedInstances/databases" 66 | 67 | foreach ($SQLMI in $SQLMIs) { 68 | 69 | $instanceName = $SQL.Name.Split("/")[0] 70 | $databaseName = $SQL.Name.Split("/")[1] 71 | Set-AzSqlInstanceDatabaseVulnerabilityAssessmentRuleBaseline -InstanceName $instanceName -DatabaseName $databaseName -RuleId $ruleid -ResourceGroupName $sql.ResourceGroupName -BaselineResult $exemptdata 72 | 73 | } 74 | 75 | #> 76 | 77 | } -------------------------------------------------------------------------------- /UpdateAzSubsASCMDEforLinuxIntegration.ps1: -------------------------------------------------------------------------------- 1 | ## REQS ## You will need ARMClient, Az modules installed. User running script should be Security Admin over Subscriptions 2 | 3 | 4 | <# 5 | NOTES: 6 | 7 | The next time you return to the ASC integrations page of the Azure portal, the Enable for Linux machines button won't be shown. 8 | 9 | Link: https://docs.microsoft.com/en-us/azure/security-center/security-center-wdatp?tabs=linux#existing-users-of-azure-defender-and-microsoft-defender-for-endpoint-for-windows 10 | 11 | #> 12 | 13 | #Login into Azure enviroment 14 | Login-AzAccount 15 | ARMClient.exe azlogin 16 | 17 | # PUT call body to enable the ASC-MDE Linux 18 | $payload1 = "{'name': 'WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW','type': 'Microsoft.Security/settings','kind': 'DataExportSettings','properties': {'enabled': false}}" 19 | 20 | # gather all subscriptions 21 | $subs = Get-AzSubscription 22 | 23 | # For each subscription check and set ASC-MDE Linux setting. get\put and invoke REST GET WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW API 24 | Foreach ($sub in $subs){ 25 | 26 | # Set subscription context 27 | 28 | #Subscription Id 29 | $subid = $sub.Id 30 | 31 | Set-AzContext -SubscriptionId $subid 32 | 33 | # ARM Call URL invoke REST WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW API 34 | $armcall = "/subscriptions/" + $subid + "/providers/Microsoft.Security/settings/WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW?api-version=2021-07-01" 35 | 36 | # Make ARM Client call for GET WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW API 37 | $check = armclient GET $armcall 38 | 39 | # Convert from JSON to Table 40 | $check = $check | ConvertFrom-Json 41 | 42 | #write out results 43 | Write-Host -ForegroundColor Magenta "Subscription: " $sub.Name " - " $subid " - Is Linux ASC-MDE NOT Integrated?: " $check.properties.enabled 44 | 45 | #Check to see if ASC-MDE Linuc is NOT Integrated or Excluded = true > WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW 46 | if ($check.properties.enabled -eq $true) { 47 | 48 | # Make ARM Client call for PUT WDATP_EXCLUDE_LINUX_PUBLIC_PREVIEW API 49 | # Update Subscription and ASC to False to exclude setting 50 | armclient PUT $armcall $payload1 51 | 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /UpdateStorageATP.ps1: -------------------------------------------------------------------------------- 1 | # Set subscription context 2 | $sub = "SUBSCRIPTIONNAME OR GUID" 3 | Set-AzContext -Subscription $sub 4 | 5 | # Collect Storage Accounts in Subscrition 6 | $storeaccts = Get-AzStorageAccount 7 | # Exports CSV list of storage accounts, customer can remove accounts as needed 8 | $storeaccts | Export-Csv c:\temp\storageaccounts.csv 9 | # Prompt a break in script to allow the customer to modify the .CSV list 10 | Read-Host -Prompt 'Please modify c:\temp\storageaccount.csv removing any storage accounts you do not want to have Storage ATP turned on, Once finished press enter to continue script' 11 | # Import the modified storage account .csv list 12 | $stores = Import-csv c:\temp\storageaccounts.csv 13 | 14 | # Install az.security module 15 | Install-Module Az.Security -Force 16 | 17 | # Disable ASC Storage protection on Subscription, switch to free 18 | Write-Host "Disabling Storage Account Protectition on ASC" 19 | Set-AzSecurityPricing -Name "StorageAccounts" -PricingTier "Free" 20 | 21 | # Run through each storage id in imported modified list and enable ATP on Storage Account 22 | foreach($store in $stores){ 23 | 24 | Enable-AzSecurityAdvancedThreatProtection -ResourceId $store.id 25 | Write-Host "Storage Account ATP Enabled on " $store.StorageAccountName 26 | 27 | } -------------------------------------------------------------------------------- /VMConnectionFlows.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .REQUIREMENTS 4 | That the Log Analytics Workspace have the ServiceMap Solution and WireData Solution installed. 5 | 6 | .DESCRIPTION 7 | A script that will export some of the ServiceMap Details around inboun and outbound connections in you enviroment for further analysis. 8 | Might be ran before an Azure Migration project was ending the 180 Day Free Trial of ServiceMap 9 | Might be ran for additional analysis offline or update of a CMDB 10 | 11 | .NOTES 12 | AUTHOR: Nathan Swift 13 | LASTEDIT: July 2, 2019 14 | #> 15 | 16 | # variables 17 | $Sub = "YOURScriptionName" 18 | $WorkspaceId = "YOURLogAnalyticsWorkSpaceID" 19 | 20 | #login and set Subscription context 21 | Login-AzAccount 22 | $Sub = Set-AzContext -Subscription $Sub 23 | 24 | #Outbound Query will filter out same to same IPs, will include a TO:Computername, and find distinct connections outbound 25 | $QueryOut = @' 26 | 27 | //UNIQUE Outbound Flows to a VM 28 | VMConnection 29 | | where SourceIp != "127.0.0.1" and DestinationIp != "127.0.0.1" 30 | | distinct Computer, ProcessName, SourceIp, DestinationIp, DestinationPort, Direction 31 | | join kind = fullouter ( WireData ) 32 | on $left.DestinationIp == $right.LocalIP 33 | | where Direction == "outbound" 34 | | where SourceIp != DestinationIp 35 | | distinct Computer, Computer1, ProcessName, DestinationPort, Direction, SourceIp, DestinationIp 36 | 37 | '@ 38 | 39 | #Outbound Query will filter out same to same IPs, will include a From:Computername, and find distinct connections inbound 40 | $QueryIn = @' 41 | 42 | VMConnection 43 | | where SourceIp != "127.0.0.1" and DestinationIp != "127.0.0.1" 44 | | distinct Computer, ProcessName, SourceIp, DestinationIp, DestinationPort, Direction 45 | | join kind = fullouter ( WireData ) 46 | on $left.SourceIp == $right.LocalIP 47 | | where Direction == "inbound" 48 | | where SourceIp != DestinationIp 49 | | distinct Computer1, Computer, ProcessName, DestinationPort, Direction, SourceIp, DestinationIp 50 | 51 | '@ 52 | 53 | # Invoke the PS KQL Query, save as a object, invoke query for 24 Hours, adjustable as needed. 54 | $queryresultsout = Invoke-AzOperationalInsightsQuery -WorkspaceId $WorkspaceId -Query $QueryOut -Timespan (New-TimeSpan -Hours 24) 55 | $queryresultsin = Invoke-AzOperationalInsightsQuery -WorkspaceId $WorkspaceId -Query $QueryIn -Timespan (New-TimeSpan -Hours 24) 56 | 57 | # parse the results out of the object query job and export to a CSV formatted file 58 | $queryresultsout.Results | Export-Csv C:\temp\VMConnectionsOutbound.csv 59 | $queryresultsin.Results | Export-Csv C:\temp\VMConnectionsInbound.csv 60 | -------------------------------------------------------------------------------- /VMInsightsHealth-SetActionGroup.ps1: -------------------------------------------------------------------------------- 1 | #PRE REQs - ARMCLient, Azure CLI, Azure PowerShell, an ActionGroup created 2 | 3 | # Sets a specific Action Group on all VMs using VM Insights Health, specifically useful to bulk update VMs ALerts to a ActionGroup - Email, ServiceNow Ticket, or Automation. 4 | 5 | # Login in 6 | Connect-AzAccount 7 | 8 | armclient login 9 | 10 | # Global Variables, see $Payload - values must be manually entered for SubId and ActionGroup Name 11 | $subid = "" 12 | $actiongroup = "" 13 | 14 | # SPecific Payload REST PUT API to modify VM Alert to a Action Group 15 | $payload = "{'properties':{'ActionGroupResourceIds':['/subscriptions/{SUBSCRIPTIONIDHERE}/resourceGroups/default-activitylogalerts/providers/microsoft.insights/actionGroups/{ACTIONGROUPNAMEHERE}']}}" 16 | 17 | # Find all the VMs 18 | $VMs = Get-AzVm 19 | 20 | # Foreach VM set the VM Alert to use a Action Group 21 | Foreach ($VM in $VMs){ 22 | 23 | $url = "https://management.azure.com/subscriptions/" + $subid + "/resourceGroups/" + $VM.ResourceGroupName + "/providers/Microsoft.Compute/virtualMachines/" + $VM.Name + "/providers/Microsoft.WorkloadMonitor/notificationSettings/default?api-version=2018-08-31-preview" 24 | armclient PUT $url $payload 25 | 26 | } -------------------------------------------------------------------------------- /VMInventoryV2.ps1: -------------------------------------------------------------------------------- 1 | # Created On: 6/13/2018 1:21 PM 2 | # Created By: Nathan Swift nate.swift@live.com 3 | # This script is as is and not supported by Microsoft 4 | # Microsoft does not assume any risk of data loss 5 | # Use it at your own risk 6 | ################################################################################ 7 | 8 | <# Possible Futures: 9 | 10 | 1. Logic to handle multiple Nics and information on VM, especially for NVAs 11 | 2. Logic to handle disk detection count, size, and performance of disks attached to VM, 12 | 3. Provide some samples/Guidance for authentication using SPN, Scheduled Task, Stored Cred in Win, AutomationAccount 13 | 14 | #> 15 | 16 | 17 | # Authenticate Piece 18 | Login-AzureRmAccount 19 | 20 | # Time Tracking Start 21 | $datetime = Get-Date 22 | Write-Host $datetime 23 | 24 | # File check to overwrite existing vm inventory collection 25 | $filecheck = Get-FileHash -Path C:\temp\subsvminventory.txt 26 | 27 | If ($filecheck.Path -eq "C:\temp\subsvminventory.txt") 28 | { 29 | Remove-Item -Path "C:\temp\subsvminventory.txt" 30 | Write-host "Removed Previous File" 31 | } 32 | else 33 | { 34 | Write-host "No Previous File Found" 35 | } 36 | 37 | # Collect all subscriptions 38 | $subs = Get-AzureRmSubscription 39 | 40 | # Outputfile for vm inventory 41 | $outputFile = "C:\temp\subsvminventory.txt" 42 | 43 | #Set and apply 1st line of csv headers 44 | $vmstring = "Subscription,ResourceGroup,VMName,Location,MACAddress,IPAdress,Allocation,NumberOfCores,MemoryMB,VMSize,Publisher,Offer,Sku" 45 | $vmstring | Out-File $outputFile -append -force 46 | 47 | # Iterate through all subscriptions 48 | foreach($sub in $subs) { 49 | 50 | # Set the current Azure Subscription to pull information from 51 | Set-AzureRmContext -Subscription $sub.Name 52 | 53 | 54 | #Collect all VMs in current context Subscription 55 | $VMs = Get-AzureRmVM 56 | 57 | # Loop and iterate through all VMs to begin collecting data 58 | foreach($VM in $VMs) { 59 | 60 | #Find VM OS Properties 61 | $image = ($VM).Storageprofile.ImageReference 62 | 63 | #Find VM Size 64 | $vmhw = ($VM).hardwareProfile.VmSize 65 | 66 | #Find VM Cores 67 | $vmhwcore = Get-AzureRMvmsize -location $VM.Location | ?{ $_.name -eq $vmhw } 68 | 69 | #Find VM Image Publisher 70 | $publisher = $image.publisher 71 | 72 | #If Publisher is NULL VM Image is Custom Maintained 73 | If ($publisher -eq $null) 74 | { 75 | $publisher = "CustomImage" 76 | } 77 | 78 | #Find VM Image Offer 79 | $offer = $image.offer 80 | 81 | #Find VM SKU 82 | $sku = $image.sku 83 | 84 | #Obtain Nic Configuration and obtain/use use the resourceid to get nic properties 85 | ## Only collecting the Primary or 1st NIC on VM, NVAs could have multiple nics, script logic will need to be introduced and csv format thought through for multiple entries in a column to handle multiple nics and store data however this logic is not baked in the script, possibly future release 86 | $VMnicid = ($VM).NetworkProfile.NetworkInterfaces[0].Id 87 | $VMnicprop = Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties 88 | 89 | #Obtain MAC Address 90 | $VMAMC = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.macAddress 91 | 92 | #Obtain IP Address of Nic ## Stored even when VM is Stopped Deallocated, however MAC is not stored when Stopped Deallocated 93 | $VMprivip = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.ipConfigurations[0].properties.PrivateIPAddress 94 | 95 | #Obtain IP Address Allocation 96 | $VMprivipq = (Get-AzureRmResource -ResourceId $VMNicid -ExpandProperties).Properties.ipConfigurations[0].properties.privateIPAllocationMethod 97 | 98 | # Write out VM line of data collected and place into csv 99 | $vmstring = "$($sub.Name),$($VM.ResourceGroupName),$($VM.name),$($VM.Location),$($VMAMC),$($VMprivip),$($VMprivipq),$($vmhwcore.NumberOfCores),$($vmhwcore.MemoryInMB),$($VM.HardwareProfile.VMSize),$($publisher),$($offer),$($sku)" 100 | 101 | #Write into and append into output file 102 | $vmstring | Out-File $outputFile -append -force 103 | 104 | } 105 | 106 | 107 | } 108 | 109 | # Time Tracking Finished 110 | $datetime = Get-Date 111 | Write-Host $datetime 112 | 113 | # Once done import the data into excel -------------------------------------------------------------------------------- /VerifyZIAWebLogDatatoSentinelExample.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -AssemblyName System.Web 2 | 3 | $appid = 'APP ID' 4 | $tenantId = 'TENANT ID' 5 | $appSecret = 'APP SECRET' 6 | 7 | $dcrImmutableId = 'DCR ID HERE' 8 | $dceEndpoint = 'https://DCEENDPOINT.LOCATION-1.ingest.monitor.azure.com' 9 | $streamName = 'STREAM NAME_CL' 10 | 11 | 12 | $scope = [System.Web.HttpUtility]::UrlEncode("https://monitor.azure.com//.default") 13 | $body = "client_id=$appId&scope=$scope&client_secret=$appSecret&grant_type=client_credentials" 14 | $headers = @{ 15 | 'Content-Type' = 'application/x-www-form-urlencoded' 16 | } 17 | 18 | $uri = "https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token" 19 | 20 | $bearerToken = (Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers).access_token 21 | 22 | $staticData = @" 23 | [{ "sourcetype" : "zscalernss-web", "TimeGenerated":"2023-02-17 22:55:01", "act":"Blocked", "reason":"Blocked", "app":"HTTPS", "dhost":"www.etsy.com", "dst":"104.94.233.143", "src":"40.83.138.250", "sourceTranslatedAddress":"10.2.3.4", "in":"50", "out":"10", "request":"www.1etsy.com/dac/common/web-toolkit/scoped/scoped_responsive_base.20220526203537%2csite-chrome/deprecated/global-nav.20220526203537%2ccommon/web-toolkit/a11y_colors/overrides.20220526203537.css", "requestContext":"www.1etsy.com/c/clothing-and-shoes?ref=catnav-10923", "outcome":"200", "requestClientApplication":"Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1", "requestMethod":"GET", "suser":"test3@bd-dev.com", "spriv":"Road Warrior", "externalId":"8106135709380313090", "fileType":"GZIP ", "destinationServiceName":"Etsy", "cat":"Professional Services", "deviceDirection":"1", "cn1":"10", "cn1Label":"riskscore", "cs1":"General Group", "cs1Label":"dept", "cs2":"Phishing", "cs2Label":"urlcat", "cs3":"None", "cs3Label":"malwareclass", "cs4":"None", "cs4Label":"malwarecat", "cs5":"Bad_Threat", "cs5Label":"threatname", "cs6":"None", "cs6Label":"md5hash", "rulelabel":"None", "ruletype":"None", "urlclass":"Advanced Security Risk", "DeviceVendor":"Zscaler" , "DeviceProduct":"NSSWeblog" ,"devicemodel":"Virtual Machine" , "flexString1":"Virtual Machine", "flexString1Label":"devicemodel", "flexString2":"Advanced Security Risk", "flexString2Label":"urlclass"},{ "sourcetype" : "zscalernss-web", "TimeGenerated":"2023-02-17 22:55:02", "act":"Allowed", "reason":"Allowed", "app":"HTTP_PROXY", "dhost":"c.bing.com", "dst":"204.79.197.200", "src":"40.90.198.229", "sourceTranslatedAddress":"40.90.198.229", "in":"6500", "out":"110", "request":"c.bing.com:443", "requestContext":"None", "outcome":"200", "requestClientApplication":"Windows Microsoft Windows 10 Pro ZTunnel/1.0", "requestMethod":"CONNECT", "suser":"testuser2@bd-dev.com", "spriv":"Road Warrior", "externalId":"7093275726860451849", "fileType":"None", "destinationServiceName":"SharePoint", "cat":"Web Search", "deviceDirection":"1", "cn1":"0", "cn1Label":"riskscore", "cs1":"Service Admin", "cs1Label":"dept", "cs2":"Web Search", "cs2Label":"urlcat", "cs3":"None", "cs3Label":"malwareclass", "cs4":"None", "cs4Label":"malwarecat", "cs5":"None", "cs5Label":"threatname", "cs6":"None", "cs6Label":"md5hash", "rulelabel":"None", "ruletype":"None", "urlclass":"Business Use", "DeviceVendor":"Zscaler" , "DeviceProduct":"NSSWeblog" , "devicemodel":"Lenovo" , "flexString1":"Lenovo", "flexString1Label":"devicemodel", "flexString2":"Advanced Security Risk", "flexString2Label":"urlclass" },{ "sourcetype" : "zscalernss-web", "TimeGenerated":"2023-02-17 22:55:03", "act":"Blocked", "reason":"Access denied due to bad server certificate", "app":"HTTP_PROXY", "dhost":"hm.baidu.com", "dst":"103.235.46.191", "src":"52.233.90.167", "sourceTranslatedAddress":"52.233.90.167", "in":"65", "out":"55", "request":"ps.eyeota.net/pixel?pid=gdomg51&t=gif&cat=Economy&us_privacy=&random=1654532044229.2", "requestContext":"None", "outcome":"200", "requestClientApplication":"Windows Microsoft Windows 10 Pro ZTunnel/1.0", "requestMethod":"CONNECT", "suser":"test1@bd-dev.com", "spriv":"Road Warrior", "externalId":"9346135709564534789", "fileType":"None ", "destinationServiceName":"General Browsing", "cat":"Web Search", "deviceDirection":"1", "cn1":"0", "cn1Label":"riskscore", "cs1":"General Group", "cs1Label":"dept", "cs2":"Adware/Spyware Sites", "cs2Label":"urlcat", "cs3":"None", "cs3Label":"malwareclass", "cs4":"None", "cs4Label":"malwarecat", "cs5":"None", "cs5Label":"threatname", "cs6":"None", "cs6Label":"md5hash", "rulelabel":"Inspect_All", "ruletype":"SSLPol", "urlclass":"Business Use", "DeviceVendor":"Zscaler" , "DeviceProduct":"NSSWeblog" ,"devicemodel":"macbookpro", "flexString1":"macbookpro", "flexString1Label":"devicemodel", "flexString2":"Advanced Security Risk", "flexString2Label":"urlclass" }] 24 | "@; 25 | 26 | 27 | $body = $staticData; 28 | $headers = @{"Authorization"="Bearer $bearerToken";"Content-Type"="application/json"}; 29 | $uri = "$dceEndpoint/dataCollectionRules/$dcrImmutableId/streams/$streamName" + "?api-version=2021-11-01-preview" 30 | $uploadResponse = Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers -------------------------------------------------------------------------------- /arccsesample.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | #[Parameter (Mandatory= $true)] 4 | #[String] $publicip = "", 5 | 6 | #[Parameter (Mandatory= $false)] 7 | #[String] $action = "", 8 | 9 | #[Parameter (Mandatory= $false)] 10 | #[String] $direction = "", 11 | 12 | #[Parameter (Mandatory= $false)] 13 | #[String] $protocol = "", 14 | 15 | #[Parameter (Mandatory= $false)] 16 | #[String] $source = "" 17 | 18 | ) 19 | 20 | $guid = New-Guid 21 | $logfile = $guid.Guid + "csesqlfw.log" 22 | Start-Transcript C:\temp\$logfile 23 | 24 | New-NetFirewallRule -Name Allow_Ping -DisplayName “Allow Ping”` 25 | 26 | -Description “Packet Internet Groper ICMPv4” ` 27 | 28 | -Protocol ICMPv4 -IcmpType 8 -Enabled True -Profile Any -Action Allow ` 29 | 30 | -Debug 31 | 32 | Stop-Transcript 33 | -------------------------------------------------------------------------------- /continuousexportruleconfig.ps1: -------------------------------------------------------------------------------- 1 | #PRE REQs - ARMCLient, Azure CLI, Azure PowerShell, an ActionGroup created 2 | 3 | # Login in 4 | Connect-AzAccount 5 | armclient login 6 | 7 | #Your AAD Tenant ID if your login is associated to many tenants 8 | armclient token {YOUR AAD TENANT ID} 9 | 10 | # URL and payload for continuous Export Rule to Storage for Heartbeat and SecurityEvents Tables 11 | $url1 = "https://management.azure.com/subscriptions/{YOUR SUB ID}/resourceGroups/{YOUR RG NAME}/providers/Microsoft.OperationalInsights/workspaces/{YOUR WORKSPACE NAME}/dataexports/exporttostore?api-version=2019-08-01-preview" 12 | $payload1 = "{'properties':{'destination':{'resourceId':'/subscriptions/{YOUR SUB ID}/resourceGroups/{YOUR RG NAME}/providers/Microsoft.Storage/storageAccounts/{YOUR STORAGE NAME}'},'tablenames':['Heartbeat','SecurityEvent'],'enable':true}}" 13 | 14 | # URL and payload for continuous Export Rule to Event Hubs for Heartbeat and SecurityEvents Tables 15 | $url2 = "https://management.azure.com/subscriptions/{YOUR SUB ID}/resourceGroups/{YOUR RG NAME}/providers/Microsoft.OperationalInsights/workspaces/{YOUR WORKSPACE NAME}/dataexports/exporttoeh?api-version=2019-08-01-preview" 16 | $payload2 = "{'properties':{'destination':{'resourceId':'/subscriptions/{YOUR SUB ID}/resourceGroups/{YOUR RG NAME}/providers/Microsoft.EventHub/namespaces/{YOUR EVENTHUB NAME}'},'tablenames':['Heartbeat','SecurityEvent'],'enable':true}}" 17 | 18 | # PUT Api to setup continuous export rule to send to Storage 19 | armclient PUT $url1 $payload1 20 | 21 | # PUT Api to setup continuous export rule to send to Event Hubs 22 | armclient PUT $url2 $payload2 -------------------------------------------------------------------------------- /exportwineventsources.ps1: -------------------------------------------------------------------------------- 1 | ## REQS 2 | 3 | # PS Module Az, powershell-yaml 4 | # ARMClient 5 | 6 | #Variables 7 | 8 | $SubId = "SUBID HERE" 9 | $rgname = "RG NAME HERE" 10 | $workspacename = "WORKSPACE NAME HERE" 11 | 12 | #via ARMClient - API 13 | 14 | #https://management.azure.com/subscriptions/{SUBID}/resourcegroups/{ResourceGroup}/providers/Microsoft.OperationalInsights/workspaces/{WorkspaceName}/dataSources?%24filter=%24filter%3Dkind%20eq%20'WindowsEvent'&api-version=2020-08-01 15 | 16 | armclient login 17 | 18 | $armcall = "/subscriptions/" + $SubId + "/resourcegroups/" + $rgname + "/providers/Microsoft.OperationalInsights/workspaces/" + $workspacename + "/dataSources?%24filter=%24filter%3Dkind%20eq%20'WindowsEvent'&api-version=2020-08-01" 19 | 20 | $wineventsourceapi = armclient GET $armcall 21 | 22 | $wineventsourceapi | Out-File c:\temp\winevtsourcesapi.json 23 | 24 | 25 | # via PowerShell 26 | 27 | Login-AzAccount 28 | 29 | Set-AzContext -Subscription $SubId 30 | 31 | $wineventsource = Get-AzOperationalInsightsDataSource -WorkspaceName $workspacename -ResourceGroupName $rgname -Kind WindowsEvent 32 | 33 | $wineventsourcejson = $wineventsource | ConvertTo-Json -Depth 4 34 | 35 | $wineventsourceyaml = $wineventsource | ConvertTo-Yaml 36 | 37 | $wineventsourcejson | Out-File c:\temp\winevtsourcesps.json 38 | 39 | $wineventsourceyaml | Out-File c:\temp\winevtsourcesps.yaml -------------------------------------------------------------------------------- /loguploader.ps1: -------------------------------------------------------------------------------- 1 | function Send-AzMonitorCustomLogs { 2 | <# 3 | .SYNOPSIS 4 | Sends custom logs to a specific table in Azure Monitor. 5 | 6 | .DESCRIPTION 7 | Script to send data to a data collection endpoint which is a unique connection point for your subscription. 8 | The payload sent to Azure Monitor must be in JSON format. A data collection rule is needed in your Azure tenant that understands the format of the source data, potentially filters and transforms it for the target table, and then directs it to a specific table in a specific workspace. 9 | You can modify the target table and workspace by modifying the data collection rule without any change to the REST API call or source data. 10 | 11 | .PARAMETER LogPath 12 | Path to the log file or folder to read logs from and send them to Azure Monitor. 13 | 14 | .PARAMETER appId 15 | Azure Active Directory application to authenticate against the API to send logs to Azure Monitor data collection endpoint. 16 | This script supports the Client Credential Grant Flow. 17 | 18 | .PARAMETER appSecret 19 | Secret text to use with the Azure Active Directory application to authenticate against the API for the Client Credential Grant Flow. 20 | 21 | .PARAMETER TenantId 22 | ID of Tenant 23 | 24 | .PARAMETER DcrImmutableId 25 | Immutable ID of the data collection rule used to process events flowing to an Azure Monitor data table. 26 | 27 | .PARAMETER DceURI 28 | Uri of the data collection endpoint used to host the data collection rule. 29 | 30 | .PARAMETER StreamName 31 | Name of stream to send data to before being procesed and sent to an Azure Monitor data table. 32 | 33 | .PARAMETER TimestampField 34 | Specific field available in your custom log to select as the main timestamp. This will be the TimeGenerated field in your table. By default, this script uses a current timestamp. 35 | 36 | .PARAMETER ShowProgressBar 37 | Show a PowerShell progress bar. Disabled by default. 38 | 39 | .EXAMPLE 40 | PS> . .\Send-AzMonitorCustomLogs.ps1 41 | PS> Send-AzMonitorCustomLogs -LogPath C:\WinEvents.json -appId 'XXXX' -appSecret 'XXXXXX' -TenantId 'XXXXXX' -DcrImmutableId 'dcr-XXXX' -DceURI 'https://XXXX.westus2-1.ingest.monitor.azure.com' -StreamName 'Custom-WindowsEvent' -TimestampField 'TimeCreated' 42 | 43 | .EXAMPLE 44 | PS> . .\Send-AzMonitorCustomLogs.ps1 45 | PS> Send-AzMonitorCustomLogs -LogPath C:\WinEvents.json -appId 'XXXX' -appSecret 'XXXXXX' -TenantId 'XXXXXX' -DcrImmutableId 'dcr-XXXX' -DceURI 'https://XXXX.westus2-1.ingest.monitor.azure.com' -StreamName 'Custom-WindowsEvent' -TimestampField 'TimeCreated' -Debug 46 | 47 | .EXAMPLE 48 | PS> . .\Send-AzMonitorCustomLogs.ps1 49 | PS> Send-AzMonitorCustomLogs -LogPath C:\WinEventsFolder\ -appId 'XXXX' -appSecret 'XXXXXX' -TenantId 'XXXXXX' -DcrImmutableId 'dcr-XXXX' -DceURI 'https://XXXX.westus2-1.ingest.monitor.azure.com' -StreamName 'Custom-WindowsEvent' -TimestampField 'TimeCreated' -Debug 50 | # PS> Send-AzMonitorCustomLogs -LogPath .\origlog\ -appId 'XXXXXX' -appSecret 'XXXXXX' -TenantId 'XXXXXX' -DcrImmutableId 'XXXXXX' -DceURI 'https://apt-rtyj.eastus-1.ingest.monitor.azure.com' -StreamName 'Custom-CyberAPT_CL' -TimestampField 'EventTime' 51 | # Send-AzMonitorCustomLogs -LogPath .\origlog\ -appId 'XXXXXX' -appSecret 'XXXXXX' -TenantId 'XXXXXX' -DcrImmutableId 'XXXXXX' -DceURI 'https://apt-rtyj.eastus-1.ingest.monitor.azure.com' -StreamName 'Custom-WindowsEvent' -TimestampField 'EventTime' 52 | 53 | 54 | .NOTES 55 | # Author: Roberto Rodriguez (@Cyb3rWard0g) 56 | # License: MIT 57 | 58 | # Reference: 59 | # https://docs.microsoft.com/en-us/azure/azure-monitor/logs/custom-logs-overview 60 | # https://docs.microsoft.com/en-us/azure/azure-monitor/logs/tutorial-custom-logs-api#send-sample-data 61 | # https://securitytidbits.wordpress.com/2017/04/14/powershell-and-gzip-compression/ 62 | 63 | # Custom Logs Limit 64 | # Maximum size of API call: 1MB for both compressed and uncompressed data 65 | # Maximum data/minute per DCR: 1 GB for both compressed and uncompressed data. Retry after the duration listed in the Retry-After header in the response. 66 | # Maximum requests/minute per DCR: 6,000. Retry after the duration listed in the Retry-After header in the response. 67 | 68 | .LINK 69 | https://github.com/OTRF/Security-Datasets 70 | #> 71 | [CmdletBinding()] 72 | param ( 73 | [Parameter(Mandatory = $true)] 74 | [ValidateScript({ 75 | foreach ($f in $_) { 76 | if ( -Not ($f | Test-Path) ) { 77 | throw "File or folder does not exist" 78 | } 79 | } 80 | return $true 81 | })] 82 | [string[]]$LogPath, 83 | 84 | [Parameter(Mandatory = $true)] 85 | [string]$appId, 86 | 87 | [Parameter(Mandatory = $true)] 88 | [string]$appSecret, 89 | 90 | [Parameter(Mandatory = $true)] 91 | [string]$TenantId, 92 | 93 | [Parameter(Mandatory = $true)] 94 | [string]$DcrImmutableId, 95 | 96 | [Parameter(Mandatory = $true)] 97 | [string]$DceURI, 98 | 99 | [Parameter(Mandatory = $true)] 100 | [string]$StreamName, 101 | 102 | [Parameter(Mandatory = $false)] 103 | [string]$TimestampField, 104 | 105 | [Parameter(Mandatory = $false)] 106 | [switch]$ShowProgressBar 107 | ) 108 | 109 | If ($PSBoundParameters['Debug']) { 110 | $DebugPreference = 'Continue' 111 | } 112 | 113 | @("[+] Automatic log uploader is starting. Creator: Roberto Rodriguez @Cyb3rWard0g / License: MIT") 114 | 115 | # Aggregate files from input paths 116 | $all_datasets = @() 117 | foreach ($file in $LogPath) { 118 | if ((Get-Item $file) -is [system.io.fileinfo]) { 119 | $all_datasets += (Resolve-Path -Path $file) 120 | } 121 | elseif ((Get-Item $file) -is [System.IO.DirectoryInfo]) { 122 | $folderfiles = Get-ChildItem -Path $file -Recurse -Include *.json 123 | $all_datasets += $folderfiles 124 | } 125 | } 126 | 127 | write-Host "*******************************************" 128 | Write-Host "[+] Obtaining access token.." 129 | ## Obtain a bearer token used to authenticate against the data collection endpoint 130 | $scope = [System.Web.HttpUtility]::UrlEncode("https://monitor.azure.com//.default") 131 | $body = "client_id=$appId&scope=$scope&client_secret=$appSecret&grant_type=client_credentials"; 132 | $headers = @{"Content-Type" = "application/x-www-form-urlencoded" }; 133 | $uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 134 | $bearerToken = (Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers).access_token 135 | Write-Debug $bearerToken 136 | 137 | Function Send-DataToDCE($payload, $size) { 138 | write-debug "############ Sending Data ############" 139 | write-debug "JSON array size: $($size/1mb) MBs" 140 | 141 | # Initialize Headers and URI for POST request to the Data Collection Endpoint (DCE) 142 | $headers = @{"Authorization" = "Bearer $bearerToken"; "Content-Type" = "application/json" } 143 | $uri = "$DceURI/dataCollectionRules/$DcrImmutableId/streams/$StreamName`?api-version=2021-11-01-preview" 144 | #$uri = "$DceURI/dataCollectionRules/$DcrImmutableId/streams/$StreamName`?api-version=2021-12-01-preview" 145 | 146 | # Showing payload for troubleshooting purposes 147 | Write-Debug ($payload | ConvertFrom-Json | ConvertTo-Json) 148 | 149 | # Sending data to Data Collection Endpoint (DCE) -> Data Collection Rule (DCR) -> Azure Monitor table 150 | Invoke-RestMethod -Uri $uri -Method "Post" -Body (@($payload | ConvertFrom-Json | ConvertTo-Json)) -Headers $headers | Out-Null 151 | } 152 | 153 | # Maximum size of API call: 1MB for both compressed and uncompressed data 154 | $APILimitBytes = 1mb 155 | $currentTime = Get-Date 156 | 157 | foreach ($dataset in $all_datasets) { 158 | $total_file_size = (get-item -Path $dataset).Length 159 | $json_records = @() 160 | $json_array_current_size = 0 161 | $event_count = 0 162 | $total_size = 0 163 | 164 | # Create ReadLines Iterator and get total number of lines 165 | $readLineIterator = [System.IO.File]::ReadLines($dataset) 166 | $numberOfLines = [Linq.Enumerable]::Count($readLineIterator) 167 | 168 | write-Host "*******************************************" 169 | Write-Host "[+] Processing $dataset" 170 | Write-Host "[+] Dataset Size: $($total_file_size/1mb) MBs" 171 | Write-Host "[+] Number of events to process: $numberOfLines" 172 | Write-Host "[+] Current time: $currentTime" 173 | 174 | 175 | # Read each JSON object from file 176 | foreach ($line in $readLineIterator) { 177 | 178 | if ($currentTime.AddMinutes(50) -lt (Get-Date)) { 179 | ## Obtain a bearer token used to authenticate against the data collection endpoint 180 | Write-Host "[+] The bearer token is close to be expired. It's time to renew the token... " -NoNewline 181 | $scope = [System.Web.HttpUtility]::UrlEncode("https://monitor.azure.com//.default") 182 | $body = "client_id=$appId&scope=$scope&client_secret=$appSecret&grant_type=client_credentials"; 183 | $headers = @{"Content-Type" = "application/x-www-form-urlencoded" }; 184 | $uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 185 | $bearerToken = (Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers).access_token 186 | $currentTime = Get-Date 187 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 188 | } 189 | 190 | # Increase event number 191 | $event_count += 1 192 | 193 | 194 | # Update progress bar with current event count 195 | if ($ShowProgressBar) { Write-Progress -Activity "Processing files" -status "Processing $dataset" -percentComplete ($event_count / $numberOfLines * 100) } 196 | 197 | write-debug "############ Event $event_count ###############" 198 | if ($TimestampField) { 199 | $Timestamp = $line | Convertfrom-json | Select-Object -ExpandProperty $TimestampField 200 | } 201 | else { 202 | $Timestamp = Get-Date ([datetime]::UtcNow) -Format O 203 | } 204 | 205 | # Creating Dictionary for Log entry 206 | $log_entry = [ordered]@{ 207 | TimeGenerated = $Timestamp 208 | RawEventData = $line 209 | } 210 | 211 | # Processing Log entry as a compressed JSON object 212 | $message = $log_entry | ConvertTo-Json -Compress 213 | Write-Debug "Processing log entry: $($message.Length) bytes" 214 | 215 | # Getting proposed and current JSON array size 216 | $json_array_current_size = ([System.Text.Encoding]::UTF8.GetBytes(@($json_records | Convertfrom-json | ConvertTo-Json))).Length 217 | $json_array_proposed_size = ([System.Text.Encoding]::UTF8.GetBytes(@(($json_records + $message) | Convertfrom-json | ConvertTo-Json))).Length 218 | Write-Debug "Current size of JSON array: $json_array_current_size bytes" 219 | 220 | if ($json_array_proposed_size -le $APILimitBytes) { 221 | $json_records += $message 222 | $json_array_current_size = $json_array_proposed_size 223 | write-debug "New size of JSON array: $json_array_current_size bytes" 224 | } 225 | else { 226 | write-debug "Sending current JSON array before processing more log entries.." 227 | Send-DataToDCE -payload $json_records -size $json_array_current_size 228 | # Keeping track of how much data we are sending over 229 | $total_size += $json_array_current_size 230 | 231 | # There are more events to process.. 232 | write-debug "######## Resetting JSON Array ########" 233 | $json_records = @($message) 234 | 235 | $json_array_current_size = ([System.Text.Encoding]::UTF8.GetBytes(@($json_records | Convertfrom-json | ConvertTo-Json))).Length 236 | Write-Debug "Starting JSON array with size: $json_array_current_size bytes" 237 | } 238 | 239 | if ($event_count -eq $numberOfLines) { 240 | write-debug "##### Last log entry in $dataset #######" 241 | Send-DataToDCE -payload $json_records -size $json_array_current_size 242 | # Keeping track of how much data we are sending over 243 | $total_size += $json_array_current_size 244 | } 245 | } 246 | Write-Host "[+] Finished processing dataset" 247 | Write-Host "[+] Number of events processed: $event_count" 248 | Write-Host "[+] Total data sent: $($total_size/1mb) MBs" 249 | write-Host "*******************************************" 250 | } 251 | } 252 | 253 | 254 | function Load-Module ($m) { 255 | 256 | # If module is imported - do nothing 257 | if (Get-Module | Where-Object { $_.Name -eq $m }) { 258 | write-host "[+] Module $m is already imported. " -NoNewline 259 | write-host "Completed" -ForegroundColor White -BackgroundColor Green 260 | } 261 | else { 262 | 263 | # If module is not imported, but available on disk then import 264 | if (Get-Module -ListAvailable | Where-Object { $_.Name -eq $m }) { 265 | write-host "[+] $m is available, loading... " -NoNewline 266 | Import-Module $m 267 | write-host "Completed" -ForegroundColor White -BackgroundColor Green 268 | } 269 | else { 270 | 271 | # If module is not imported, not available on disk, but is in online gallery then install and import 272 | if (Find-Module -Name $m | Where-Object { $_.Name -eq $m }) { 273 | write-host "[+] $m is not available, installing... " -NoNewline 274 | Install-Module -Name $m -Force -Verbose -Scope CurrentUser 275 | write-host "Completed" -ForegroundColor White -BackgroundColor Green 276 | write-host "[+] $m is now available, loading... " -NoNewline 277 | Import-Module $m 278 | write-host "Completed" -ForegroundColor White -BackgroundColor Green 279 | } 280 | else { 281 | 282 | # If the module is not imported, not available and not in the online gallery then abort 283 | write-host "[!!!] Module $m not imported, not available and not in an online gallery, exiting." -BackgroundColor Red -ForegroundColor White 284 | EXIT 1 285 | } 286 | } 287 | } 288 | } 289 | 290 | 291 | 292 | Push-Location (Split-Path $MyInvocation.MyCommand.Path) 293 | 294 | Load-Module Microsoft.Graph.Applications 295 | Load-Module Az.OperationalInsights 296 | Load-Module Az.SecurityInsights 297 | Load-Module Az.Accounts 298 | Load-Module Az.Resources 299 | 300 | Write-Host "[+] Authenticating... " -NoNewline 301 | Connect-AzAccount -ErrorAction Stop | Out-Null 302 | 303 | write-host "Completed" -ForegroundColor White -BackgroundColor Green 304 | $tokenExpiryDate = (Get-Date).AddHours(1) 305 | 306 | Add-Type -AssemblyName System.Web 307 | 308 | $tempSub = Read-Host "Please enter your Azure subscription id" 309 | if (!($tempSub -eq "")) { 310 | $subscriptionId = $tempSub 311 | } 312 | else { 313 | Write-Host "[!] Subscription Id can't be empty. Exiting now." -BackgroundColor Red -ForegroundColor White 314 | Exit 315 | } 316 | 317 | Write-Host "*******************************************" 318 | Write-Host "[+] Setting variables... " -NoNewline 319 | 320 | 321 | 322 | $resourceGroup = "rg-sent-adv-hunting" 323 | $workspaceName = "sent-adv-hunting" 324 | $fuctionName = "fWindowsEvent" 325 | $location = "westeurope" 326 | 327 | $original_file = '.\orig\apt29_evals_day1_manual_2020-05-01225525.json' 328 | $destination_file = '.\apt29.json' 329 | $day1date = "2020-05-01" 330 | $day2date = "2020-05-02" 331 | ## Azure AD Graph's globally unique appId is 00000002-0000-0000-c000-000000000000 identified by the ResourceAppId 332 | $graphResourceId = "00000002-0000-0000-c000-000000000000" 333 | $appDisplayName = "app-sent-adv-hunting" 334 | $newResourceAccess = @{ 335 | ResourceAppId = $graphResourceId; 336 | ResourceAccess = @( 337 | @{ 338 | # User.Read scope (delegated permission) to sign-in and read user profile 339 | id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6"; 340 | type = "Scope"; 341 | } 342 | ) 343 | } 344 | 345 | $subs = Get-AzSubscription -SubscriptionId $subscriptionId -ErrorAction Stop 346 | 347 | if ($null -eq $subs) { 348 | Write-Host "[!!!] Can't get a subscription by id. Exiting now." -BackgroundColor Red -ForegroundColor White 349 | Exit 350 | } 351 | 352 | 353 | $subscriptionId = $subs.Id 354 | $tenantId = $subs.TenantId 355 | $workspaceResourceId = "/subscriptions/" + $subscriptionId + "/resourcegroups/" + $resourceGroup + "/providers/microsoft.operationalinsights/workspaces/" + $workspaceName 356 | $alertRuleName = "RTLO technique detected" 357 | $dceName = "sent-adv-hunting-dce" 358 | $dcrName = "sent-adv-hunting-dcr" 359 | $endpointDceUri = "https://management.azure.com/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroup + "/providers/Microsoft.Insights/dataCollectionEndpoints/" + $dceName + "?api-version=2021-09-01-preview" 360 | $dcrUri = "https://management.azure.com/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroup + "/providers/Microsoft.Insights/dataCollectionRules/" + $dcrName + "?api-version=2021-09-01-preview" 361 | $functionUri = "https://management.azure.com/subscriptions/" + $subscriptionId + "/resourcegroups/" + $resourceGroup + "/providers/Microsoft.OperationalInsights/workspaces/" + $workspaceName + "/savedSearches/" + $fuctionName + "/?api-version=2020-08-01" 362 | $dcrId = "/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroup + "/providers/Microsoft.Insights/dataCollectionRules/" + $dcrName 363 | $dceId = "/subscriptions/" + $subscriptionId + "/resourceGroups/" + $resourceGroup + "/providers/Microsoft.Insights/dataCollectionEndpoints/" + $dceName 364 | $roleContributorId="b24988ac-6180-42a0-ab88-20f7382dd24c" 365 | $roleMonitoringPublisherId="3913510d-42f4-4e42-8a64-420c390055eb" 366 | 367 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 368 | 369 | Write-Host "[+] Connecting to Microsoft Graph API... " -NoNewline 370 | Connect-MgGraph -TenantId $tenantId -Scopes "Application.ReadWrite.All" | Out-Null 371 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 372 | 373 | # Create the resource group if needed 374 | try { 375 | Get-AzResourceGroup -Name $resourceGroup -ErrorAction Stop | Out-Null 376 | Write-Host "[+] Resource Group already exists." 377 | 378 | } 379 | catch { 380 | Write-Host "[+] Creating a new resource group... " -NoNewline 381 | New-AzResourceGroup -Name $resourceGroup -Location $location | Out-Null 382 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 383 | } 384 | 385 | # Create the workspace 386 | try { 387 | $ws = Get-AzOperationalInsightsWorkspace -Name $workspaceName -ResourceGroupName $resourceGroup -ErrorAction Stop -WarningAction Ignore 388 | Write-Host "[+] Log Analytics workspace already exists." 389 | $workspaceId = $ws.CustomerId 390 | } 391 | catch { 392 | Write-Host "[+] Creating a new log analytics workspace... " -NoNewline 393 | $ws = New-AzOperationalInsightsWorkspace -Location $location -Name $workspaceName -Sku PerGB2018 -ResourceGroupName $resourceGroup -RetentionInDays 90 -WarningAction Ignore 394 | Start-Sleep 60 395 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 396 | $workspaceId = $ws.CustomerId 397 | } 398 | 399 | #Enable Sentinel 400 | $solution = "SecurityInsights" 401 | $packs = "" 402 | $packs = Get-AzOperationalInsightsIntelligencePack -ResourceGroupName $resourceGroup -WorkspaceName $workspaceName -WarningAction Ignore 403 | 404 | if (!(($packs | Where-Object { $_.Name -eq 'SecurityInsights' }).Enabled)) { 405 | Write-Host "[+] Enabling Microsoft Sentinel for the workspace... " -NoNewline 406 | Set-AzOperationalInsightsIntelligencePack -ResourceGroupName $resourceGroup -WorkspaceName $workspaceName -IntelligencePackName $solution -Enabled $true | Out-Null 407 | Start-Sleep 60 408 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 409 | } 410 | else { 411 | Write-Host "[+] Microsoft Sentinel is already deployed." 412 | } 413 | 414 | 415 | #Register a new app for REST API 416 | try { 417 | $app = Get-MgApplication -Filter "DisplayName eq '$appDisplayName' " -ErrorAction Stop 418 | if ($null -eq $app) { 419 | throw "No application found" 420 | } 421 | Write-Host "[+] Azure AD App Registration already exists. Recreating... " -NoNewline 422 | Remove-MgApplication -ApplicationId $app.Id | Out-Null 423 | 424 | $appRegistration = New-MgApplication -DisplayName $appDisplayName 425 | $applicationID = $appRegistration.Id 426 | Start-Sleep -Seconds 15 427 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 428 | 429 | Write-Host "[+] Creating a new app secret... " -NoNewline 430 | $appSecret = Add-MgApplicationPassword -ApplicationId $appRegistration.Id 431 | $appSecretValue = $appSecret.SecretText 432 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 433 | 434 | Write-Host "[+] Assigning app permissions... " -NoNewline 435 | $app = Get-MgApplication -ApplicationId $applicationID 436 | ## Get the existing permissions of the application 437 | $existingResourceAccess = $app.RequiredResourceAccess 438 | ## If the app has no existing permissions, or no existing permissions from our new permissions resource 439 | if ( ([string]::IsNullOrEmpty($existingResourceAccess) ) -or ($existingResourceAccess | Where-Object { $_.ResourceAppId -eq $graphResourceId } -eq $null) ) { 440 | $existingResourceAccess += $newResourceAccess 441 | Update-MgApplication -ApplicationId $applicationID -RequiredResourceAccess $existingResourceAccess | Out-Null 442 | } 443 | ## If the app already has existing permissions from our new permissions resource 444 | else { 445 | $newResourceAccess.ResourceAccess += $existingResourceAccess.ResourceAccess 446 | Update-MgApplication -ApplicationId $applicationId -RequiredResourceAccess $newResourceAccess | Out-Null 447 | } 448 | 449 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 450 | 451 | } 452 | catch { 453 | Write-Host "[+] Creating a new Azure AD App Registration... " -NoNewline 454 | $appRegistration = New-MgApplication -DisplayName $appDisplayName #-Oauth2AllowImplicitFlow $false -AvailableToOtherTenants $false 455 | $applicationID = $appRegistration.Id 456 | Start-Sleep -Seconds 15 457 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 458 | 459 | Write-Host "[+] Creating a new app secret... " -NoNewline 460 | $appSecret = Add-MgApplicationPassword -ApplicationId $appRegistration.Id 461 | $appSecretValue = $appSecret.SecretText 462 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 463 | 464 | 465 | #add permissions read 466 | Write-Host "[+] Assigning app permissions... " -NoNewline 467 | $app = Get-MgApplication -ApplicationId $applicationID 468 | ## Get the existing permissions of the application 469 | $existingResourceAccess = $app.RequiredResourceAccess 470 | 471 | ## If the app has no existing permissions, or no existing permissions from our new permissions resource 472 | if ( ([string]::IsNullOrEmpty($existingResourceAccess) ) -or ($existingResourceAccess | Where-Object { $_.ResourceAppId -eq $graphResourceId } -eq $null) ) { 473 | $existingResourceAccess += $newResourceAccess 474 | Update-MgApplication -ApplicationId $applicationID -RequiredResourceAccess $existingResourceAccess | Out-Null 475 | } 476 | ## If the app already has existing permissions from our new permissions resource 477 | else { 478 | $newResourceAccess.ResourceAccess += $existingResourceAccess.ResourceAccess 479 | Update-MgApplication -ApplicationId $applicationId -RequiredResourceAccess $newResourceAccess | Out-Null 480 | } 481 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 482 | } 483 | 484 | #create service principal for the app 485 | $aid = $app.AppId 486 | $sp = Get-MgServicePrincipal -Filter "AppId eq '$aid'" 487 | 488 | if ($null -eq $sp) { 489 | Write-Host "[+] Creating a new service principal for the app registration... " -NoNewline 490 | $sp = New-MgServicePrincipal -AppId $app.AppId 491 | Start-Sleep 30 492 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 493 | } 494 | else { 495 | Write-Host "[+] Service principal for the app registration already exists." 496 | } 497 | 498 | 499 | 500 | #Assign Contributor and Monitoring Metrics Publisher role for the resource group 501 | Write-Host "[+] Assigning Resource Group Contributor role... " -NoNewline 502 | #New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -AppRoleId $roleContributorId 503 | 504 | New-AzRoleAssignment -ObjectId $sp.Id -RoleDefinitionName Contributor -ResourceGroupName $resourceGroup -WarningAction Ignore | Out-Null 505 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 506 | 507 | Write-Host "[+] Assigning Monitoring Metrics Publisher role... " -NoNewline 508 | New-AzRoleAssignment -ObjectId $sp.Id -RoleDefinitionName "Monitoring Metrics Publisher" -ResourceGroupName $resourceGroup -WarningAction Ignore | Out-Null 509 | start-sleep 30 510 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 511 | 512 | 513 | 514 | Write-Host "[+] Gathering the bearer token... " -NoNewline 515 | #$scope = [System.Web.HttpUtility]::UrlEncode("https://monitor.azure.com//.default") 516 | $scope = [System.Web.HttpUtility]::UrlEncode("https://management.core.windows.net//.default") 517 | $body = "client_id=$aid&scope=$scope&client_secret=$appSecretValue&grant_type=client_credentials"; 518 | $headers = @{"Content-Type" = "application/x-www-form-urlencoded" }; 519 | $uriLogin = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 520 | $bearerToken = (Invoke-RestMethod -Uri $uriLogin -Method "Post" -Body $body -Headers $headers).access_token 521 | $headers = @{"Authorization" = "Bearer $bearerToken"; "Content-Type" = "application/json" }; 522 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 523 | 524 | # Creating/validating DCE 525 | $dcePayload = @{ 526 | location = $location 527 | properties = @{ 528 | networkAcls = @{ 529 | publicNetworkAccess = "Enabled" 530 | } 531 | } 532 | } 533 | $dcePayloadJson = $dcePayload | ConvertTo-Json 534 | 535 | try { 536 | $dceObject = Invoke-RestMethod -Uri $endpointDceUri -Method "Get" -Headers $headers; 537 | $dceLogIngestionEndpoint = $dceObject.properties.logsIngestion.endpoint 538 | if (($null -eq $dceLogIngestionEndpoint) -or ($dceLogIngestionEndpoint -eq "")) { 539 | # Create DataCollection Endpoint 540 | # PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Insights/dataCollectionEndpoints/{dataCollectionEndpointName}?api-version=2021-04-01 541 | Write-Host "[+] Creating a new DCE... " -NoNewline 542 | $dceCreateResponse = Invoke-RestMethod -Uri $endpointDceUri -Method "Put" -Body $dcePayloadJson -Headers $headers; 543 | $dceObject = Invoke-RestMethod -Uri $endpointDceUri -Method "Get" -Headers $headers; 544 | $dceLogIngestionEndpoint = $dceObject.properties.logsIngestion.endpoint 545 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 546 | } 547 | else { 548 | Write-Host "[+] DCE already exists." 549 | } 550 | } 551 | catch { 552 | 553 | # Create DataCollection Endpoint 554 | # PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Insights/dataCollectionEndpoints/{dataCollectionEndpointName}?api-version=2021-04-01 555 | Write-Host "[+] Creating a new DCE... " -NoNewline 556 | $dceCreateResponse = Invoke-RestMethod -Uri $endpointDceUri -Method "Put" -Body $dcePayloadJson -Headers $headers; 557 | $dceObject = Invoke-RestMethod -Uri $endpointDceUri -Method "Get" -Headers $headers; 558 | $dceLogIngestionEndpoint = $dceObject.properties.logsIngestion.endpoint 559 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 560 | } 561 | 562 | 563 | 564 | 565 | # Creating/validating DCR 566 | $dcrPayload = @{ 567 | location = $location 568 | properties = @{ 569 | dataCollectionEndpointId = "$dceId" 570 | streamDeclarations = @{ 571 | "Custom-WindowsEvent" = @{ 572 | columns = 573 | @( 574 | @{ 575 | name = "TimeGenerated" 576 | type = "datetime" 577 | }, 578 | @{ 579 | name = "RawEventData" 580 | type = "string" 581 | } 582 | ) 583 | 584 | } 585 | } 586 | dataSources = @{} 587 | destinations = @{ 588 | logAnalytics = @(@{ 589 | workspaceResourceId = "$workspaceResourceId" 590 | name = "$workspaceName" 591 | } 592 | ) 593 | } 594 | dataFlows = @(@{ 595 | streams = @("Custom-WindowsEvent") 596 | destinations = @("$workspaceName") 597 | transformkql = "source | extend EventData = parse_json(RawEventData) | extend Channel=tostring(EventData.Channel),Computer=tostring(EventData.Hostname),EventID=toint(EventData.EventID),EventLevel=toint(EventData.Level),Provider=tostring(EventData.SourceName),Task=toint(EventData.Task),Type='WindowsEvent'| project TimeGenerated,Channel,Computer,EventData,EventID,EventLevel,Provider,Task,Type" 598 | outputStream = "Microsoft-WindowsEvent" 599 | } 600 | ) 601 | } 602 | } 603 | 604 | $dcrPayloadJson = $dcrPayload | ConvertTo-Json -Depth 10 605 | 606 | try { 607 | $dcrObject = Invoke-RestMethod -Uri $dcrUri -Method "Get" -Headers $headers; 608 | $dcrImmutableId = $dcrObject.properties.immutableId 609 | $correctDcrWorkspace = $dcrObject.properties.destinations.logAnalytics | Where-Object { $_.workspaceId -eq $workspaceId } 610 | 611 | if (($null -eq $dcrImmutableId) -or ($dcrImmutableId -eq "")) { 612 | # Create a new DCR 613 | Write-Host "[+] Creating a new DCR... " -NoNewline 614 | $dcrCreateResponse = Invoke-RestMethod -Uri $dcrUri -Method "Put" -Body $dcrPayloadJson -Headers $headers; 615 | $dcrImmutableId = $dcrCreateResponse.properties.immutableId 616 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 617 | } 618 | elseif ($correctDcrWorkspace.name -ne $workspaceName) { 619 | Write-Host "[+] Workspace has changed. Recreating DCR... " -NoNewline 620 | $dcrCreateResponse = Invoke-RestMethod -Uri $dcrUri -Method "DELETE" -Headers $headers; 621 | Start-Sleep 15 622 | $dcrCreateResponse = Invoke-RestMethod -Uri $dcrUri -Method "Put" -Body $dcrPayloadJson -Headers $headers; 623 | $dcrImmutableId = $dcrCreateResponse.properties.immutableId 624 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 625 | } 626 | else { 627 | Write-Host "[+] DCR already exists." 628 | } 629 | } 630 | catch { 631 | # Create a new DCR 632 | Write-Host "[+] Creating a new DCR... " -NoNewline 633 | $dcrCreateResponse = Invoke-RestMethod -Uri $dcrUri -Method "Put" -Body $dcrPayloadJson -Headers $headers; 634 | $dcrImmutableId = $dcrCreateResponse.properties.immutableId 635 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 636 | } 637 | 638 | 639 | 640 | # replace 2 days from apt logs 641 | 642 | if (!(Test-Path $destination_file -PathType Leaf)) { 643 | Write-Host "[+] Replacing dates in the original file... " -NoNewline 644 | (Get-Content $original_file) | Foreach-Object { 645 | $_ -replace $day1date, ((Get-Date).AddDays(-3)).ToString("yyyy-MM-dd") ` 646 | -replace $day2date, ((Get-Date).AddDays(-2)).ToString("yyyy-MM-dd") 647 | } | Set-Content $destination_file 648 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 649 | } 650 | else { 651 | Write-Host "[+] The original log file has already been processed, and all dates were replaced." 652 | 653 | } 654 | 655 | 656 | Do { 657 | $Answer = Read-Host -Prompt 'Do you want to start APT29 dataset log uploading? Please note that it will take up to 3 hours. (y/n)' 658 | } 659 | Until ($Answer -eq 'y' -or $Answer -eq 'n') 660 | 661 | if ($Answer -eq 'y') { 662 | Send-AzMonitorCustomLogs -LogPath $destination_file -appId $app.AppId -appSecret $appSecretValue -TenantId $tenantId -DcrImmutableId $dcrImmutableId -DceURI $dceLogIngestionEndpoint -StreamName 'Custom-WindowsEvent' -TimestampField 'EventTime' -ShowProgressBar 663 | } 664 | 665 | 666 | Write-Host "[+] Gathering the bearer token for function creation... " -NoNewline 667 | #$scope = [System.Web.HttpUtility]::UrlEncode("https://monitor.azure.com//.default") 668 | $scope = [System.Web.HttpUtility]::UrlEncode("https://management.azure.com//.default") 669 | $body = "client_id=$aid&scope=$scope&client_secret=$appSecretValue&grant_type=client_credentials"; 670 | $headers = @{"Content-Type" = "application/x-www-form-urlencoded" }; 671 | $uriLogin = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 672 | $bearerToken = (Invoke-RestMethod -Uri $uriLogin -Method "Post" -Body $body -Headers $headers).access_token 673 | $headers = @{"Authorization" = "Bearer $bearerToken"; "Content-Type" = "application/json" }; 674 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 675 | 676 | # Creating/validating function 677 | $funcPayload = @{ 678 | properties = @{ 679 | category = "SentAdvHunting" 680 | displayName = "fWindowsEvent" 681 | version = 2 682 | functionAlias = "fWindowsEvent" 683 | query = "WindowsEvent | extend _timestamp_ = todatetime(EventData.['@timestamp']) | project-rename TimeIngested = TimeGenerated, TimeGenerated = _timestamp_" 684 | } 685 | } 686 | 687 | 688 | $funcPayloadJson = $funcPayload | ConvertTo-Json -Depth 10 689 | 690 | try { 691 | $functionObject = Invoke-RestMethod -Uri $functionUri -Method "Get" -Headers $headers; 692 | $functionId = $functionObject.id 693 | 694 | if (($null -eq $functionId) -or ($functionId -eq "")) { 695 | Write-Host "[+] Creating a new function... " -NoNewline 696 | $functionCreateResponse = Invoke-RestMethod -Uri $functionUri -Method "Put" -Body $funcPayloadJson -Headers $headers; 697 | $functionId = $functionCreateResponse.id 698 | 699 | if (($null -eq $functionId) -or ($functionId -eq "")) { 700 | Write-Host "[!] Error! Function wasn't created." -ForegroundColor White -BackgroundColor Red 701 | Exit 702 | } 703 | else { 704 | Start-Sleep 30 705 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 706 | } 707 | } 708 | else { 709 | Write-Host "[+] The function fWindowsEvent already exists." 710 | } 711 | } 712 | catch { 713 | # Create a new function 714 | Write-Host "[+] Creating a new function... " -NoNewline 715 | $functionCreateResponse = Invoke-RestMethod -Uri $functionUri -Method "Put" -Body $funcPayloadJson -Headers $headers; 716 | $functionId = $functionCreateResponse.id 717 | 718 | if (($null -eq $functionId) -or ($functionId -eq "")) { 719 | Write-Host "[!] Error! The function fWindowsEvent wasn't created." -ForegroundColor White -BackgroundColor Red 720 | Exit 721 | } 722 | else { 723 | Start-Sleep 30 724 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 725 | } 726 | } 727 | 728 | if ((Get-Date) -gt $tokenExpiryDate) 729 | { 730 | Do { 731 | $Answer = Read-Host -Prompt 'Your authentication token is expired. You have to re-authenticate to proceed. Are you ready to do it now? (y/n)' 732 | } 733 | Until ($Answer -eq 'y' -or $Answer -eq 'n') 734 | 735 | if ($Answer -eq 'y') { 736 | Connect-AzAccount -ErrorAction Stop | Out-Null 737 | } 738 | } 739 | 740 | 741 | #Sentinel's analytic rule creation 742 | $alertRule = "" 743 | $alertRule = Get-AzSentinelAlertRule -ResourceGroupName $resourceGroup -WorkspaceName $workspaceName | Where-Object { $_.DisplayName -eq "$alertRuleName" } 744 | 745 | if (($null -eq $alertRule) -or ($alertRule -eq "")) { 746 | Write-Host "[+] Creating Microsoft Sentinel analytics rule... " -NoNewline 747 | $alertRule = New-AzSentinelAlertRule -ResourceGroupName $resourceGroup -WorkspaceName $workspaceName -Scheduled -Enabled -DisplayName $alertRuleName -Severity High -Query "fWindowsEvent | where EventID == 4688 | extend NewProcessName = tostring(EventData.NewProcessName) | where NewProcessName contains ' ' " -QueryFrequency (New-TimeSpan -Hours 1) -QueryPeriod (New-TimeSpan -Days 10) -TriggerThreshold 0 -SuppressionEnabled -SuppressionDuration (New-TimeSpan -Hours 23) 748 | if (!($null -eq $alertRule)) 749 | { 750 | Write-Host "Completed" -ForegroundColor White -BackgroundColor Green 751 | } 752 | } 753 | else { 754 | Write-Host "[+] Microsoft Sentinel analytics rule is already created." 755 | } 756 | 757 | 758 | 759 | Pop-Location 760 | -------------------------------------------------------------------------------- /mapdrive.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | [Parameter (Mandatory=$false)] 4 | [object] $inputfscredobj, 5 | 6 | [string] $inputfsurl 7 | ) 8 | 9 | Remove-PSDrive -Name S -Force 10 | 11 | start-sleep -Seconds 60 12 | 13 | New-PSDrive -Name S -PSProvider FileSystem -Root "\\$($inputfsurl)\samplefileshare" -Credential $inputfscredobj -Persist -------------------------------------------------------------------------------- /remainprivateips.ps1: -------------------------------------------------------------------------------- 1 | #################################### Single Instance Subnet monitored 2 | 3 | $vnet = "XXXXXX" 4 | $subnet = "XXXXX" 5 | $vnetobj = Get-AzVirtualNetwork -Name $vnet 6 | 7 | #define the consumed private ips within the subnet 8 | $subnetobj = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnetobj -Name $subnet 9 | $consumedprivips = $subnetobj.IpConfigurations.Count 10 | 11 | # Define the subnet's cidr 12 | $cidr = $subnetobj.AddressPrefix -split "/" 13 | $cidr = $cidr[1] 14 | 15 | # Table for /cidr into private ips allowed 16 | $maxprivips = switch ($cidr) 17 | { 18 | 20 {4091} 19 | 21 {2043} 20 | 22 {1019} 21 | 23 {507} 22 | 24 {251} 23 | 25 {123} 24 | 26 {59} 25 | 27 {27} 26 | 28 {11} 27 | 29 {3} 28 | } 29 | 30 | $percent_s = ($consumedprivips/$maxprivips).tostring("P") 31 | $percent_i = ([Math]::Round(($consumedprivips/$maxprivips)*100 + 0.005, 2)) 32 | 33 | Write-Host $percent_s 34 | 35 | #################################### Subnets within VNETS in Subscription Report \ Could be used for conditional logic alerting or briefing report 36 | 37 | # Object table for VNET\Subnet remaining private ips 38 | $priviptable = @() 39 | 40 | #Obtain all VNETs out there in Subscription 41 | $VNETs = Get-AzVirtualNetwork 42 | 43 | foreach ($VNET in $VNETs){ 44 | 45 | $vnetobj = Get-AzVirtualNetwork -Name $VNET.Name 46 | $subnets = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnetobj 47 | foreach ($subnet in $subnets){ 48 | 49 | $subnetobj = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnetobj -Name $subnet.Name 50 | $consumedprivips = $subnetobj.IpConfigurations.Count 51 | 52 | # Define the subnet's cidr 53 | $cidr = $subnetobj.AddressPrefix -split "/" 54 | $cidr = $cidr[1] 55 | 56 | # Table for /cidr into private ips allowed 57 | $maxprivips = switch ($cidr) 58 | { 59 | 20 {4091} 60 | 21 {2043} 61 | 22 {1019} 62 | 23 {507} 63 | 24 {251} 64 | 25 {123} 65 | 26 {59} 66 | 27 {27} 67 | 28 {11} 68 | 29 {3} 69 | } 70 | 71 | # Basic Math for precent consumed 72 | 73 | $percent_s = ($consumedprivips/$maxprivips).tostring("P") 74 | $percent_i = ([Math]::Round(($consumedprivips/$maxprivips)*100 + 0.005, 2)) 75 | 76 | #Define PS Object Entry 77 | $Object = New-Object psobject -Property @{ 78 | VNET = $VNET.Name 79 | Subnet = $subnet.Name 80 | SubnetUsed = $consumedprivips 81 | SubnetTotal = $maxprivips 82 | SubnetConsumed = $percent_s 83 | } 84 | #Entry into PSTable 85 | $priviptable += $Object 86 | 87 | } 88 | 89 | } 90 | 91 | $priviptable -------------------------------------------------------------------------------- /rmd4sql.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules Az.Accounts, Az.ConnectedMachine 2 | 3 | <# 4 | .SYNOPSIS 5 | Checks all Azure Arc-connected machines in a specified subscription and resource group for a specific extension and removes it if found. 6 | 7 | .DESCRIPTION 8 | This script sets the Azure context to a specified subscription, retrieves all Azure Arc-connected machines in a given resource group, 9 | and for each machine, checks if the specified extension (default: "MicrosoftDefenderForSQL") exists. If the extension is present, 10 | it initiates its removal asynchronously. 11 | 12 | .PARAMETER subscriptionId 13 | The ID of the Azure subscription to target. This parameter is mandatory. 14 | 15 | .PARAMETER resourceGroupName 16 | The name of the resource group containing the Azure Arc-connected machines. This parameter is mandatory. 17 | 18 | .PARAMETER extensionName 19 | The name of the extension to check and remove. Defaults to "MicrosoftDefenderForSQL". 20 | 21 | .EXAMPLE 22 | .\Remove-ArcExtension.ps1 -subscriptionId "your-subscription-id" -resourceGroupName "your-resource-group" 23 | Runs the script with the default extension name "MicrosoftDefenderForSQL". 24 | 25 | .EXAMPLE 26 | .\Remove-ArcExtension.ps1 -subscriptionId "your-subscription-id" -resourceGroupName "your-resource-group" -extensionName "CustomExtension" 27 | Runs the script targeting a custom extension named "CustomExtension". 28 | #> 29 | 30 | param ( 31 | [Parameter(Mandatory = $true)] 32 | [string]$subscriptionId, 33 | 34 | [Parameter(Mandatory = $true)] 35 | [string]$resourceGroupName, 36 | 37 | [string]$extensionName = "MicrosoftDefenderForSQL" 38 | ) 39 | 40 | # Record the start time for logging 41 | $startTime = Get-Date 42 | Write-Output "Script started at $startTime" 43 | 44 | # Set the Azure context to the specified subscription 45 | try { 46 | Set-AzContext -SubscriptionId $subscriptionId -ErrorAction Stop | Out-Null 47 | Write-Output "Successfully set Azure context to subscription: $subscriptionId" 48 | } catch { 49 | Write-Error "Failed to set Azure context: $_" 50 | exit 1 51 | } 52 | 53 | # Retrieve all Azure Arc-connected machines in the specified resource group 54 | try { 55 | $machines = Get-AzConnectedMachine -ResourceGroupName $resourceGroupName 56 | } catch { 57 | Write-Error "Failed to retrieve Azure Arc-connected machines: $_" 58 | exit 1 59 | } 60 | 61 | # Check if any machines were found 62 | if ($machines.Count -eq 0) { 63 | Write-Output "No Azure Arc-connected machines found in resource group $resourceGroupName." 64 | exit 0 65 | } 66 | 67 | # Iterate through each Azure Arc-connected machine 68 | foreach ($machine in $machines) { 69 | $machineName = $machine.Name 70 | Write-Output "Processing machine: $machineName" 71 | 72 | # Verify if the extension exists on the Arc-enabled server 73 | $extension = Get-AzConnectedMachineExtension -ResourceGroupName $resourceGroupName -MachineName $machineName -Name $extensionName -ErrorAction SilentlyContinue 74 | 75 | if ($null -eq $extension) { 76 | Write-Output "Extension $extensionName not found on machine $machineName in resource group $resourceGroupName." 77 | } else { 78 | # Uninstall the extension 79 | Write-Output "Uninstalling extension $extensionName from machine $machineName..." 80 | try { 81 | Remove-AzConnectedMachineExtension -ResourceGroupName $resourceGroupName -MachineName $machineName -Name $extensionName -ErrorAction Stop -NoWait 82 | Write-Output "Extension $extensionName uninstallation initiated successfully for machine $machineName." 83 | } catch { 84 | Write-Error "Failed to initiate uninstallation for machine ${machineName}: $_" 85 | } 86 | } 87 | } 88 | 89 | # Record the end time and calculate duration 90 | $endTime = Get-Date 91 | $duration = $endTime - $startTime 92 | Write-Output "Script completed at $endTime. Duration: ${duration.TotalSeconds} seconds" -------------------------------------------------------------------------------- /updaterulesforplaybook.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | A script to update all the scheduled sentinel rules to use a specific logic app | useful for a notification or ticket scenario | Use prior to Automation Rules 4 | .NOTES 5 | AUTHOR: Nathan Swift 6 | LASTEDIT: June 29, 2021 7 | FUTURES: 8 | PREREQS: https://www.powershellgallery.com/packages/Az.SecurityInsights/1.0.0 9 | #> 10 | 11 | #variables for logic app and azure sentinel workspace 12 | $logicappname = "" 13 | $logicapprgname = "" 14 | $sentinelrgname = "" 15 | $sentinelworkspacename = "" 16 | 17 | # load LogicApp playbook Object infromation needed to set on Sentinel Rules 18 | $LogicAppResourceId = Get-AzLogicApp -ResourceGroupName $logicapprgname -Name $logicappname 19 | $LogicAppTriggerUri = Get-AzLogicAppTriggerCallbackUrl -ResourceGroupName $logicapprgname -Name $logicappname -TriggerName "When_a_response_to_an_Azure_Sentinel_alert_is_triggered" 20 | 21 | # filtering on only scheduled KQL alaert types 22 | $AlertRules = Get-AzSentinelAlertRule -ResourceGroupName $sentinelrgname -WorkspaceName $sentinelworkspacename | Where-Object {$_.Kind -eq "Scheduled"} 23 | 24 | # loop through the alert rules for each alert rule of type scheduled 25 | foreach($AlertRule in $AlertRules) { 26 | 27 | Write-Host "Attempting to Update Rule: " $AlertRule.DisplayName 28 | 29 | # Add a new playbook autopmation to the alert rule firing 30 | $AlertRuleAction = New-AzSentinelAlertRuleAction -ResourceGroupName $sentinelrgname -WorkspaceName $sentinelworkspacename -AlertRuleId $AlertRule.name -LogicAppResourceId ($LogicAppResourceId.Id) -TriggerUri ($LogicAppTriggerUri.Value) 31 | 32 | ## could use similar logic here to update existing playbook with another one: https://docs.microsoft.com/en-us/powershell/module/az.securityinsights/update-azsentinelalertruleaction?view=azps-6.0.0 33 | 34 | Write-Host "Rule: " $AlertRule.DisplayName " LogicAppPlaybook Added as Action: " $AlertRuleAction.LogicAppResourceId 35 | 36 | 37 | } -------------------------------------------------------------------------------- /updatexml.ps1: -------------------------------------------------------------------------------- 1 | # $ASRPIP = '10.11.12.13' 2 | 3 | #Obtain Azure MetaData Instance 4 | $ASRPIP = Invoke-RestMethod -Method GET -Uri http://169.254.169.254/metadata/instance?api-version=2017-04-02 -Headers @{"Metadata"="True"} 5 | 6 | #Opbtain Public IP Address assigned 7 | $ASRPIP = $ASRPIP.network.interface.ipv4.ipaddress.publicipaddress 8 | 9 | #Create a backup file 10 | Copy-Item -Path "C:\Program Files (x86)\FileZilla Server\fileZilla Server.xml" -Destination "C:\Program Files (x86)\FileZilla Server\FileZilla Server.xml.bkp" -Force 11 | 12 | #Restore from backup 13 | ## Copy-Item -Path "C:\Program Files (x86)\FileZilla Server\fileZilla Server.xml.bkp" -Destination "C:\Program Files (x86)\FileZilla Server\FileZilla Server.xml" -Force 14 | 15 | # path of XML config for FileZilla Server 16 | $path = 'C:\Program Files (x86)\FileZilla Server\fileZilla Server.xml' 17 | 18 | # Obtain XML config as a object 19 | [xml]$xml = (Get-Content $path) 20 | 21 | # Specific public ip setting is on line 12 22 | ## $xml.FileZillaServer.Settings.Item[12].InnerText 23 | 24 | # Create a variable to replace the public ip address from the parameter passed into script 25 | $node = $xml.FileZillaServer.Settings.Item[12] 26 | 27 | # replace 28 | $node.'#text' = $ASRPIP 29 | 30 | # Save update changes 31 | $xml.Save($path) 32 | 33 | # Restart Service 34 | Restart-Service "FileZilla Server" --------------------------------------------------------------------------------