Powershell: Get AzureRM automation schedule recurrence info - azure

I'm doing some inventory trying to gather all my start/stop VM schedules from Azure.
I'm strugling with extracting the days selected for weekly recurrence schedules.
I can extract all the data from single schedules with:
Select-AzureRmSubscription <name>
$schedule = Get-AzureRmAutomationSchedule -AutomationAccountName <name)-ResourceGroupName <name> -Name <name>
And then get all the days:
$schedule.WeeklyScheduleOptions.DaysOfWeek -join ","
Which outputs: Monday,Tuesday,Wednesday,Thursday,Friday
But if I loop through all my subscriptions and build a psobject
with all schedule data this data comes up empty:
$AzSubs = Get-AzureRmSubscription
$objs = #()
foreach ($AzSub in $AzSubs){
Get-AzureRmSubscription -SubscriptionName $AzSub.Name | Select-AzureRmSubscription
$azAutAccs = Get-AzureRmAutomationAccount
foreach ($azAutAcc in $azAutAccs){
$AzAutScheds = Get-AzureRmAutomationSchedule -AutomationAccountName $azAutAcc.AutomationAccountName -ResourceGroupName $azAutAcc.ResourceGroupName
$AzAutScheds = $AzAutScheds | where{$_.IsEnabled -eq "True"}
foreach ($AzAutSched in $AzAutScheds){
$DOW = $azAutSched.WeeklyScheduleOptions.DaysOfWeek -join "," | out-string
$DOM = $azAutSched.MonthlyScheduleOptions.DaysOfMonth -join "," | out-string
$obj = new-object psobject -Property #{
SchedName = $AzAutSched.Name
LastModifiedTime = (get-date ([DateTime]::Parse($AzAutSched.LastModifiedTime)) -Format "dd-MM-yyyy HH:mm (zzz)")
IsEnabled = $AzAutSched.IsEnabled
AutomationAccount = $azAutAcc.AutomationAccountName
ResourceGroup = $azAutAcc.ResourceGroupName
NextRun = ([DateTime]::Parse($azAutSched.NextRun))
StartTime = (get-date ([DateTime]::Parse($azAutSched.StartTime)) -Format "HH:mm (zzz)")
TimeZone = $azAutSched.TimeZone
Interval = $azAutSched.Interval
Frequency = $azAutSched.Frequency
WeekSchedule = $DOW
MonthSchedule = $DOM
}
$objs += $obj
}
}
}
$objs | sort SchedName | ft -Property SchedName,LastModifiedTime,StartTime,TimeZone,Interval,Frequency,WeekSchedule,MonthSchedule
Then my table ends up with just blank columns for WeekSchedule/MonthSchedule.
I have tried different combos of leaving out the out-string parameter, leaving out the join, setting the property directly in the property line, and as quoted building the variable above the object and referencing it on the property line. None of them work.
Anyone can shed some light as to what I am missing? Or other hints on how to accomplish this are most welcome.
AzureRM module is up to date.

According to my test you need to get individual schedule, not all the schedules in the resource group, it will work in this case:
foreach ($azAutAcc in $azAutAccs){
$AzAutScheds = Get-AzAutomationSchedule -AutomationAccountName $azAutAcc.AutomationAccountName -ResourceGroupName $azAutAcc.ResourceGroupName
$AzAutScheds = $AzAutScheds | Where-Object {$_.IsEnabled -eq "True"}
foreach ($AzAutSched in $AzAutScheds){
$AzAutSched = Get-AzAutomationSchedule -AutomationAccountName $azAutAcc.AutomationAccountName -ResourceGroupName $azAutAcc.ResourceGroupName -Name $AzAutSched.Name
$DOW = $azAutSched.WeeklyScheduleOptions.DaysOfWeek -join "," | out-string
$DOM = $azAutSched.MonthlyScheduleOptions.DaysOfMonth -join "," | out-string
$objs += new-object psobject -Property #{
SchedName = $AzAutSched.Name
LastModifiedTime = (get-date ([DateTime]::Parse($AzAutSched.LastModifiedTime)) -Format "dd-MM-yyyy HH:mm (zzz)")
IsEnabled = $AzAutSched.IsEnabled
AutomationAccount = $azAutAcc.AutomationAccountName
ResourceGroup = $azAutAcc.ResourceGroupName
NextRun = ([DateTime]::Parse($azAutSched.NextRun))
StartTime = (get-date ([DateTime]::Parse($azAutSched.StartTime)) -Format "HH:mm (zzz)")
TimeZone = $azAutSched.TimeZone
Interval = $azAutSched.Interval
Frequency = $azAutSched.Frequency
WeekSchedule = $DOW
MonthSchedule = $DOM
}
}
}

