Get expiring Azure AD applications - azure

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)

Related

Caluculating expiration of Azure secrets

What my script does is get a list of Azure app registrations and then calculate how many days left until the secret expires. It works fine in the main, except that if an app registration has 2 secrets it falls over. Having 2 secretes isn't common, but we introduced a 2nd secret on one app registration just for a short period of testing.
After obtaining the list of app registrations (there are 10 in total), my script then goes through each of them to obtain the expiration date date and then calculate the number of days left. Below is a snippet ...
foreach ($app in $applications) {
$Appname = $app.displayName
$AppID = $app.Id
$ApplID = $app.AppId
$AppCreds = Get-AzADAppCredential -ObjectId $AppID | select StartDateTime, EndDateTime, Hint
$today = get-date
$StartDate = $AppCreds.StartDateTime
$EndDate = $AppCreds.EndDateTime
$operation = $EndDate - $today
$ODays = $operation.Days
# Check how many days are remaining for secret expiration
if ($ODays -le $Days -and $ODays -ge 0)
Once obtained it sends an e-mail if a secret is going to expire within 60 days.
When the script hits the app registration with two secrets it fails with ...
"Problem occurred: Method invocation failed because [System.Object[]] does not contain a method named 'op_Subtraction'."
Any ideas why this is happening?
The $AppCreds is array in case if you have more than app secret. Here is fully re-worked script to build the report:
function Get-AzADAppCredentialExpiration(){
$retArray = #()
$applications = Get-AzADApplication
$today = get-date
foreach($app in $applications){
$AppCreds = #(Get-AzADAppCredential -ObjectId $app.Id)
$AppCreds | %{
$retArray += [PSCustomObject]#{
AppName = $app.DisplayName;
ClientSecretId = $_.KeyId
SecretHint = $_.Hint
DaysLeft = ($_.EndDateTime - $today).Days
}
}
}
return $retArray
}
$report = Get-AzADAppCredentialExpiration
$report | ? {$_.DaysLeft -le 30 -and $_.DaysLeft -gt 0} | Group-Object -Property AppName | %{
Write-Host "Key for application $($_.Name) will be expired soon:" -ForegroundColor Yellow
$_.Group | %{
Write-Host "`t$($_.SecretHint) ($($_.ClientSecretId))" -ForegroundColor Yellow
}
}
$report | ? {$_.DaysLeft -le 0} | Group-Object -Property AppName | %{
Write-Host "Key for application $($_.Name) are expired:" -ForegroundColor Red
$_.Group | %{
Write-Host "`t$($_.SecretHint) ($($_.ClientSecretId))" -ForegroundColor Red
}
}
Old answer
The $AppCreds is array in case if you have more than app secret. So you should check if it array and then calculate accordingly:
foreach ($app in $applications) {
$Appname = $app.displayName
$AppID = $app.Id
$ApplID = $app.AppId
$AppCreds = Get-AzADAppCredential -ObjectId $AppID | select StartDateTime, EndDateTime, Hint
$today = get-date
if($AppCreds -is [Array]){
$AppCreds | %{
$StartDate = $_.StartDateTime
$EndDate = $_.EndDateTime
$operation = $EndDate - $today
#....
}
}
else{
$StartDate = $AppCreds.StartDateTime
$EndDate = $AppCreds.EndDateTime
$operation = $EndDate - $today
}

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.

PowerShell: Working with Strings and Hashtables

I have this code:
$passwordsParameters = "sysPassword = 12 &&& testPass = 13 &&& systemPassword = 10"
$parametersList = #($passwordsParameters -split '&&&')
$passwordsTable = #{}
ForEach ($parameter in $parametersList) {
$splitToKeyValue = #($parameter -split '=')
$passwordsTable += $passwordsTable = #{
$splitToKeyValue[0].trim() = $splitToKeyValue[1].trim()
}
}
ForEach ($pass in $passwordsTable.Keys) {
if ($passwordsTable[$pass] -ne "") {
Write-Host "set $pass ="$passwordsTable[$pass]""
} else {
Write-Host "A value for the parameter $pass was not entered."
}
}
# Add-Content "d:\myFile.txt" "set $pass ="$passwordsTable[$pass]""
Which perfectly works when I use Write-Host. But I want to do something like in the comment in line 25. I tried several ways but I always got a static string instead of the values that I get from the Hashtable.
At the end I want to have something like:
set pass1 = 12
set pass2 = 5
in myFile.txt
Any help is appreciated. Thanks!
You could change Write-Host (just prints to a console) to Write-Output ( which passes an object to a pipeline). Write-Output does not print to the console.
$passwordsParameters = "sysPassword = 12 &&& testPass = 13 &&& systemPassword = 10"
$parametersList = #($passwordsParameters -split '&&&')
$passwordsTable = #{}
ForEach ($parameter in $parametersList) {
$splitToKeyValue = #($parameter -split '=')
$passwordsTable += $passwordsTable = #{
$splitToKeyValue[0].trim() = $splitToKeyValue[1].trim()
}
}
$counter=0
ForEach ($pass in $passwordsTable.Keys) {
if ($passwordsTable[$pass] -ne "") {
$counter++
Write-Output "set "pass$counter = $passwordsTable[$pass]"`n" | Add-Content -NoNewline myFile.txt
} else {
Write-Host "A value for the parameter $pass was not entered."
}
}
Output:
set pass1=10
set pass2=13
set pass3=12
You can replace the first foreach loop if you simply replace all the &&& by a newline and use cmdlet ConvertFrom-StringData.
Add-Content also has a switch called -PassThru that will let you write to the file and also output to console.
$passwordsParameters = "sysPassword = 12 &&& testPass = 13 &&& systemPassword = 10"
$passwordsTable = $passwordsParameters -replace '&&&', [Environment]::NewLine | ConvertFrom-StringData
foreach ($pass in $passwordsTable.Keys) {
if ($passwordsTable[$pass]) {
$msg = 'set {0} = {1}' -f $pass, $passwordsTable[$pass]
# or use: $msg = "set $pass = $($passwordsTable[$pass])"
# write this to the file. switch -PassThru will also output to the console
Add-Content -Path 'D:\myFile.txt' -Value $msg -PassThru
} else {
Write-Host "A value for the parameter '$pass' was not entered."
}
}

