Alerting when my Apple certificates expire in Intune using Azure Automation

One of the things I’m always aware of is certificate expiry, especially the ones used for Apple VPP, if those expire and aren’t renewed in time, you may find yourself factory resetting and re-enrolling again which I’m sure we all want to avoid.

Whilst Outlook reminders are a wonderful thing, there is still an element of user error, plus if you renew early, the expiry is now earlier next year so it’s a bit too manual for my liking.

It’s time to automate!

For this script, I’m going to take the excellent work by Peter van der Woude here and extend it slightly to use an Azure Automation Runbook and email me when a certificate is due to expire.

Let’s start with the Automation Account within Azure

Once that’s complete we want to deploy the required modules to it

In PowerShell Gallery select this module and deploy to Azure Automation:

https://www.powershellgallery.com/packages/Microsoft.Graph.Users.Actions/1.9.6

Once deployed, we can reference it in our script

Next up, we need an AAD App Registration we can use to grab the backups

In Azure AD, create a new app registration:

Within API Permissions grant these roles under MS Graph:

  • DeviceManagementApps.Read.All
  • DeviceManagementConfiguration.Read.All
  • DeviceManagementServiceConfig.Real.All
  • Mail.Send

You’ll note it’s still missing permissions so you now need to click the Grant Admin Consent button

Finally create a Secret and save that, the App ID and the Tenant details to use in the script

Now, back to the Azure Automation Account and click Runbook

Create a new Runbook

Enter the Powershell code below (or grab from here) (update the fields as required for your environment)

<#PSScriptInfo
.VERSION 1.0.3
.GUID 1000d8c2-73b3-48a8-b1ec-f894fec7df58
.AUTHOR AndrewTaylor
.DESCRIPTION Alerts when a certificate is due to expire
.COMPANYNAME
.COPYRIGHT GPL
.TAGS intune endpoint MEM environment
.LICENSEURI https://github.com/andrew-s-taylor/public/blob/main/LICENSE
.PROJECTURI https://github.com/andrew-s-taylor/public
.ICONURI
.EXTERNALMODULEDEPENDENCIES microsoft.graph.intune, microsoft.graph.users.actions
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
.PRIVATEDATA
#>
<# 

.DESCRIPTION 
Alerts on expiry of Apple Certificates with AAD App Registration, Azure Blob and Azure Automation Account

#> 


##############################################################################################################################################
##### UPDATE THESE VALUES #################################################################################################################
##############################################################################################################################################
## Your Azure Tenant Name
$tenant = "<YOUR TENANT NAME>"

##Your Azure Tenant ID
$tenantid = "<YOUR TENANT ID>"

##Your App Registration Details
$clientId = "<YOUR CLIENT ID>"
$clientSecret = "<YOUR CLIENT SECRET>"

$EmailAddress = "<YOUR EMAIL ADDRESS>"

##From Address
$MailSender = "<YOUR FROM ADDRESS>"


##############################################################################################################################################

Function Get-ScriptVersion(){
    
  <#
  .SYNOPSIS
  This function is used to check if the running script is the latest version
  .DESCRIPTION
  This function checks GitHub and compares the 'live' version with the one running
  .EXAMPLE
  Get-ScriptVersion
  Returns a warning and URL if outdated
  .NOTES
  NAME: Get-ScriptVersion
  #>
  
  [cmdletbinding()]
  
  param
  (
      $liveuri
  )
$contentheaderraw = (Invoke-WebRequest -Uri $liveuri -Method Get)
$contentheader = $contentheaderraw.Content.Split([Environment]::NewLine)
$liveversion = (($contentheader | Select-String 'Version:') -replace '[^0-9.]','') | Select-Object -First 1
$currentversion = ((Get-Content -Path $PSCommandPath | Select-String -Pattern "Version: *") -replace '[^0-9.]','') | Select-Object -First 1
if ($liveversion -ne $currentversion) {
write-host "Script has been updated, please download the latest version from $liveuri" -ForegroundColor Red
}
}