Related

How to split different values in powershell by a line

With this script i am able to fetch all the Tags that a VM has but i want that in output the each key and its value should be separated by a line in the way that each key and its value appears on different lines like this
reference image
# Sign into Azure Portal
connect-azaccount
# Fetch the Virtual Machines from the subscription
$azureVMDetails = get-azvm
# Fetch the NIC details from the subscription
$azureNICDetails = Get-AzNetworkInterface | ?{ $_.VirtualMachine -NE $null}
#Fetching Virtual Machine Details
$virtual_machine_object = $null
$virtual_machine_object = #()
#Iterating over the NIC Interfaces under the subscription
foreach($azureNICDetail in $azureNICDetails){
#Fetching the VM Name
$azureVMDetail = $azureVMDetails | ? -Property Id -eq $azureNICDetail.VirtualMachine.id
#Fetching the VM Tags
foreach($azureDetail in $azureVMDetails) {
$vm_tags = $azureVMDetail| Select-Object -Property (
#{name='Tags'; expression = {($_.tags.GetEnumerator().ForEach({ '{0} : {1}' -f $_.key, $_.value }) -join ';')}}
)
}
#VM Details export
$virtual_machine_object_temp = new-object PSObject
$virtual_machine_object_temp | add-member -membertype NoteProperty -name "name" -Value $azureVMDetail.Name
$virtual_machine_object_temp | add-member -membertype NoteProperty -name "comments" -Value ($vm_tags.Tags -join ';')
$virtual_machine_object += $virtual_machine_object_temp
}
#Report format and path
$virtual_machine_object | Export-Csv "C:\Users\JOHN\Desktop\Inventory\Final Scripts\VM_details_$(get-date -f dd.MM.yyyy).csv" -NoTypeInformation -Force
I tried to reproduce the same in my environment and got the results successfully by using the below PowerShell script:
$vmdeatil = Get-AzVm -Name testvm | Select -ExpandProperty Tags
$value = $vmdeatil
foreach($i in 0..($value.Count -1))
{
$ErrorActionPreference = ‘SilentlyContinue’
[array]$report += [pscustomobject] #{
key = $key[$i]
name = $value[$i]
}
}
$report | Export-Csv -Path "C:\Users\ruk1.csv" -NoTypeInformation
Response:
The output is successfully exported in the csv file like below:

Powershell export CSV looks weird

