Password Reminder with Proactive Remediation for AAD joined devices – Update (Using Azure Functions for a more secure way to call the Enterprise Application)

Introduction

A few weeks back I did a post about how to use Proactive Remediation’s to trigger a Toast for the end user notifying them that their password is about to expire (Feel free to read it here). I posted about it on Reddit & Twitter and got great responses for which I am really grateful, thanks to everyone who read and commented. The people who read it highlighted a potential issue with the Detection script regarding authentication against the Enterprise Application. The Detection script contains the authentication details of the Enterprise Application in clear-text which would be readable as the scripts that run on the device is stored in the registry and this could definitely be seen as a security flaw. Although I definitely see this as a risk, I don’t necessarily see this is as a “big” risk for a couple of reasons added together:

  1. For this to be exploitable one would need to get a hold of a device and be able to log onto it
  2. The only permissions we granted in the Enterprise Application was to read user profiles

But someone pointed out that the guys over at https://msendpointmgr.com addressed a similar issue a while in back in this post and that we could probably utilize their solution to more securely authenticate. Since they came up with such an elegant solution I was really eager to try it out.

Solution

Note: The solution going forward is based on the solution provided in the previous post

Instead of putting the authentication details in the Detection script we are going to create an an Azure Function that in turn will do the MS Graph Authentication and Query and just the respond with the details about when password was last set. Along with this we will make sure that the call to the Azure Function comes from an Azure AD joined device in our tenant. This should make our Authentication & MS Graph calls more secure and not provide any plain text authentication details in the script. The URL for the Azure Function will however be exposed, but we can control what information may be returned from the function as well as previously mention the device doing the HTTP call to the azure function will need to be a device joined to the correct tenant or the function will simply return a forbidden status.

To visualize this I have created a very lackluster Visio Sketch and provided a screenshot of said sketch:

Intune will Provide the AAD Device with the Detection script that will call the azure function, authenticate using the Enterprise Application and query Azure AD via MS Graph

What we need

  • An Enterprise Application (We’re going to modify the one we have already)
    • We need some new that permissions will be used to verify that the device is joined to our tenant
  • A Detection Script (We’re going to modify the one we have already)
    • We’re going to remove the Authentication details from the Detection script and replace them with an HTTP call
  • An Azure Function
    • This is what will complete the MSGraph query and return the information to the detection script

Enterprise Application

We’re gonna start of with modifying the Enterprise Application. We’re not going to change a lot since the application already does what we need, but seeing as we’re going to verify that the device is joined to our Azure tenant we need for the function to be able to fetch the devices currently in our tenant so it can do a comparison.

  1. Open Azure AD
  2. Navigate to App registrations
  3. Select the Application you Created previously (Note that there are multiple tabs and that if you can’t find your application select “All Applications” and search for it)
  4. Navigate to the API permissions tab
  5. Select Add a permission
  6. Chose Microsoft Graph
  7. Chose Application permissions
  8. Search Device.Read.All
  9. Mark the box for Device.Read.All under Device
  10. Add permissions
  11. Review that the correct permissions have been granted then Select Grant admin consent for “Tenant”
Make sure you have both User.Read.All & Device.Read.All and don’t forget to Grant the Permissions

Detection Script

We already have a detection script so we’re just gonna add some modifications to it. Mainly what we’re doing is removing the Authentication details for the Enterprise Application and the MSGraph query from the script, we’re also adding the HTTP call and response for the Azure Function and the gathering of the current device AzureADDeviceID and TenantID to send along with the HTTP call. Thanks again to the msendpointmgr guys & Nikolaj for the Get-AzureADDeviceID function.

Azure Function

So onto the really cool part, now we’re going to the create the Azure function that will handle the call, the query and the response.
The Function App consists of basically 2 parts when you create it. Firstly its the Application it Runs on and secondly the Functions within the plan. So we’re gonna start off by creating the Function App and then Function that will run the code. Also this will add Azure consumption and cost, but fortunately for us with the Consumption plan we get a bunch of free executions monthly and if your not in a very big environment this shouldn’t add any extra cost. Feel free to check out this pricing option for Azure Functions.

Creating the Function App

  1. Start off by searching for Function App in Azure
  2. Select Create
  3. Chose a fitting Subscription, Resource Group, and Region
  4. Select a fitting Name for your Function App, these names need to be unique, I chose “IntunePasswordNotificationTenantName” but it doesn’t matter
  5. Select Code, Powershell Core, Version 7.0
  6. Select Windows for Operating System and Consumption for Plan type
  7. Go through the rest of the tabs at your own preference and then Create