Function Connect-ToGraph {
  <#
.SYNOPSIS
Authenticates to the Graph API via the Microsoft.Graph.Authentication module.

.DESCRIPTION
The Connect-ToGraph cmdlet is a wrapper cmdlet that helps authenticate to the Intune Graph API using the Microsoft.Graph.Authentication module. It leverages an Azure AD app ID and app secret for authentication or user-based auth.

.PARAMETER Tenant
Specifies the tenant (e.g. contoso.onmicrosoft.com) to which to authenticate.

.PARAMETER AppId
Specifies the Azure AD app ID (GUID) for the application that will be used to authenticate.

.PARAMETER AppSecret
Specifies the Azure AD app secret corresponding to the app ID that will be used to authenticate.

.PARAMETER Scopes
Specifies the user scopes for interactive authentication.

.EXAMPLE
Connect-ToGraph -TenantId $tenantID -AppId $app -AppSecret $secret

-#>
  [cmdletbinding()]
  param
  (
      [Parameter(Mandatory = $false)] [string]$Tenant,
      [Parameter(Mandatory = $false)] [string]$AppId,
      [Parameter(Mandatory = $false)] [string]$AppSecret,
      [Parameter(Mandatory = $false)] [string]$scopes
  )

  Process {
      Import-Module Microsoft.Graph.Authentication
      $version = (get-module microsoft.graph.authentication | Select-Object -expandproperty Version).major

      if ($AppId -ne "") {
          $body = @{
              grant_type    = "client_credentials";
              client_id     = $AppId;
              client_secret = $AppSecret;
              scope         = "https://graph.microsoft.com/.default";
          }
   
          $response = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$Tenant/oauth2/v2.0/token -Body $body -UseBasicParsing
          $accessToken = $response.access_token
   
          $accessToken
          if ($version -eq 2) {
              write-host "Version 2 module detected"
              $accesstokenfinal = ConvertTo-SecureString -String $accessToken -AsPlainText -Force
          }
          else {
              write-host "Version 1 Module Detected"
              Select-MgProfile -Name Beta
              $accesstokenfinal = $accessToken
          }
          $graph = Connect-MgGraph  -AccessToken $accesstokenfinal 
          Write-Host "Connected to Intune tenant $TenantId using app-based authentication (Azure AD authentication not supported)"
      }
      else {
          if ($version -eq 2) {
              write-host "Version 2 module detected"
          }
          else {
              write-host "Version 1 Module Detected"
              Select-MgProfile -Name Beta
          } -UseBasicParsing
          $graph = Connect-MgGraph -scopes $scopes
          Write-Host "Connected to Intune tenant $($graph.TenantId)"
      }
  }
}    


#Get Creds and connect
#Connect to Graph
write-host "Connecting to Graph"
write-host $body

Connect-ToGraph -Tenant $tenant -AppId $clientId -AppSecret $clientSecret
write-host "Graph Connection Established"

#MDM Push
$30days = ((get-date).AddDays(30)).ToString("yyyy-MM-dd")
$pushuri = "https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate"
$pushcert = Invoke-MgGraphRequest -Uri $pushuri -Method Get -OutputType PSObject
$pushexpiryplaintext = $pushcert.expirationDateTime
$pushexpiry = ($pushcert.expirationDateTime).ToString("yyyy-MM-dd")
if ($pushexpiry -lt $30days) {
write-host "Cert Expiring" -ForegroundColor Red

#Send Mail    
$URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
$BodyJsonsend = @"
                    {
                        "message": {
                          "subject": "Apple Push Certificate Expiry",
                          "body": {
                            "contentType": "HTML",
                            "content": "Your Apple Push Certificate is due to expire on <br>
                            $pushexpiryplaintext <br>
                            Please Renew before this date
                            "
                          },
                          "toRecipients": [
                            {
                              "emailAddress": {
                                "address": "$EmailAddress"
                              }
                            }
                          ]
                        },
                        "saveToSentItems": "false"
                      }
"@

Invoke-MgGraphRequest -Method POST -Uri $URLsend -Body $BodyJsonsend -ContentType "application/json"

}
else {
write-host "All fine" -ForegroundColor Green
}


#VPP
$30days = ((get-date).AddDays(30)).ToString("yyyy-MM-dd")
$vppuri = "https://graph.microsoft.com/beta/deviceAppManagement/vppTokens"
$vppcert = Invoke-MgGraphRequest -Uri $vppuri -Method Get -OutputType PSObject
$vppexpiryvalue = $vppcert.value
$vppexpiryplaintext = $vppexpiryvalue.expirationDateTime
$vppexpiry = ($vppexpiryvalue.expirationDateTime).ToString("yyyy-MM-dd")
if ($vppexpiry -lt $30days) {
write-host "Cert Expiring" -ForegroundColor Red
#Send Mail    
$URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
$BodyJsonsend = @"
                    {
                        "message": {
                          "subject": "Apple VPP Certificate Expiry",
                          "body": {
                            "contentType": "HTML",
                            "content": "Your Apple VPP Certificate is due to expire on <br>
                            $vppexpiryplaintext <br>
                            Please Renew before this date
                            "
                          },
                          "toRecipients": [
                            {
                              "emailAddress": {
                                "address": "$EmailAddress"
                              }
                            }
                          ]
                        },
                        "saveToSentItems": "false"
                      }
"@

Invoke-MgGraphRequest -Method POST -Uri $URLsend -Body $BodyJsonsend -ContentType "application/json"
}
else {
write-host "All fine" -ForegroundColor Green
}