I have an issue with my CSV export to Excel with powershell. When I import it looks like pretty bad and I can't find any information that helps me to solve it.
Here I attach an image of the import and the code. I see other CSV imports and it looks normal with its categories spaced by rows in Excel, but I don't know how to do it.
Image of my workbook
$Computers = Get-ADComputer -Filter {OperatingSystem -like "*Server*"} -Properties OperatingSystem | Select-Object -ExpandProperty Name
Foreach($computer in $computers){
if(!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "cannot reach $computer offline" -f red}
else {
$outtbl = #()
Try{
$sr=Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
$Xr=Get-WmiObject –class Win32_processor -ComputerName $computer -ErrorAction Stop
$ld=get-adcomputer $computer -properties Name,Lastlogondate,operatingsystem,ipv4Address,enabled,description,DistinguishedName -ErrorAction Stop
$r="{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $computer |Measure-Object Capacity -Sum).Sum / 1GB)
$x = gwmi win32_computersystem -ComputerName $computer |select #{Name = "Type";Expression = {if (($_.pcsystemtype -eq '2') )
{'Laptop'} Else {'Desktop Or Other something else'}}},Manufacturer,#{Name = "Model";Expression = {if (($_.model -eq "$null") ) {'Virtual'} Else {$_.model}}},username -ErrorAction Stop
$t= New-Object PSObject -Property #{
serialnumber = $sr.serialnumber
computername = $ld.name
Ipaddress=$ld.ipv4Address
Enabled=$ld.Enabled
Description=$ld.description
Ou=$ld.DistinguishedName.split(',')[1].split('=')[1]
Type = $x.type
Manufacturer=$x.Manufacturer
Model=$x.Model
Ram=$R
ProcessorName=($xr.name | Out-String).Trim()
NumberOfCores=($xr.NumberOfCores | Out-String).Trim()
NumberOfLogicalProcessors=($xr.NumberOfLogicalProcessors | Out-String).Trim()
Addresswidth=($xr.Addresswidth | Out-String).Trim()
Operatingsystem=$ld.operatingsystem
Lastlogondate=$ld.lastlogondate
LoggedinUser=$x.username
}
$outtbl += $t
}
catch [Exception]
{
"Error communicating with $computer, skipping to next"
}
$outtbl | select Computername,enabled,description,ipAddress,Ou,Type,Serialnumber,Manufacturer,Model,Ram,ProcessorName,NumberOfCores,NumberOfLogicalProcessors,Addresswidth,Operatingsystem,loggedinuser,Lastlogondate |export-csv -Append C:\temp\VerynewAdinventory.csv -nti
}
}
As commented, your locale computer uses a different delimiter character that Export-Csv by default uses (that is the comma).
You can check what character your computer (and thus your Excel) uses like this:
[cultureinfo]::CurrentCulture.TextInfo.ListSeparator
To use Export-Csv in a way that you can simply double-click the output csv file to open in Excel, you need to either append switch -UseCulture to it, OR tell it what the delimiter should be if not a comma by appending parameter -Delimiter followed by the character you got from the above code line.
That said, your code does not produce the full table, because the export to the csv file is in the wrong place. As Palle Due commented, you could have seen that if you would indent your code properly.
Also, I would advise to use more self-describing variable names, so not $r or $x, but $memory and $machine for instance.
Nowadays, you should use Get-CimInstance rather than Get-WmiObject
AND adding to an array with += should be avoided as it is both time and memory consuming. (on every addition to an array, which is of fixed size, the entire array has to be rebuilt in memory).
Your code revised:
# set the $ErrorActionPreference to Stop, so you don't have to add -ErrorAction Stop everywhere in the script
# remember the currens value, so you can restore that afterwards.
$oldErrorPref = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# get an array of computers, gathering all properties you need
$computers = Get-ADComputer -Filter "OperatingSystem -like '*Server*'" -Properties OperatingSystem, LastLogonDate, IPv4Address, Description
$result = foreach ($computer in $computers) {
$serverName = $computer.Name
if(!(Test-Connection -ComputerName $serverName -BufferSize 16 -Count 1 -ErrorAction SilentlyContinue -Quiet)) {
Write-Host "cannot reach $serverName offline" -ForegroundColor Red
continue # skip this computer and proceed with the next one
}
try {
# instead of Get-WmiObject, nowadays you should use Get-CimInstance
$bios = Get-WmiObject -Class Win32_bios -ComputerName $serverName
$processor = Get-WmiObject -Class Win32_Processor -ComputerName $serverName
$memory = Get-WmiObject -Class Win32_PhysicalMemory -ComputerName $serverName
$disks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $serverName
$machine = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $serverName |
Select-Object #{Name = "Type"; Expression = {
if ($_.pcsystemtype -eq '2') {'Laptop'} else {'Desktop Or Other something else'}}
},
Manufacturer,
#{Name = "Model"; Expression = {
if (!$_.model) {'Virtual'} else {$_.model}}
},
UserName
# output an object to be collected in variable $result
# put the properties in the order you would like in the output
[PsCustomObject] #{
ComputerName = $serverName
Enabled = $computer.Enabled
Description = $computer.description
IpAddress = $computer.IPv4Address
Ou = $computer.DistinguishedName.split(',')[1].split('=')[1]
Type = $machine.type
SerialNumber = $bios.serialnumber
Manufacturer = $machine.Manufacturer
Model = $machine.Model
Ram = '{0} GB' -f (($memory | Measure-Object Capacity -Sum).Sum / 1GB)
ProcessorName = $processor.Name
NumberOfCores = $processor.NumberOfCores
NumberOfLogicalProcessors = $processor.NumberOfLogicalProcessors
Addresswidth = $processor.Addresswidth
OperatingSystem = $computer.OperatingSystem
# {0:N2} returns the number formatted with two decimals
TotalFreeDiskSpace = '{0:N2} GB' -f (($disks | Measure-Object FreeSpace -Sum).Sum / 1GB)
LoggedInUser = $machine.UserName
Lastlogondate = $computer.LastLogonDate
}
}
catch {
Write-Warning "Error communicating with computer $serverName, skipping to next"
}
}
# restore the ErrorActionPreference to its former value
$ErrorActionPreference = $oldErrorPref
# output the completed array in a CSV file
# (using the delimiter characer your local machine has set as ListSeparator)
$result | Export-Csv -Path 'C:\temp\VerynewAdinventory.csv' -UseCulture -NoTypeInformation