When the Function App is finished creating we can now head into it and start creating our Function

  1. Select the blade Functions
  2. Select Create
  3. You know get a few option of how to publish your code and you can chose your own preference, for this case I chose Develop in Portal
  4. Chose HTTP trigger as Template
  5. Select a fitting Name for your Function I chose “IntunePasswordNotification” but it doesn’t matter
  6. Select Function as Authorization level
  7. Create
Creating the Function

Now to enter the code in our Function

  1. Select the Function you created
  2. Select the Code + Test blade
  3. Here we need to enter the code that will run when we call on the function

To give just a little bit more insight on what is actually going on here for those who might be wondering. The function we’re creating is going to receive a “Payload” (a JSON body) from our detection script. This payload will be the AzureAD Device ID of the device that calls it, along with the Tenant ID and the Username of the logged on user. So the first thing in our script is going to be a parameter that is going to be the the input for the rest of the script, containing said payload.

The script will after this authenticate to the MS Graph and Azure AD using the Enterprise Application details you will input in the Script. After this it will verify that the device exists in the Azure AD and if it does it will do an MSGraph query to check when the Username provided in the Payload last set his password and return this info to the device.

Input the Enterprise Application details in the Function code then paste the code into the Function:

After we’re finished uploading and saving the code we need to get the URL and paste it into our detection script

  1. From the Code + Test blade select Get function URL
  2. Copy the URL
  3. Paste it into the Detection Script on Row 9
  4. Upload the Detection Script into Intune
Get the function URL

Done!

Note: when in the Function App you can open the Log slider at the bottom to view and verify what is going on when the HTTP call is coming in from the device

Try it out, tinker with it how you like and if you have any way to improve please feel free to comment

Further

Surely there are still ways to improve upon this and would love if someone would come with new ideas. One of the things I see as a clear improvement still stands from the last post and it would be to restring the AAD application to only being able to fetching the required information instead of the whole profile. I would be ever so grateful for feedback, comments or ideas how to improve upon this further

Thanks for reading