#DEP
$30days = ((get-date).AddDays(30)).ToString("yyyy-MM-dd")
$depuri = "https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings"
$depcert = Invoke-MgGraphRequest -Uri $depuri -Method Get -OutputType PSObject
$depexpiryvalue = $depcert.value
$depexpiryplaintext = $depexpiryvalue.tokenexpirationDateTime

$depexpiry = ($depexpiryvalue.tokenExpirationDateTime).ToString("yyyy-MM-dd")
if ($depexpiry -lt $30days) {
write-host "Cert Expiring" -ForegroundColor Red

#Send Mail    
$URLsend = "https://graph.microsoft.com/v1.0/users/$MailSender/sendMail"
$BodyJsonsend = @"
                    {
                        "message": {
                          "subject": "Apple DEP Certificate Expiry",
                          "body": {
                            "contentType": "HTML",
                            "content": "Your Apple DEP Certificate is due to expire on <br>
                            $depexpiryplaintext <br>
                            Please Renew before this date
                            "
                          },
                          "toRecipients": [
                            {
                              "emailAddress": {
                                "address": "$EmailAddress"
                              }
                            }
                          ]
                        },
                        "saveToSentItems": "false"
                      }
"@

Invoke-MgGraphRequest -Method POST -Uri $URLsend -Body $BodyJsonsend
}
else {
write-host "All fine" -ForegroundColor Green
}

Click Test Pane to make sure it’s worked

Now we need to publish it once it’s tested ok

Now add a schedule to run it daily

That’s it, you should now be annoyed by daily emails until you renew the certificates!