Programatically Get ADF pipeline consumption report

I'm interested in querying the pipeline consumption report that is available from the Data Factory monitor. Is there a table on Log Analytics or PowerShell cmdlet that would return this information? I checked the ADFv2 PowerShell module but couldn't find any. My goal is to aggregate the information available in this report to identify what are the most costly pipelines.
reference: https://techcommunity.microsoft.com/t5/azure-data-factory/new-adf-pipeline-consumption-report/ba-p/1394671
Thank you
Doing more research someone pointed me to a GitHub page where the product team posted a PowerShell script to find part of what I was looking for {1}. So I did some modifications to the script to have the output that I needed. With the output below I can extract the values from the MS calculator to get an estimated cost for each pipeline run. {2}
$startTime = "21/6/2021 7:00:00"
$endTime = "21/6/2021 10:00:00"
$adf = '<data factory name>'
$rg = '<resrouce group name>'
$outputObj = #()
$pipelineRuns = Get-AzDataFactoryV2PipelineRun -ResourceGroupName $rg -DataFactoryName $adf -LastUpdatedAfter $startTime -LastUpdatedBefore $endTime
# loop through all pipelines and child activities to return billable information
foreach ($pipelineRun in $pipelineRuns) {
$activtiyRuns = Get-AzDataFactoryV2ActivityRun -ResourceGroupName $rg -DataFactoryName $adf -pipelineRunId $pipelineRun.RunId -RunStartedAfter $startTime -RunStartedBefore $endTime
foreach ($activtiyRun in $activtiyRuns) {
if ($null -ne $activtiyRun.Output -and $null -ne $activtiyRun.Output.SelectToken("billingReference.billableDuration")) {
$obj = #()
$obj = $activtiyRun.Output.SelectToken("billingReference.billableDuration").ToString() | ConvertFrom-Json
$obj | Add-Member -MemberType NoteProperty -Name activityType -value $activtiyRun.Output.SelectToken("billingReference.activityType").ToString()
$obj | Add-Member -MemberType NoteProperty -Name pipelineName -value $pipelineRun.PipelineName
$obj | Add-Member -MemberType NoteProperty -Name activtiyRuns -value $activtiyRuns.Count
$outputObj += $obj
}
else {}
}
}
# output aggregated result set as table
$groupedObj = $outputObj | Group-Object -Property pipelineName, activityType, meterType
$groupedObj | ForEach-Object {
$value = $_.name -split ', '
New-Object psobject -Property #{
activityType = $value[1];
meterType = $value[2];
pipelineName = $value[0];
executionHours = [math]::Round(($_.Group | Measure-object -Property duration -sum).Sum, 4)
orchestrationActivityRuns = $groupedObj.group.activtiyRuns[0]
}
} | Sort-Object -Property meterType | Format-Table
Output sample:
Consumption report from the Data Factory monitor
reference:
https://github.com/Azure/Azure-DataFactory/tree/main/SamplesV2/PastRunDetails#simple-script-that-prints--activity-level-run-details-in-45-day-range {1}
https://azure.microsoft.com/en-us/pricing/calculator/?service=data-factory%2F {2}

Receiving has literal was incomplete for below query