This Post Has 10 Comments

  1. Joseph

    Hi

    Nice solution friend

    All time in function app is throwing the following error:

    [Error] EXCEPTION: Response status code does not indicate success: 403 (Forbidden).Exception :Type : Microsoft.PowerShell.Commands.HttpResponseExceptionResponse : StatusCode: 403, ReasonPhrase: ‘Forbidden’, Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:{Cache-Control: no-cacheTransfer-Encoding: chunkedStrict-Transport-Security: max-age=31536000request-id: 902bb297-cd86-4724-8587-9a154e0d58d6client-request-id: 902bb297-cd86-4724-8587-9a154e0d58d6x-ms-ags-diagnostic: {“ServerInfo”:{“DataCenter”:”France Central”,”Slice”:”E”,”Ring”:”5″,”ScaleUnit”:”001″,”RoleInstance”:”PA2PEPF00000F3C”}}x-ms-resource-unit: 1Date: Tue, 31 May 2022 17:49:15 GMTContent-Type: application/json}TargetSite :Name : ThrowTerminatingErrorDeclaringType : System.Management.Automation.MshCommandRuntime, System.Management.Automation, Version=7.0.8.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35MemberType : MethodModule : System.Management.Automation.dllStackTrace :at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)Message : Response status code does not indicate success: 403 (Forbidden).Source : System.Management.AutomationHResult : -2146233088TargetObject : Method: GET, RequestUri: ‘https://graph.microsoft.com/v1.0/users/xxxxx@xxxxxxx.onmicrosoft.com?$select=userprincipalname,lastPasswordChangeDateTime’, Version: 1.1, Content: System.Net.Http.StringContent, Headers:{Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6InBPRWZPLUFVRHNEUWVacTU3ZW5Rd29sZGVHQ1Zsc3d2Mk5xaENjemJ5dFUiLCJhbGciOiJSUzI1NiIsIng1dCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyIsImtpZCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20vIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvODM2ZWQwNjUtMjdhYy00ZjUyLTg0YjUtOThmOThmNzQ0MTZkLyIsImlhdCI6MTY1NDAxOTA1NiwibmJmIjoxNjU0MDE5MDU2LCJleHAiOjE2NTQwMjI5NTYsImFpbyI6IkUyWmdZSGl6NzdQSWxSMmFTMjlOWTFaeTFwamdBUUE9IiwiYXBwX2Rpc3BsYXluYW1lIjoiUGFzc3dvcmQgUmVtaW5kZXIiLCJhcHBpZCI6IjlkMDEzNDc5LWE2NzEtNDZlZC04ZTA1LTA0MDkwZmZmYTFmOCIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzgzNmVkMDY1LTI3YWMtNGY1Mi04NGI1LTk4Zjk4Zjc0NDE2ZC8iLCJpZHR5cCI6ImFwcCIsIm9pZCI6IjE2YjJhMGZhLWI5NjgtNGI2Ny04YjFhLTdkOTgxNzNjNWU0MCIsInJoIjoiMC5BVGtBWmRCdWc2d25Vay1FdFpqNWozUkJiUU1BQUFBQUFBQUF3QUFBQUFBQUFBQTVBQUEuIiwicm9sZXMiOlsiRGV2aWNlLlJlYWQuQWxsIl0sInN1YiI6IjE2YjJhMGZhLWI5NjgtNGI2Ny04YjFhLTdkOTgxNzNjNWU0MCIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJFVSIsInRpZCI6IjgzNmVkMDY1LTI3YWMtNGY1Mi04NGI1LTk4Zjk4Zjc0NDE2ZCIsInV0aSI6IlVLNFllOXljYWtTNFZJNTlnVHRPQUEiLCJ2ZXIiOiIxLjAiLCJ3aWRzIjpbIjA5OTdhMWQwLTBkMWQtNGFjYi1iNDA4LWQ1Y2E3MzEyMWU5MCJdLCJ4bXNfdGNkdCI6MTU3MzU1MTIzOH0.MwVEuLxDtVMFVXHxSVrrrpYLA9-C765st8sjvv3OdYJEjFgS0tyiUr2JY37N1b4-RDkh8THZLnzksipe6zoSCZ28u8jnLkKCFL_u216LRGDiEU5p2–kCfvvmYi2o8-PiABr8ncqLL3pjm0jacKoXJlZwg-yr9fBixj7R30yZXOe_RyoqWkBncWkbVGefTDvwcZFCPLf6t3CqjXOmveBPmtfNIZShaIwAmoYx8LNGajQMproDJb2LLtHebQx6xPb8snQJqzhhIIfTmJv-MkF99WSiYzaL2wo9l226_V_VbQI82fGh-v3kUMzcJsZLrhl24OYkxuFI-5rJuYpWIAg0gAccept: application/jsonUser-Agent: Mozilla/5.0User-Agent: (Windows NT 10.0; Microsoft Windows 10.0.14393; en-US)User-Agent: PowerShell/7.0.8Content-Type: application/jsonContent-Length: 0}CategoryInfo : InvalidOperation: (Method: GET, Reques…ontent-Length: 0}:HttpRequestMessage) [Invoke-RestMethod], HttpResponseExceptionFullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommandErrorDetails : {“error”:{“code”:”Authorization_RequestDenied”,”message”:”Insufficient privileges to complete the operation.”,”innerError”:{“date”:”2022-05-31T17:49:16″,”request-id”:”902bb297-cd86-4724-8587-9a154e0d58d6″,”client-request-id”:”902bb297-cd86-4724-8587-9a154e0d58d6″}}}InvocationInfo :MyCommand : Invoke-RestMethodScriptLineNumber : 91OffsetInLine : 25HistoryId : 1ScriptName : D:\home\site\wwwroot\PasswordReminder\run.ps1Line : $Response = Invoke-RestMethod -Uri $URI -Headers $authHeader -Method $method -ErrorAction StopPositionMessage : At D:\home\site\wwwroot\PasswordReminder\run.ps1:91 char:25+ … $Response = Invoke-RestMethod -Uri $URI -Headers $authHeader -Method …+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~PSScriptRoot : D:\home\site\wwwroot\PasswordReminderPSCommandPath : D:\home\site\wwwroot\PasswordReminder\run.ps1InvocationName : Invoke-RestMethodCommandOrigin : InternalScriptStackTrace : at Invoke-MSGraphQuery, D:\home\site\wwwroot\PasswordReminder\run.ps1: line 91at , D:\home\site\wwwroot\PasswordReminder\run.ps1: line 206
    2022-05-31T17:49:18.732 [Error] Executed ‘Functions.PasswordReminder’ (Failed, Id=7e2cd62e-7cc2-4345-9457-62dfe9474119, Duration=5113ms)Result: FailureException: {“error”:{“code”:”Authorization_RequestDenied”,”message”:”Insufficient privileges to complete the operation.”,”innerError”:{“date”:”2022-05-31T17:49:16″,”request-id”:”902bb297-cd86-4724-8587-9a154e0d58d6″,”client-request-id”:”902bb297-cd86-4724-8587-9a154e0d58d6″}}}Stack: at System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)at System.Management.Automation.Runspaces.Pipeline.Invoke()at System.Management.Automation.PowerShell.Worker.ConstructPipelineAndDoWork(Runspace rs, Boolean performSyncInvoke)at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)at System.Management.Automation.PowerShell.CoreInvoke[TOutput](IEnumerable input, PSDataCollection1 output, PSInvocationSettings settings)at System.Management.Automation.PowerShell.Invoke[T](IEnumerable input, IList1 output, PSInvocationSettings settings)at System.Management.Automation.PowerShell.Invoke[T]()at Microsoft.Azure.Functions.PowerShellWorker.PowerShell.PowerShellExtensions.InvokeAndClearCommands[T](PowerShell pwsh) in /mnt/vss/_work/1/s/src/PowerShell/PowerShellExtensions.cs:line 45at Microsoft.Azure.Functions.PowerShellWorker.PowerShell.PowerShellManager.InvokeNonOrchestrationFunction(DurableController durableController, IDictionary outputBindings) in /mnt/vss/_work/1/s/src/PowerShell/PowerShellManager.cs:line 301at Microsoft.Azure.Functions.PowerShellWorker.PowerShell.PowerShellManager.InvokeFunction(AzFunctionInfo functionInfo, Hashtable triggerMetadata, TraceContext traceContext, RetryContext retryContext, IList`1 inputData, FunctionInvocationPerformanceStopwatch stopwatch) in /mnt/vss/_work/1/s/src/PowerShell/PowerShellManager.cs:line 230at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.InvokeFunction(AzFunctionInfo functionInfo, PowerShellManager psManager, FunctionInvocationPerformanceStopwatch stopwatch, InvocationRequest invocationRequest) in /mnt/vss/_work/1/s/src/RequestProcessor.cs:line 335at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.ProcessInvocationRequestImpl(StreamingMessage request, AzFunctionInfo functionInfo, PowerShellManager psManager, FunctionInvocationPerformanceStopwatch stopwatch) in /mnt/vss/_work/1/s/src/RequestProcessor.cs:line 307

    1. Viktor

      Hi Jospeh!
      Sorry for the late reply, actually this message indicates not enough permissions on the enterprise application. Can you redo the step for enterprise application?

      Be sure to grant the applications, if it doesn’t work out still feel free to message me on Reddit and we can chat about it to find the solution?

      https://www.reddit.com/user/IntRangeNoShut

  2. Frank van Rijt

    We are using a script that queries the on prem AD (directly or via the VPN that is open), as we are using PTA for authentication) and based on that we generate a toast message to change the PW.

    Query script used:

    #=============================================================================================================================
    #
    # Script Name: Detect_Expired_User_Password.ps1
    # Description: Detect almosted password by
    # Notes: Configure $expiringdays to select the number of days before a user should be warned.
    # Author: Frank van Rijt
    #=============================================================================================================================

    # Define Variables
    $results = @()
    $expiringDays = 14
    $DomainName=”OnPrem Active Directory Domain”

    try {
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    $PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain,$DomainName)
    $SamAccountName = ([System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($PrincipalContext,[System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName,[Environment]::UserName)).SamAccountName
    $PrincipalContext.Dispose()
    }
    catch {
    $errMsg = “Password Age Check warning, cannot query Active Directory for user. No connection to DGN?”
    Write-Error $errMsg
    exit 0
    }
    if (($SamAccountName) -AND ($DomainName)) {
    try {
    $Root = [ADSI] “LDAP://$($DomainName)”
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher($Root, “(SamAccountName=$($SamAccountName))”)
    $Searcher.PropertiesToLoad.Add(“msDS-UserPasswordExpiryTimeComputed”) | Out-Null
    $Result = $Searcher.FindOne();
    $ExpiryDate = [DateTime]::FromFileTime([Int64]::Parse((($Result.Properties[“msDS-UserPasswordExpiryTimeComputed”])[0]).ToString()))
    }
    catch {
    Write-Error “Failed to retrieve password expiration date from Active Directory.”
    exit 0

    }
    if ($ExpiryDate) {
    $LocalCulture = Get-Culture
    $RegionDateFormat = [System.Globalization.CultureInfo]::GetCultureInfo($LocalCulture.LCID).DateTimeFormat.LongDatePattern
    $ExpiryDate = Get-Date $ExpiryDate -f “$RegionDateFormat”
    $Today = Get-Date -f “$RegionDateFormat”
    $DateDiff = New-TimeSpan -Start $Today -End $ExpiryDate
    if ($DateDiff.Days -le $expiringDays -AND $DateDiff.Days -ge 0) {
    Write-Output “Password expiring within 14 days: ” $($DateDiff.Days)
    exit 1
    }
    else {
    Write-Error $($DateDiff.Days)
    exit 0
    }
    }
    elseif (-NOT($ExpiryDate)) {
    Write-Error “No Password expiration date found”
    exit 0
    }
    }
    elseif (-NOT($SamAccountName) -OR ($DomainName)) {
    Write-Error “No user or domain found, nothing script can handle”
    exit 0
    }

    1. Viktor

      Hi!

      Very cool,
      I guess this is not viable if there is no line of sight to AD but if you are using a Always on VPN this could be cool!
      Thanks for Sharing

  3. Rene

    Hi,
    I think there should be a difference between App Registration and Enterprise App. Both are valid choices in Azure but with different options. Especially the needed secrets are only in App Registrations.

    Could you please clarify. I would like to use your script, as we don’t store mail-addresses in AAD and the users don’t get a notification.

    Kind regards

    1. Viktor

      Hi Rene! Sorry for the late response. So in your case you want to do an App Registration. Basically an Enterprise application is a application from a third party that requires permissions in your Environment, and an app registration is a app you register and control. Hope that helps, let me know if I can help you out any further.

      Kind regards

  4. Jackson

    Hi,

    After following the guides i deployed it to 5 Machines, however all of them has Detection Status showing “With Issues” and “Remediation Status showing “Recurred”

    Further error logs for Pre-Remediation is below
    ==================================================================================================
    Cannot convert value “” to type “System.DateTime”. Error: “String was not recognized as a valid DateTime.” At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:123 char:1 + [datetime]$lastpasswordChange = $User.lastPasswordChangeDateTime -rep … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException + FullyQualifiedErrorId : RuntimeException You cannot call a method on a null-valued expression. At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:125 char:1 + $PasswordExpirationDate = ($lastpasswordChange).AddDays($PasswordExpi … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull New-TimeSpan : Cannot bind parameter ‘End’ to the target. Exception setting “End”: “Cannot convert null to type “System.DateTime”.” At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:129 char:49 + … imeSpan = New-Timespan -Start $StartDate -End $PasswordExpirationDate + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (:) [New-TimeSpan], ParameterBindingException + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.NewTimeSpanCommand

    Post Remediation is below
    ====================================================================================================
    Cannot convert value “” to type “System.DateTime”. Error: “String was not recognized as a valid DateTime.” At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:123 char:1 + [datetime]$lastpasswordChange = $User.lastPasswordChangeDateTime -rep … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException + FullyQualifiedErrorId : RuntimeException You cannot call a method on a null-valued expression. At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:125 char:1 + $PasswordExpirationDate = ($lastpasswordChange).AddDays($PasswordExpi … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull New-TimeSpan : Cannot bind parameter ‘End’ to the target. Exception setting “End”: “Cannot convert null to type “System.DateTime”.” At C:\WINDOWS\IMECache\HealthScripts\1c7bfec0-b593-40a3-8953-b608019a14ec_2\detect.ps1:129 char:49 + … imeSpan = New-Timespan -Start $StartDate -End $PasswordExpirationDate + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (:) [New-TimeSpan], ParameterBindingException + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.NewTimeSpanCommand

    1. Anonymous

      Hi Jackson,
      did you find a solution for this? I’m facing the same issue

  5. Mark Graham

    Have you investigated using a Managed Identity instead of an app registration?
    It seems like it should be doable to me.

  6. doogalbeez

    Excellent post! Trying to get it working and getting this issue:
    ERROR: Response status code does not indicate success: 401 (Unauthorized). Exception : Type : Microsoft.PowerShell.Commands.HttpResponseException Response : StatusCode: 401, ReasonPhrase: ‘Unauthorized’, Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:

    It continues to not liking line 38 (or 36 in your code):
    InvocationName : Invoke-RestMethod CommandOrigin : Internal ScriptStackTrace : at Get-MSGraphAuthToken, C:\home\site\wwwroot\IntunePasswordNotification\run.ps1: line 38 at ,

    Tried to change the line to use “https://$Resource/” but it didn’t help:
    $AuthBody = “grant_type=client_credentials&client_id=$($credential.UserName)&client_secret=$($credential.GetNetworkCredential().Password)&resource=https%3A%2F%2F$Resource%2F”

    Can you check if the MSGraph connection to the function needs to be updated? I am still learning Graph and not sure what I could do to get it working.

    Thanks!

Leave a Reply