51 thoughts on “Alerting when my Apple certificates expire in Intune using Azure Automation”

  1. Hi Andrew

    do you have a good idea regarding this client secret expiring and perhaps also certificates? so this don’t stop working?

    Reply
  2. Hey Andrew,
    After updating the Scrip I am getting the following error.
    The remote server returned an error: (400) Bad Request.

    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘String’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘AccessToken’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.The remote server returned an error: (400) Bad Request.

    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘String’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘AccessToken’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.

    Reply
  3. Getting Following Error while running the Script.

    Completed

    The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer’s first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.
    S…
    Authentication needed. Please call Connect-MgGraph.

    Reply
  4. I just Ignore the errors and wait untill the other items will expire. the MDM mails are working. so already happy with that part.
    thank you for the script and the support.

    Reply
  5. @odata.context : https://graph.microsoft.com/beta/$metadata#deviceManagement/applePushNotificationCerti
    ficate/$entity
    id : c0f832b7-16d8-4f4f-9040-71d4d699fbb4
    appleIdentifier : intunemdm.ios@****
    topicIdentifier : com.apple.mgmt.External.1efbbce6-ce33-4fe2-a58b-38b17dc19f12
    lastModifiedDateTime : 2/22/2023 12:35:34 PM
    expirationDateTime : 2/22/2024 12:25:18 PM
    certificateUploadStatus :
    certificateUploadFailureReason :
    certificateSerialNumber : 7DF6267A5687788D
    certificate :

    Reply
  6. Permissions of my user that was signed in with powershell was the problem
    Fixed that part

    PS C:\Windows\system32> $pushuri = “https://graph.microsoft.com/beta/deviceManagement/applePushNotificationCertificate”
    PS C:\Windows\system32> $pushcert = Invoke-MgGraphRequest -Uri $pushuri -Method Get -OutputType PSObject
    PS C:\Windows\system32> $pushexpiryplaintext = $pushcert.expirationDateTime
    PS C:\Windows\system32> $vppuri = “https://graph.microsoft.com/beta/deviceAppManagement/vppTokens”
    PS C:\Windows\system32> $vppcert = Invoke-MgGraphRequest -Uri $vppuri -Method Get -OutputType PSObject
    PS C:\Windows\system32> $vppexpiryvalue = $vppcert.value
    PS C:\Windows\system32> $depuri = “https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings”
    PS C:\Windows\system32> $depcert = Invoke-MgGraphRequest -Uri $depuri -Method Get -OutputType PSObject
    PS C:\Windows\system32> $depexpiryvalue = $depcert.value
    PS C:\Windows\system32>

    I dont get any data

    the test with runbook still gives this error:

    eyJ0eXAiOiJKV1QiLCJub25jZSI6InF2cThibG5LeUdYYTJXSWVRYzZMd193OTBYMk1tRVNwVEJlN0NUNmhEVEUiLCJhbGciOiJSUzI1NiIsIng1dCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyIsImtpZCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8wNWI3NTM0NC1hNTVmLTQ3YzctODhiYy1mYjE1MzdhMzg0NTQvIiwiaWF0IjoxNjk1MDQzMjc5LCJuYmYiOjE2OTUwNDMyNzksImV4cCI6MTY5NTA0NzE3OSwiYWlvIjoiRTJGZ1lMaDd4cGlIMjdwbDI2bGZaa2RGdkI2ZkJ3QT0iLCJhcHBfZGlzcGxheW5hbWUiOiJBcHBsZUFsZXJ0cyIsImFwcGlkIjoiYTUxODdjYjEtODQ0NC00OTE1LTk1ZDQtNzlmYzljZTE0ZDY3IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvMDViNzUzNDQtYTU1Zi00N2M3LTg4YmMtZmIxNTM3YTM4NDU0LyIsImlkdHlwIjoiYXBwIiwib2lkIjoiMmZjODMzMjMtNGQ0My00NmNlLWI5OGItM2ZkMmRkMWRkOGNkIiwicmgiOiIwLkFYTUFSRk8zQlYtbHgwZUl2UHNWTjZPRVZBTUFBQUFBQUFBQXdBQUFBQUFBQUFCekFBQS4iLCJyb2xlcyI6WyJEZXZpY2VNYW5hZ2VtZW50U2VydmljZUNvbmZpZy5SZWFkLkFsbCIsIkRldmljZU1hbmFnZW1lbnRDb25maWd1cmF0aW9uLlJlYWQuQWxsIiwiTWFpbC5TZW5kIiwiRGV2aWNlTWFuYWdlbWVudEFwcHMuUmVhZC5BbGwiXSwic3ViIjoiMmZjODMzMjMtNGQ0My00NmNlLWI5OGItM2ZkMmRkMWRkOGNkIiwidGVuYW50X3JlZ2lvbl9zY29wZSI6IkVVIiwidGlkIjoiMDViNzUzNDQtYTU1Zi00N2M3LTg4YmMtZmIxNTM3YTM4NDU0IiwidXRpIjoiUmtJam02ZDlIa2lFTVdNUVpDVjFBQSIsInZlciI6IjEuMCIsIndpZHMiOlsiMDk5N2ExZDAtMGQxZC00YWNiLWI0MDgtZDVjYTczMTIxZTkwIl0sInhtc190Y2R0IjoxNTAyMzczODE2LCJ4bXNfdGRiciI6IkVVIn0.mLqxys2gxsqo5REbIuClNfQmdAc_FqfhZpYNE53UBK09m7Zy3MTUpr4aKR9ijKGerONlB5A3GPth8NNTMvSL786rDLqjyE5jw__L5VhBB0hmZkKFVFhmn39iu7jYYEh8i_eQpJWWuCeHTpmyJkDvQRsLVwL9JJlGy2KA40dXL_SuG7bIhWEHSgeTxLLphqw8QhdbFS94rYN9qh34C4CaP9eYVRzTxAsa4YPFVzLNxtCCAHz8aV0nbTl89ri4ilC2q5icV8dUvd79u5WMiNGH8izb8ssONsHp9LNdBjujSG5QOEpzMtK938nAJZY4AuuQCpHDMzVfD4BPDKKDEguHBA
    System.Management.Automation.MethodException: Cannot find an overload for “ToString” and the argument count: “1”.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    But a email is send : Your Apple DEP Certificate is due to expire on
    11/07/2023 12:42:42 11/07/2023 12:45:38 11/07/2023 12:44:36 11/07/2023 12:43:57 11/07/2023 12:45:10 11/07/2023 12:46:26
    Please Renew before this date

    so maybe I can ignore the error

    Reply
  7. Invoke-MgGraphRequest : GET https://graph.microsoft.com/beta/deviceManagement/depOnboardingSettings
    HTTP/1.1 403 Forbidden
    Transfer-Encoding: chunked
    Vary: Accept-Encoding
    Strict-Transport-Security: max-age=31536000
    request-id: 9f681a68-7a90-4c9e-b162-b151cf623ad3
    client-request-id: bc66b15d-7b6a-4f3f-9edc-d9f5bb241c80
    x-ms-ags-diagnostic: {“ServerInfo”:{“DataCenter”:”West
    Europe”,”Slice”:”E”,”Ring”:”5″,”ScaleUnit”:”003″,”RoleInstance”:”AM1PEPF00029182″}}
    Date: Mon, 18 Sep 2023 11:06:23 GMT
    Content-Encoding: gzip
    Content-Type: application/json
    {“error”:{“code”:”Forbidden”,”message”:”{\r\n \”_version\”: 3,\r\n \”Message\”: \”Application is not authorized to
    perform this operation. Application must have one of the following scopes:
    DeviceManagementServiceConfiguration.Read.All, DeviceManagementServiceConfig.Read.All,
    DeviceManagementServiceConfiguration.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All – Operation ID (for
    customer support): 00000000-0000-0000-0000-000000000000 – Activity ID: bc66b15d-7b6a-4f3f-9edc-d9f5bb241c80 – Url: http
    s://fef.msub06.manage.microsoft.com/DeviceEnrollmentFE_2309/StatelessDeviceEnrollmentFEService/deviceManagement/depOnbo
    ardingSettings?api-version=5023-06-28\”,\r\n \”CustomApiErrorPhrase\”: \”\”,\r\n \”RetryAfter\”: null,\r\n
    \”ErrorSourceService\”: \”\”,\r\n \”HttpHeaders\”: \”{}\”\r\n}”,”innerError”:{“date”:”2023-09-18T11:06:23″,”request-id
    “:”9f681a68-7a90-4c9e-b162-b151cf623ad3″,”client-request-id”:”bc66b15d-7b6a-4f3f-9edc-d9f5bb241c80″}}}
    At line:1 char:12
    + $depcert = Invoke-MgGraphRequest -Uri $depuri -Method Get -OutputType …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Method: GET, Re…d9f5bb241c80
    }:HttpRequestMessage) [Invoke-MgGraphRequest], HttpResponseException
    + FullyQualifiedErrorId : InvokeGraphHttpResponseException,Microsoft.Graph.PowerShell.Authentication.Cmdlets.Invok
    eMgGraphRequest

    Same for all 3

    Reply
  8. Hi Andrew,

    I’m using version 1.0.4 and getting this error:

    System.Management.Automation.MethodException: Cannot find an overload for “ToString” and the argument count: “1”.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Any idea what i’m doing wrong?

    Reply
    • It sounds like one of the variables isn’t being grabbed from Graph.

      If you try an interactive connect-mggraph and then run lines 134-136, what do you get in $pushexpiryplaintext?

      Then do the same for 176-179 and 221-224

      Reply
  9. Hopefully my last dumb question, now it is running and scheduled but the emails are not publishing the expiration date. Will that only show up if it is within 30 days?

    Example:
    Your Apple Push Certificate is due to expire on

    Please Renew before this date

    Reply
  10. ok re-did all permissions and made sure they are app permissions, saved. re-tested:

    Run Settings
    Run on Azure
    Using a hybrid runbook worker can increase test performance. Learn more
    Activity-level tracing
    This configuration is available only for graphical runbooks.
    Trace level
    None
    Basic
    Detailed
    Completed
    The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer’s first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘Path’ because it is an empty string.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘Message’ because it is null.
    at System.Management.Automation.ParameterBinderController.BindPositionalParametersInSet(UInt32 validParameterSets, Dictionary`2 nextPositionalParameters, CommandParameterInternal argument, ParameterBindingFlags flags, ParameterBindingException& bindingException)
    at System.Management.Automation.ParameterBinderController.BindPositionalParameters(Collection`1 unboundArguments, UInt32 validParameterSets, UInt32 defaultParameterSet, ParameterBindingException& outgoingBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘UseBasicParsing’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Reply
  11. Yup, I even went through those again last night to double-check:
    API/Permission Name –> Type –> Description –> Admin Consent Required –> Status
    DeviceManagementApps.Read.All –> Delegated –> Read Microsoft Intune apps –> Yes
    –> Granted
    DeviceManagementApps.Read.All –> Application –> Read Microsoft Intune apps –> Yes
    –> Granted
    DeviceManagementConfiguration.Read.All –> Delegated –> Read Microsoft Intune Device Configuration and Policies –> Yes –> Granted
    DeviceManagementServiceConfig.Read.All –> Delegated –> Read Microsoft Intune configuration –> Yes –> Granted
    Mail.Send –> Delegated –> Send mail as a user –> No –> Granted
    User.Read –> Delegated –> Sign in and read user Profile –> No Granted

    Reply
  12. Hello there, I do have an app reg configured. It’s configured in the code but should the client Id be the name of the client secret or the application Id of the app reg?

    Reply
    • Client ID is the Application Client ID on the App Reg overview screen
      Client Secret is the Secret Value (not the secret ID)

      Check you app permissions as well, forbidden means something isn’t authorized

      Reply
  13. hello,
    was really excited for this but every time i try to test the runbook it fails:

    InteractiveBrowserCredential authentication failed: Unable to load DLL ‘IEFRAME.dll’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)
    The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer’s first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘Path’ because it is an empty string.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘Message’ because it is null.
    at System.Management.Automation.ParameterBinderController.BindPositionalParametersInSet(UInt32 validParameterSets, Dictionary`2 nextPositionalParameters, CommandParameterInternal argument, ParameterBindingFlags flags, ParameterBindingException& bindingException)
    at System.Management.Automation.ParameterBinderController.BindPositionalParameters(Collection`1 unboundArguments, UInt32 validParameterSets, UInt32 defaultParameterSet, ParameterBindingException& outgoingBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    The remote server returned an error: (400) Bad Request.

    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘String’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter ‘AccessToken’ because it is null.
    at System.Management.Automation.ParameterBinderBase.ValidateNullOrEmptyArgument(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, Type argumentType, Object parameterValue, Boolean recurseIntoCollections)
    at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
    at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.
    Authentication needed. Please call Connect-MgGraph.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘ForegroundColor’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
    at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    — End of stack trace from previous location where exception was thrown —
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
    at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
    at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
    Authentication needed. Please call Connect-MgGraph.

    any ideas? i tried the updated code from github

    Reply
  14. It works

    i did however need to change line 238 from:
    Invoke-RestMethod -Method POST -Uri $URLsend -Headers $headers -Body $BodyJsonsend
    to:
    Invoke-MgGraphRequest -Method POST -Uri $URLsend -Body $BodyJsonsend

    Reply
  15. perhaps i havent explained it good enough, my edit was in the Blog version of the script: version 1.0.0

    on the github version 1.0.1i haven’t edited anything other than the “box” UPDATE THESE VALUES

    when i run the github version i recive the error and mails come through, but without the dates wehre the certificate needs to be updated, also, the email is being sent even if there are not more than 30 days until it expire

    Reply
  16. still the same, mails come in but with no date

    eyJ0eXAiOiJKV1QiLCJub25jZSI6IkJjUGxsUWpnWnhWSXpkSlp6dloybEdKbUhxMmRORTNoUEZnRm1TczVqMHMiLCJhbGciOiJSUzI1NiIsIng1dCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyIsImtpZCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC85MjljMzQ1OS1jNDM1LTQxYTMtOGMyYi03MDhlYmJhZmQ5ZjgvIiwiaWF0IjoxNjkzMjk5NTkzLCJuYmYiOjE2OTMyOTk1OTMsImV4cCI6MTY5MzMwMzQ5MywiYWlvIjoiRTJGZ1lGakRQVlZKN3FIb2xwTEdCWXkzM2w3NUNRQT0iLCJhcHBfZGlzcGxheW5hbWUiOiJJbnR1bmUtQXBwbGVDZXJ0LUFsZXJ0cyIsImFwcGlkIjoiMjYzMTBhMDYtOGJmZC00Yjg0LTk5ODItNjdiODk3OTY3ZmZiIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTI5YzM0NTktYzQzNS00MWEzLThjMmItNzA4ZWJiYWZkOWY4LyIsImlkdHlwIjoiYXBwIiwib2lkIjoiMTdlZTAxNjktMzgyNC00MmIyLThlMGMtOTUzYThjZjc3ZDc3IiwicmgiOiIwLkFTRUFXVFNja2pYRW8wR01LM0NPdTZfWi1BTUFBQUFBQUFBQXdBQUFBQUFBQUFDR0FBQS4iLCJyb2xlcyI6WyJEZXZpY2VNYW5hZ2VtZW50TWFuYWdlZERldmljZXMuUmVhZC5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50U2VydmljZUNvbmZpZy5SZWFkLkFsbCIsIkRldmljZU1hbmFnZW1lbnRTZXJ2aWNlQ29uZmlnLlJlYWRXcml0ZS5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50Q29uZmlndXJhdGlvbi5SZWFkLkFsbCIsIk1haWwuU2VuZCIsIkRldmljZU1hbmFnZW1lbnRDb25maWd1cmF0aW9uLlJlYWRXcml0ZS5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50QXBwcy5SZWFkLkFsbCJdLCJzdWIiOiIxN2VlMDE2OS0zODI0LTQyYjItOGUwYy05NTNhOGNmNzdkNzciLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiRVUiLCJ0aWQiOiI5MjljMzQ1OS1jNDM1LTQxYTMtOGMyYi03MDhlYmJhZmQ5ZjgiLCJ1dGkiOiJNbktSbUowbk1rZUdab0lac1lFLUFBIiwidmVyIjoiMS4wIiwid2lkcyI6WyIwOTk3YTFkMC0wZDFkLTRhY2ItYjQwOC1kNWNhNzMxMjFlOTAiXSwieG1zX3RjZHQiOjE0NzMxNDg4NzUsInhtc190ZGJyIjoiRVUifQ.bjK4dUkBVMNbCT2Gfq7fhKozJvzXfIwK-U4GGWoZ8kQtYSyEhlnULWPD7IxNWDQ6DJGXs4rY9f-S3TDeiOvzptNpI0jgh87rB98Ji9T39u6OfeMMfH35srjy7RXYgcfeqqRbsmWkvtzAZsS-QhPyEsxtTr5FTWlKl0Xr0QUvCj2FxhMSd_XznOUq3-OQeDe56jCOhzbh36RYYNEVtCb-MHD1anca3RIh9V2ZBQv3qwd9alPUd1tr7_1lQkebFsX_LKJfsVxhKDWB4WD-lK_vaeGlTswAC37m6x6auvRj0-wpf7dQw31hY2zUW6w7hL53xC6giQgZerbtJOFPZoOlmg
    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Reply
  17. i have tried a few things so have added a few more permissions
    DeviceManagementApps.Read.All
    Application
    DeviceManagementConfiguration.Read.All
    Application
    DeviceManagementConfiguration.ReadWrite.All
    Application
    DeviceManagementManagedDevices.Read.All
    Application
    DeviceManagementServiceConfig.Read.All
    Application
    DeviceManagementServiceConfig.ReadWrite.All
    Application
    Mail.Send
    Application
    Send mail as any user
    User.Read
    Delegated
    Sign in and read user profile

    Reply
  18. Hi Andrew

    Great idea
    i have issues getting it to work though

    have tried both the script in the blog (version 1.0.0) and GitHub (version 1.0.1)

    version 1.0.0 works both will send me the mail even if there is more than 30 days (have tried with 360 days)until expiration

    i also edited line 196 with this: Invoke-RestMethod -Method POST -Uri $URLsend -Headers $headers -Body $BodyJsonsend
    as i did not receive a mail regarding DEP until i compared it with PUSH and VPP and changed it

    reports this when run:
    System.Management.Automation.MethodException: Cannot find an overload for “ToString” and the argument count: “1”.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    System.Management.Automation.MethodException: Cannot find an overload for “ToString” and the argument count: “1”.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    System.Management.Automation.MethodException: Cannot find an overload for “ToString” and the argument count: “1”.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Version 1.0.1
    sends me a mail (even if the expiration is more or less than 30 days (have tried with 360 days), but not with any information regarding expiration or anything
    like this:
    Your Apple VPP Certificate is due to expire on

    Please Renew before this date

    it reports this error as well:
    eyJ0eXAiOiJKV1QiLCJub25jZSI6IldIS1B5bmZLOUQ0WXo2ZVo5dFNkdHR4UTk2dHN1S25HNjZsNGYyd0dPWjQiLCJhbGciOiJSUzI1NiIsIng1dCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyIsImtpZCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC85MjljMzQ1OS1jNDM1LTQxYTMtOGMyYi03MDhlYmJhZmQ5ZjgvIiwiaWF0IjoxNjkyOTUxNjc2LCJuYmYiOjE2OTI5NTE2NzYsImV4cCI6MTY5Mjk1NTU3NiwiYWlvIjoiRTJGZ1lOQm5aSDRyWVR2OStUS1pONTdjTDFVK0FBQT0iLCJhcHBfZGlzcGxheW5hbWUiOiJJbnR1bmUtQXBwbGVDZXJ0LUFsZXJ0cyIsImFwcGlkIjoiMjYzMTBhMDYtOGJmZC00Yjg0LTk5ODItNjdiODk3OTY3ZmZiIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTI5YzM0NTktYzQzNS00MWEzLThjMmItNzA4ZWJiYWZkOWY4LyIsImlkdHlwIjoiYXBwIiwib2lkIjoiMTdlZTAxNjktMzgyNC00MmIyLThlMGMtOTUzYThjZjc3ZDc3IiwicmgiOiIwLkFTRUFXVFNja2pYRW8wR01LM0NPdTZfWi1BTUFBQUFBQUFBQXdBQUFBQUFBQUFDR0FBQS4iLCJyb2xlcyI6WyJEZXZpY2VNYW5hZ2VtZW50TWFuYWdlZERldmljZXMuUmVhZC5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50U2VydmljZUNvbmZpZy5SZWFkLkFsbCIsIkRldmljZU1hbmFnZW1lbnRTZXJ2aWNlQ29uZmlnLlJlYWRXcml0ZS5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50Q29uZmlndXJhdGlvbi5SZWFkLkFsbCIsIk1haWwuU2VuZCIsIkRldmljZU1hbmFnZW1lbnRDb25maWd1cmF0aW9uLlJlYWRXcml0ZS5BbGwiLCJEZXZpY2VNYW5hZ2VtZW50QXBwcy5SZWFkLkFsbCJdLCJzdWIiOiIxN2VlMDE2OS0zODI0LTQyYjItOGUwYy05NTNhOGNmNzdkNzciLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiRVUiLCJ0aWQiOiI5MjljMzQ1OS1jNDM1LTQxYTMtOGMyYi03MDhlYmJhZmQ5ZjgiLCJ1dGkiOiJpS1lKRjlCVEMwU00yb0w2aWh4Z0FBIiwidmVyIjoiMS4wIiwid2lkcyI6WyIwOTk3YTFkMC0wZDFkLTRhY2ItYjQwOC1kNWNhNzMxMjFlOTAiXSwieG1zX3RjZHQiOjE0NzMxNDg4NzUsInhtc190ZGJyIjoiRVUifQ.f3r_H57Cvsj6sq61p9S5HB7TYsmUfthBGcio_Zz9-zbDSd4EES8Uu0sbMdjIev0xKKBHzSIkWzBewI1Xb4FjgFbHIV3a1quVjwv1XA2SXEUl4OBCxjqQeg5rosQJY1DH-HDq3l8AGT5Tun4BK_oSFkzH6k89IBwbdjn7fggfn3u1lOButvXcTnIxV5fCF73o2jXsBFJRGs9Kgi8a_AT-D1y2Xi2LZoCjoRI2PlTtmlXahjP25LPUwjKvsQseqHaRdIx92OsYRuv1pOXjvRZh9ale9Wojv05Ik2gic6fRUD9eqabHPGmGqMOqCZ454Qu7zN3VWM7RDSLs_yM8jl2K-g
    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    The remote server returned an error: (401) Unauthorized.
    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Reply
    • Hi,
      A 401 error normally means the app reg doesn’t have the permissions it needs to run the command (which is why it isn’t finding any dates). Can you confirm which API permissions it has?

      Reply
  19. I may try the second option. I think I have setup the app registration properly. I wish I could just use a system managed identity, but it seems that feature is too new for anyone to have written a guide.

    Reply
  20. I cannot get the graph authentication to work. This seems to be the problem with most online scripts I find. They don’t appear to use best practices for authentication in a way that is easy to reproduce and that works in 2022.

    Does your single token connection request work in 2022 without MFA and without certificates? I see conflicting info that says you must use one or both of those things.

    Script error: The remote server returned an error: (400) Bad Request

    Reply
    • Hi, this one uses an App Registration so there isn’t any MFA required. Do you have the app reg configured with all of the required permissions?
      The other option would be to use the MS.Graph.Intune module and replace the invoke-webrequest queries with invoke-msgraphrequest (and connect-msgraph with secret values)

      Reply
  21. Great idea, I have not tested the code yet but how will this work if you have several VPP/DEP tokens in your tenant.

    Reply
  22. I was going through your tutorial and I get this error when doing the testing

    System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
    at CallSite.Target(Closure , CallSite , Object , String )
    at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
    at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    Reply

Leave a Comment