Powershell: Get AzureRM automation schedule recurrence info

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
}
}
}

Loop through and replace backslash in filename (string)

I am trying to replace a \ contained in the filenames I have queried from the database. My script below loops through a CSV containing project codes. For each project code the database is queried and the project name is retrieved.
However, the projectname contains \ which I am trying to replace.
$startRow = 2
$col = 3
$excel = New-Object -COM Excel.Application
$wb = $excel.Workbooks.Open("\myprojectfolder\projectcodes.csv")
$excel.Visible = $false
for ($i = 1; $i -le $wb.Sheets.Count; $i++){
$sh = $wb.Sheets.Item($i)
$endRow = $sh.UsedRange.Rows.Count
$rangeAddress = $sh.Cells.Item($startRow,$col).Address() + ":" +
$sh.Cells.Item($endRow,$col).Address()
$sh.Range($rangeAddress).Value2 | foreach {
#GET PROJECT NAME TO APPEND TO FOLDER NAME
$projectCode = $_
$Server= "MYSERVER"
$Database = "MYDATABASE"
$SQLQuery = $("SELECT [description] FROM [dbo].[projects] WHERE [project] = '$projectCode'")
$Connection = New-Object System.Data.SQLClient.SQLConnection
$Connection.ConnectionString = "server='$Server';database='$Database';trusted_connection=true;"
$Connection.Open()
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection
$Command.CommandText = $SQLQuery
$Reader = $Command.ExecuteReader()
while ($Reader.Read()) {
$projectName = $Reader.GetValue($1)
#CHECK AND REPLACE '\' CHARACTER IN PROJECTNAME
if ($projectName -like '*\\*') {
Write-Debug "PROJECT NAME CONTAINS \"
$projectName.Replace('\\', '_')
}
$folderPath = "\\myfolder\"
$pathTogether = $folderPath + $projectCode + "_" + $projectName + "\"
New-Item -Path $pathTogether -Type Directory -force
#CHECK IF FILE EXISTS IN APPROPRIATE DIRECTORY
$testFile = $pathTogether + $projectCode + "_" + $projectName + ".xlsm"
$fileExist = Test-Path $testFile
if ($fileExist -eq $false) {
$templateFile = $folderPath + "my_template\my_template.xlsm"
Copy-Item $templateFile $pathTogether
$newPath = $pathTogether + "\my_template.xlsm"
$saveFile = $projectCode + "_" + $projectName + ".xlsm"
$renameToOLD = $projectCode + "_" + $projectName + "_RENAMED" + ".xlsm"
#RENAME PROJECT FILE TO HAVE OLD IN FILENAME
Rename-Item $newPath $saveFile
$projectxlFile = New-Object -COM Excel.Application
$projectxlFile.workbooks.open($pathTogether + "\" + $saveFile)
$queryWS = $projectxlFile.worksheets.Item("Query")
$queryWS.Cells.Item(8,2) = $projectCode
$projectxlFile.DisplayAlerts = $False
$projectxlFile.Visible = $False
$savePath = $pathTogether + $saveFile
#Add-Type -AssemblyName Microsoft.Office.Interop.Excel
#$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbookMacroEnabled
$projectxlFile.ActiveWorkbook.Save()
$projectxlFile.Workbooks.Close()
$projectxlFile.Quit()
$ProcID = Get-Process |
Where-Object {$_.MainWindowHandle -eq $projectxlFile.HWND} |
Select -ExpandProperty ID
Get-Process -Id $ProcID | Stop-Process -Force
##[System.Runtime.Interopservices.Marshal]::ReleaseComObject($projectxlFile)
}
}
$Connection.Close()
}
}
$excel.Workbooks.Close()
$excel.Quit()
$ProcID2 = Get-Process |
Where-Object {$_.MainWindowHandle -eq $excel.HWND} |
Select -ExpandProperty ID
Get-Process -Id $ProcID2 | Stop-Process -Force
###[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
$projectName.Replace('\\', '_')
By default the Replace() method does regular string replacements, so the above would only replace double backslashes with an underscore. Also, it doesn't replace in-place, so you need to assign the modified string back to the variable:
$projectName = $projectName.Replace('\', '_')
The -like operator does wildcard matches, so you mustn't escape the backslash in that expression either, otherwise you don't even get to the replacement operation:
if ($projectName -like '*\*') {
Write-Debug 'PROJECT NAME CONTAINS \'
$projectName = $projectName.Replace('\', '_')
}

Resources