$subscriptions = Get-AzSubscription
$result = foreach ($vsub in $subscriptions){
Select-AzSubscription $vsub.SubscriptionID
Write-Host
Write-Host "Working on $($vsub.Name)"
Write-Host
foreach($VM in (Get-AzVM)){
# $Tier = (Get-AzResource -ResourceId $webapp.ServerFarmId).Sku.Tier
# $Plan = Get-AzAppServicePlan -ResourceGroupName $webapp.ResourceGroup
# output the object so it gets collected in $result
[PSCustomObject]#{
TenantId = $vsub.TenantId
SubscriptionName = $vsub.Name
VMName = $VM.Name
ResourceGroup = $VM.ResourceGroup
# Hostname = $webapp.DefaultHostName
#PricingTier = $Tier
#SKU = #($Plan.Sku.Size) -join ','
#AppServiceName = #($Plan.Name) -join ','
Status = $VM.PowerState
Location = $VM.Location
Size = $VM.HardwareProfile.VmSize
Application_Name= $VM.Tags.Application_Name
Application_Owner= $VM.Tags.Application_Owner
Business_Owner = $VM.Tags.Business_Owner
Cost_Code = $VM.Tags.Cost_Code
Created_Date = $VM.Tags.Created_Date
Environment_Name = $VM.Tags.Environment_Name
ENVIRONMENT_NAME = $VM.Tags.ENVIRONMENT_NAME
#AppType = $webapp.Kind
#SubscriptionID = $vsub.SubscriptionID
}
}
}
# sort unique and export the file
$result | Sort-Object * -Unique | Export-Csv -Path "C:\Users\Desktop\Scripts\vm_inventory.csv" -NoTypeInformation
I am trying to run this query to get the details of the VM but I am receiving the hash literal is incomplete for PSCustomObject, as per my knowledge all the brackets are proper but don't know why I am receiving the error. Request to please help me on the same.

Get expiring Azure AD applications

I am trying to get all the Azure AD Application secrets and certificates that will expire in the next 30 days. I'm using Get-AzADApplication piped to Get-AzADAppCredential to get the applications EndDate but it is not returning the correct results as it doesnt match the dates correctly even if I format them both exactly the same. The code below returns some apps that expire in 2025!
$todaysDate = (Get-Date -UFormat "%e/%m/%Y")
$expiryDate = Get-Date $(Get-Date).AddDays(30) -UFormat "%e/%m/%Y"
$aboutToExpire = Get-AzADApplication | ForEach-Object {
$app = $_
#(
Get-AzADAppCredential -ObjectId $_.ObjectId -ErrorAction SilentlyContinue
) | Where-Object { (Get-Date $_.EndDate -UFormat "%e/%m/%Y") -le $expiryDate -and (Get-Date $_.EndDate -UFormat "%e/%m/%Y") -gt $todaysDate} | ForEach-Object {
[PSCustomObject] #{
AppName = $app.DisplayName
ObjectID = $app.ObjectId
AppId = $app.ApplicationId
StartDate = $_.StartDate
EndDate = $_.EndDate
ExpiryDate = $expiryDate
}
}
}
$aboutToExpire
Here is what I'm using for searching expired secrets and certs. I believe you have an issue with the date comparison because of the not correct date format, please take a look at my example.
$apps = Get-AzADApplication
$xs = Get-Date
$ys = Get-Date (Get-Date).AddDays(+60)
$alertListExps = #()
$alertListExpd = #()
foreach ($app in $apps)
{
$secrets = Get-AzADAppCredential -ObjectId $app.ObjectId
if ($null -eq $secrets){}
else
{
foreach ($secret in $secrets)
{
$secretDate = [datetime]$secret.EndDate #::parseexact($secret.EndDate,'dd/MM/yyyy HH:mm:ss',$null)
if ($secretDate -le $xs)
{
$alertListExpd += "*App:* " + $app.DisplayName + " *exired:* " + $secret.EndDate + ' ' + '(' + $secret.Type + ')' | Out-String
}
elseif ($secretDate -le $ys)
{
$alertListExps += "*App:* " + $app.DisplayName + " *exires:* " + $secret.EndDate + ' ' + '(' + $secret.Type + ')' | Out-String
}
}
}
It was a date issue. The first issue was trying to parse the date. Thanks to #oleh-tarasenko for the parseexact code. The second issue was with the comparison operator trying to compare en-AU dates with en-US dates and either failing or outputting bad results. Trick was to provide the specific culture. Code below.
$CIDE = New-Object System.Globalization.CultureInfo("en-US")
$endDate = [DateTime]::parseexact($secret.EndDate, "d/MM/yyyy h:mm:ss tt", $CIDE)

Resources