Serverless LAPS powered by Microsoft Intune, Azure Functions and Azure Key Vault!

Serverless LAPS powered by Microsoft Intune, Azure Functions and Azure Key Vault!

UPDATE 25 August 2021

Please note that this blog post dates from 2018 and there have been major updates to the Azure components used in this solution. Currently more up-to-date and enhanced community tools are also available. I would suggest looking into CloudLAPS by @NickolajA

Original content

I’m excited to introduce a Serverless Local Administrator Password Solution (SLAPS 😉) for Windows 10 Intune Managed devices, powered by Microsoft Intune PowerShell scriptsAzure Functions and Azure Key Vault.

Building this solution has been quite a challenge, as there were many obstacles to overcome. If you’ve read the article of Oliver Kieselbach: “Deep dive Microsoft Intune Management Extension – PowerShell Scripts“, then you know that all script contents are logged in plain text (including passwords!) to the IntuneManagementExtension.log file. So I had to come up with a solution that would not expose passwords in plain text at any time.

That’s where Azure Functions come in! I managed to create an HTTP-based API, using the experimental PowerShell language support in a Function App, that contains all of the logics for returning random passwords on demand and simultaneously storing them securely into an Azure Key Vault. Passwords are contained in a variable and never exposed in plain text.

From the Intune PowerShell script, the Azure Function is queried over a TLS 1.2 encrypted connection, using the Invoke-RestMethod cmdlet. The hostname of the initiating device, and the username defined in the PowerShell Script are used to create the Local Administrator, and are also passed with the POST method to the Azure Function within the request body of the Invoke-RestMethod cmdlet.

When the Azure Function is triggered, it will return a random generated password to the PowerShell script, and also creates or updates a secret in your Azure Key Vault. The name of the secret is based on the hostname of the device. The value will be the randomly generated password, and the username is added as a tag.

Sounds nice, right? But there’s one more challenge… I don’t want to expose the url containing the authorization code to trigger the Azure Function in plain text within the log file. I want to be able to rely on the contents of Key vault. Exposing this information would allow anyone to read the log file and put false information in the Key vault, by executing the same cmdlets with modified data. I figured out I could manipulate the log file during the runtime of the PowerShell script using the code below:

# Azure Function Uri (containing "azurewebsites.net") for storing Local Administrator secret in Azure Key Vault
$uri = 'https://myfunctions.azurewebsites.net/api/SetKeyVaultSecret?code=myFunctionCode=='

# Hide the $uri (containing "azurewebsites.net") from logs to prevent manipulation of Azure Key Vault
$intuneManagementExtensionLogPath = "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log"
Set-Content -Path $intuneManagementExtensionLogPath -Value (Get-Content -Path $intuneManagementExtensionLogPath | Select-String -Pattern "azurewebsites.net" -NotMatch)

This would remove each line from the log file that matches the “azurewebsites.net” string.

Note:
Please note that this is just a workaround to hide information from the log file. It is absolutely not waterproof. An ‘attacker’ will still be able to retrieve the contents of the PowerShell script. The Intune Management Extension will download scripts just before they are executed to “C:\Program Files (x86)\Microsoft Intune Management Extension\Policies\Scripts”, where it will be removed after execution.

Your Local Administrator credentials are safe, because they are never exposed, however your Key vault may get manipulated if the Azure Function is abused. If you require a better solution, please vote for Secure authentication within PowerShell scripts for Intune MDM on the Microsoft Intune UserVoice forums

 


Prerequisites

Before being able to configure the Local Administrator Password Solution using the instructions below, the following prerequisites must be met:

 

Note:
The Azure Function is using the experimental PowerShell language support. I’d be happy to share a C# example if anyone is up for the challenge 🙂

 


Configuration

Considering that a Function App and Key vault have already been deployed, I’ve divided the configuration of the Serverless Local Administrator Password Solution into six parts:

  1. Register a Managed Service Identity with Azure Active Directory;
  2. Grant the Managed Service Identity access to the Key vault;
  3. Create the Azure Function;
  4. Test the Azure Function;
  5. Deploy the PowerShell script with Microsoft Intune;
  6. Validate the deployment of the PowerShell script;

 

1. Register a Managed Service Identity with Azure Active Directory

A Managed Service Identity needs to be registered with Azure Active Directory first, that will be used to authenticate with the Azure Key Vault.

  • Open the Azure Portal;
  • Navigate to Function Apps;
  • In the Functions Apps blade, select the Function App you wish to configure;
  • Navigate to the Platform features tab;
  • Under Networking, click Managed service identity;
  • Set Register with Azure Active Directory to On and click Save.

 

2. Grant the Managed Service Identity access to the Key vault

The next step is to add the Managed Service Identity to an Access policy in the Key vault.

  • Navigate to Key vaults;
  • On the Key vaults blade, select the Key vault you wish to configure;
  • In the Key vault blade that displays, click Access policies;
  • In the Access policies blade that displays, click Add new;
  • In the Add access policy blade that displays, click Select principal;
  • Enter the name of your Function App and click Select;
  • In the Secret permissions, select Secret Management Operations Set;
  • Click OK and click Save.

 

3. Create the Azure Function

Now that the Managed Service Identity of the Function App has been granted access to Set secrets in the Key vault. It’s time to create the Azure Function.

  • Navigate to Function Apps;
  • In the Functions Apps blade, select the Function App you wish to configure;
  • Select Functions and click New function;
  • Enable the Experimental Language Support functions;
  • Click HTTP triggerPowerShell;
  • Configure the new function:
    • Language: PowerShell
    • Name: Set-KeyVaultSecret
    • Authorization level: Function
  • Replace the content of the Set-KeyVaultSecret function with the PowerShell code of the Set-KeyVaultSecret.ps1 file downloaded from the TechNet Gallery;
  • Set the name of your Key vault in the $keyVaultName variable;
  • Click Save;

Note:
Take note of the Function URL (</> Get Function URL), which needs to be set in the New-LocalAdmin.ps1 script later.

By default the Function App requires a TLS connection with a minimum version of 1.2.

 

4. Test the Azure Function

Before continuing to configure the PowerShell script in Intune that creates the Local Administrator accounts, it’s recommended to test if the Azure Function works as expected.

  • Grab the request body from the New-LocalAdmin.ps1 script;
  • Replace the variables with values, for example:
    {
        "keyName": "TEST-PC01",
        "contentType": "Local Administrator Credentials",
        "tags": {
            "Username": "localadmin"
        }
    }
  • Run the Azure Function and review the output;
    If the Function runs successfully, a random password is returned the Output pane.
  • Validate the creation of the secret in the Key vault as defined in the keyName.

 

5. Deploy the PowerShell script with Microsoft Intune

  • Modify the configuration section of the New-LocalAdmin.ps1 script;
    • Choose a username for the Local Administrator accounts;
    • Enter the Function URL;

      The New-LocalAdmin.ps1 script will restart itself in a 64-bit process, because cmdlets like New-LocalUser are not available in a 32-bit process, which is the architecture of the Intune Management Extension agent.

  • In the Azure Portal, navigate to IntuneDevice Configuration PowerShell scripts;
  • In the PowerShell scripts blade, click + Add;
  • On the Add PowerShell script blade, enter the following information and click Create;
    • Name: New-LocalAdmin
    • Script Location: Select the New-LocalAdmin.ps1 file, previously downloaded from the TechNet Gallery
  • In the New-LocalAdmin blade that displays, click Assignments;
  • Select a group you wish to deploy the solution to;
  • Save the assignments;

 

Note:
PowerShell scripts can only be deployed to users. On shared devices, the provided PowerShell script will change the password of the Local Administrator user every time a new user logs on to a device.

 

6. Validate the deployment of the PowerShell script

PowerShell scripts are executed by the Intune Management Extension. The agent will check every 60 minutes if a script is ready to be deployed. You can force a sync by restarting the Microsoft Intune Management Extension Service on the device.

The status of the PowerShell script deployment is returned to the Monitor > Device/User status overview in Intune.

When the PowerShell script succeeds, validate the creation of a Local Administrator on the device, and that the credentials were stored in the Key vault.

 


More information

Need to troubleshoot further? Check the log files on a device at C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log, which contains the logging for PowerShell Scripts.

If you have any questions or feedback, feel free to leave a comment!

 

60 thoughts on “Serverless LAPS powered by Microsoft Intune, Azure Functions and Azure Key Vault!

  1. Pretty nice man… thanks for this! Have you made any changes since posting? Any better ways to control access to being able to invoke the Azure function, besides PKI?
    I went ahead and upvoted the uservoice on secure authentication through PowerShell.

    Thanks again!

  2. So I am failing on this somehow, interface changed and the “Managed Service Identity” has changed to “Identity” and the object seems to never exist when I create it. As such, when I run the script, I get this in out Output:

    {
    “id”: “f7a61fa2-6aec-4672-b547-77154614306d”,
    “requestId”: “abeff90e-055f-4519-902d-6cfbcc64f389”,
    “statusCode”: 500,
    “errorCode”: 0,
    “message”: “An error has occurred. For more information, please check the logs for error ID f7a61fa2-6aec-4672-b547-77154614306d”
    }

    and in the log window i receive:

    2019-03-19T16:37:16.952 [Info] Function started (Id=8459ee2d-f154-4355-aa5d-db8d0242faf8)
    2019-03-19T16:37:17.311 [Error] Invoke-RestMethod : {“error”:{“code”:”Forbidden”,”message”:”Access denied”,”innererror”:{“code”:”AccessDenied”}}}
    at run.ps1: line 34
    + Invoke-RestMethod
    + _________________
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
    2019-03-19T16:37:17.342 [Error] Exception while executing function: Functions.Set-KeyVaultSecret. Microsoft.Azure.WebJobs.Script: PowerShell script error. Microsoft.PowerShell.Commands.Utility: The remote server returned an error: (403) Forbidden.
    2019-03-19T16:37:17.373 [Error] Function completed (Failure, Id=8459ee2d-f154-4355-aa5d-db8d0242faf8, Duration=416ms)

    the permissions are set properly per this article so I am unsure why this is occurring.

    1. Hi Corey,

      You are getting an authentication error on setting the secret to Azure Key Vault. It seems that the Managed Service Identity (Service Principal) of the Function App itself does not have the “Secret permissions” set to “Set” on the Azure Key Vault, hence you get a 403 forbidden error.

      Can you double check that the “Access policy” on the Azure Key Vault, that it contains the “Application” (Function App) with “Secret permissions” set to “Set”? When I remove my Access Policy, I get the same error as you do.

  3. Admiring the time and energy you put into your blog and in depth information you provide.
    It’s awesome to come across a blog every once
    in a while that isn’t the same unwanted rehashed material.
    Fantastic read! I’ve saved your site and I’m adding
    your RSS feeds to my Google account.

  4. Hi John, I getting this error. Please let me know where I went wrong?

    2019-04-25T04:39:49 Welcome, you are now connected to log-streaming service.
    2019-04-25T04:40:49 No new trace in the past 1 min(s).
    2019-04-25T04:41:49 No new trace in the past 2 min(s).
    2019-04-25T04:42:27.065 [Information] Executing ‘Functions.Set-KeyVaultSecret’ (Reason=’This function was programmatically called via the host APIs.’, Id=9907eb8d-80af-426c-b3a9-a5ae9eeece66)
    2019-04-25T04:42:27.113 [Error] Executed ‘Functions.Set-KeyVaultSecret’ (Failed, Id=9907eb8d-80af-426c-b3a9-a5ae9eeece66)
    Result: Failure
    Exception: No parameter defined in the script or function for the input binding ‘Request’.

    Stack: at Microsoft.Azure.Functions.PowerShellWorker.AzFunctionInfo..ctor(RpcFunctionMetadata metadata) in C:\projects\azure-functions-powershell-worker\src\FunctionInfo.cs:line 120
    at Microsoft.Azure.Functions.PowerShellWorker.FunctionLoader.LoadFunction(FunctionLoadRequest request) in C:\projects\azure-functions-powershell-worker\src\FunctionLoader.cs:line 45
    at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.ProcessFunctionLoadRequest(StreamingMessage request) in C:\projects\azure-functions-powershell-worker\src\RequestProcessor.cs:line 187
    2019-04-25T04:43:49 No new trace in the past 1 min(s).
    2019-04-25T04:44:49 No new trace in the past 2 min(s).
    2019-04-25T04:45:49 No new trace in the past 3 min(s).
    2019-04-25T04:46:44.174 [Information] Executing ‘Functions.Set-KeyVaultSecret’ (Reason=’This function was programmatically called via the host APIs.’, Id=b62dc2e5-9ae0-4315-9a6c-200a7be2a4bc)
    2019-04-25T04:46:44.215 [Error] Executed ‘Functions.Set-KeyVaultSecret’ (Failed, Id=b62dc2e5-9ae0-4315-9a6c-200a7be2a4bc)
    Result: Failure
    Exception: No parameter defined in the script or function for the input binding ‘Request’.

    Stack: at Microsoft.Azure.Functions.PowerShellWorker.AzFunctionInfo..ctor(RpcFunctionMetadata metadata) in C:\projects\azure-functions-powershell-worker\src\FunctionInfo.cs:line 120
    at Microsoft.Azure.Functions.PowerShellWorker.FunctionLoader.LoadFunction(FunctionLoadRequest request) in C:\projects\azure-functions-powershell-worker\src\FunctionLoader.cs:line 45
    at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.ProcessFunctionLoadRequest(StreamingMessage request) in C:\projects\azure-functions-powershell-worker\src\RequestProcessor.cs:line 187
    2019-04-25T04:47:49 No new trace in the past 1 min(s).
    2019-04-25T04:48:49 No new trace in the past 2 min(s).
    2019-04-25T04:49:49 No new trace in the past 3 min(s).
    2019-04-25T04:50:49 No new trace in the past 4 min(s).
    2019-04-25T04:51:49 No new trace in the past 5 min(s).
    2019-04-25T04:52:49 No new trace in the past 6 min(s).
    2019-04-25T04:53:49 No new trace in the past 7 min(s).
    2019-04-25T04:54:49 No new trace in the past 8 min(s).
    2019-04-25T04:55:49 No new trace in the past 9 min(s).
    2019-04-25T04:56:49 No new trace in the past 10 min(s).
    2019-04-25T04:57:49 No new trace in the past 11 min(s).
    2019-04-25T04:58:49 No new trace in the past 12 min(s).

    2019-04-25T04:59:34 The application was terminated.

    1. Hi Nkoro,

      Please note that Azure Functions have had a big overhaul since writing this post. I used the “Experimental” PowerShell support in Azure Functions v1. Nowadays there is Azure Functions v2 and recently released with support for PowerShell Core in Public Preview. I’m expecting it works differently.

      Regards,

      John

  5. Awesome blog and thanks very much for all your help John.

    Got this working with v2 using the code below.

    Had to generate a password using the function as it’s not available in core. Removed some characters I don’t like 🙂

    ###Azure Function v2####

    using namespace System.Net

    param($Request)

    $keyVaultName = “YOUR KEY VAULT”
    $computer = $request.body.keyname
    write-host “Attempting to connect to Key Vault using MSI”
    # Get Azure Key Vault Access Token using the Function’s Managed Service Identity

    # Azure Key Vault resource to obtain access token
    $vaultTokenUri = ‘https://vault.azure.net’
    $apiVersion = ‘2017-09-01’

    # Get Azure Key Vault Access Token using the Function’s Managed Service Identity
    $authToken = Invoke-RestMethod -Method Get -Headers @{ ‘Secret’ = $env:MSI_SECRET } -Uri “$($env:MSI_ENDPOINT)?resource=$vaultTokenUri&api-version=$apiVersion”

    # Use Azure Key Vault Access Token to create Authentication Header
    $authHeader = @{ Authorization = “Bearer $($authToken.access_token)” }

    write-host “Setting Random Password”

    function new-password{

    $Alphabets = ‘a,b,c,d,e,f,g,h,i,j,k,m,n,p,q,r,t,u,v,w,x,y,z’
    $numbers = 2..9
    $specialCharacters = ‘!,@,#,$,%,&,*,?,+’
    $array = @()
    $array += $Alphabets.Split(‘,’) | Get-Random -Count 6
    $array[0] = $array[0].ToUpper()
    $array[-1] = $array[-1].ToUpper()
    $array += $numbers | Get-Random -Count 3
    $array += $specialCharacters.Split(‘,’) | Get-Random -Count 3
    ($array | Get-Random -Count $array.Count) -join “”
    }

    $password = new-password

    # Generate a new body to set a secret in the Azure Key Vault
    $body = $request.body | Select-Object -Property * -ExcludeProperty keyName

    # Append the random password to the new body
    $body | Add-Member -NotePropertyName value -NotePropertyValue “$password”

    # Convert the body to JSON
    $body = $body | ConvertTo-Json

    # Azure Key Vault Uri to set a secret
    Write-host “Creating string to key vault”
    $vaultSecretUri = “https://$keyVaultName.vault.azure.net/secrets/$($request.Body.keyName)/?api-version=2016-10-01”

    # Set the secret in Azure Key Vault
    try{
    write-host “Setting secret in Azure Key Vault”
    $null = Invoke-RestMethod -Method PUT -Body $body -Uri $vaultSecretUri -ContentType ‘application/json’ -Headers $authHeader

    # Associate values to output bindings by calling ‘Push-OutputBinding’.
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    Body = $password })
    }
    catch{$error[0].errordetails.message}

    1. Hi Bally Singh,

      Thank you for your comment and sharing your code. Great that you’ve got it working with v2. I will try it out some time and update the post accordingly.

      Regards,

      John

    2. Hi Bally.

      I am using this code within my Azure function. However, when testing with the body element of the NewLocalAdmin file, there is no output being generated.

      Any ideas?

      1. Hi Mark,

        I noticed the same but think it has to do with the formatting the WordPress does in the comment. I’ve just released updated scripts on GitHub, using some of Bally’s code.

        Would be glad to know if it works out now!

        https://github.com/jseerden/SLAPS

        Will update the blog post accordingly when I find some time to do so!

        Best regards,

        John

        1. Thanks John.

          I have reverted back to your original code for the PS function, as funnily enough after refreshing the functions page within Azure a couple of times, the option reappeared for ‘Experimental Language Support’. It was not visible initially.

          So… I am using the ‘Set-KeyVaultSecret’ script from your article for the function, and the test within the function now generates a password without any errors. The problem is now relating to the ‘New-LocalAdmin’ client-side script. It fails, with various 401 (unauthorised) errors within the log, and I can see that the password has not been passed to the client as the $password variable is null.

          I’ve taken the script from Github and updated it, I’ll see what happens 🙂

  6. Do you have this on Github by chance? I think theres a large option for the community to help expand this and bug fix. Such as the ability to add a scheduled task creation to run this at X date.

    1. Hi,

      I’ve managed the password rotation packaging the script as a win32 application, the detection script detects whether or not the password is to old.
      Next step is to handle passwords that were used.

      I’ll share on my github when it’s done and I’ve removed all sensible information.

    2. Hi Corey,

      Would be awesome if the community picks this up too! I’m seeing increased activity on this blog post, so planning an update soon.

      Prior to updating the post, I just released updated scripts on GitHub (https://github.com/jseerden/SLAPS), using some of Bally’s code, that should work with a v2 Azure Function App running PowerShell Core (which is in Preview).

      Best regards,

      John

  7. Hello John,

    Thanks for all of your hard work on this. I’m using Bally’s code to get this working and am able to test and execute the Set-KeyVaultSecret function successfully from within Azure, however, when trying to run the local ps1 script, I’m being returned an empty string value. I’m sure I’m missing something simple?

    1. Hi Nick,

      I experienced the same issue and believe it might have to do something with the formatting in WordPress.

      I’ve just released updated scripts on GitHub (https://github.com/jseerden/SLAPS), using some of Bally’s code, that should work with a v2 Azure Function App running PowerShell Core (which is in Preview).

      Would be glad to know if it works out now!

      Best regards,

      John

  8. Was getting the same error as NKoro. “Exception: No parameter defined in the script or function for the input binding ‘Request’.” Any update there by chance?

    Thanks!

  9. This is awesome thank you. But I think these instructions are outdated correct? I have ran into some snags where for example i cant find the Powershell Experimental language as an option?

  10. If you have any ARM devices, be sure to also add logic to check for them. Right now you get a powershell recursive loop if you run it as is.

    Example:
    if ((gwmi -Class Win32_OperatingSystem ).OSArchitecture -like “*ARM*”)
    {
    #ARM devices are not supported
    Write-Output “ARM Devices are not supported for this script and must be configured manually”
    }

    1. Hi Michael,

      Agreed, at least if you are targetting the PowerShell scripts to users. If you would be assigning it to devices you should exclude a dynamic group that includes all ARM devices.

      Best regards,

      John

  11. This is an awesome solution, but my concern is that this script will only run once per user if deployed via Intune. Have you tested using Azure Automation DSC, so that it runs every 30 minutes no matter whom logs in…like LAPS does?

    1. Hi,

      The script will probably run once per user, that’s true. I haven’t worked with Azure Automation DSC on Windows 10 clients.

      Another way to accomplish rotation with just Intune is to repackage the script as a Win32 app, and include some logic that the detection rule can check when the admin credential was created/updated. As detections run every hour with the IntuneManagementExtension, and when it fails that will trigger a reinstall, periodical rotation can be achieved like that.

      Best regards,

      John

  12. I’m followed the updated tutorial from Tim Hermie and the scripts from https://github.com/jseerden/SLAPS, the random password generates, but it doesnt update the secrets in key vault, the error is
    2020-04-08T21:26:29 Welcome, you are now connected to log-streaming service. The default timeout is 2 hours. Change the timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds).
    2020-04-08T21:27:29 No new trace in the past 1 min(s).
    2020-04-08T21:28:29 No new trace in the past 2 min(s).
    2020-04-08T21:29:29 No new trace in the past 3 min(s).
    2020-04-08T21:30:29 No new trace in the past 4 min(s).
    2020-04-08T21:31:29 No new trace in the past 5 min(s).
    2020-04-08T21:32:18.621 [Information] Executing ‘Functions.set-keyvaultsecret’ (Reason=’This function was programmatically called via the host APIs.’, Id=bb3f1e71-56db-48bf-82d6-04c8ef7fb462)
    2020-04-08T21:32:20.183 [Information] Managed dependency download is in progress, function execution will continue when it’s done.
    2020-04-08T21:33:29 No new trace in the past 1 min(s).
    2020-04-08T21:34:29 No new trace in the past 2 min(s).
    2020-04-08T21:35:29 No new trace in the past 3 min(s).
    2020-04-08T21:36:02.364 [Error] ERROR: Invoke-RestMethod : {“error”:{“code”:”MethodNotAllowed”,”message”:”HTTP PUT not allowed”}}
    At D:\home\site\wwwroot\set-keyvaultsecret\run.ps1:49 char:9
    + $null = Invoke-RestMethod -Method PUT -Body $body -Uri $vaultSecretUr …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Method: PUT, Reques\u2026application/json
    }:HttpRequestMessage) [Invoke-RestMethod], HttpResponseException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
    Script stack trace:
    at , D:\home\site\wwwroot\set-keyvaultsecret\run.ps1: line 49

    Microsoft.PowerShell.Commands.HttpResponseException: Response status code does not indicate success: 405 (Method Not Allowed).
    at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)

    Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcException : Result: ERROR: Invoke-RestMethod : {“error”:{“code”:”MethodNotAllowed”,”message”:”HTTP PUT not allowed”}}
    At D:\home\site\wwwroot\set-keyvaultsecret\run.ps1:49 char:9
    + $null = Invoke-RestMethod -Method PUT -Body $body -Uri $vaultSecretUr …
    +

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Method: PUT, Reques\u2026application/json
    }:HttpRequestMessage) [Invoke-RestMethod], HttpResponseException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
    Script stack trace:
    at , D:\home\site\wwwroot\set-keyvaultsecret\run.ps1: line 49

    Microsoft.PowerShell.Commands.HttpResponseException: Response status code does not indicate success: 405 (Method Not Allowed).
    at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)

    Exception: Response status code does not indicate success: 405 (Method Not Allowed).
    Stack: at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)
    2020-04-08T21:36:02.946 [Information] Executed ‘Functions.set-keyvaultsecret’ (Succeeded, Id=bb3f1e71-56db-48bf-82d6-04c8ef7fb462)

    what am i missing?

    1. Hi mihaii,

      Hope you have resolved this issue in the meantime.

      It seems to me that the Azure Functions Managed Identity Service Principal doesn’t have the necessary permissions to put credentials in the Azure Key Vault.

      Best regards,

      John

      1. Hi mihaii,
        Did you resolve this in the end? I am having exactly the same error as you.

        Thanks,
        peonnomore

  13. Hey mate, I do use it :).
    I managed to solve the first error I have got but now I have another one.

    When I run the test, I get the password but it does not write in the vault… I get an error:

    [Error] ERROR: Invoke-RestMethod : {“error”:{“code”:”MethodNotAllowed”,”message”:”HTTP PUT not allowed”}}

    1. Hi Anton, did you copy/paste the test body? If so, you may want to overwrite (really type them yourself) ALL the double quotes in that body and try again…

    2. I’m having the same error.

      ERROR: Invoke-RestMethod : {“error”:{“code”:”MethodNotAllowed”,”message”:”HTTP PUT not allowed”}}

      I have confirmed that the vault access policy is set to allow the function, so I’m not sure what’s left to adjust?

    3. Having the same issue. Retyped the double quotes in the new-localadmin.ps1 file. Still no luck. Permissions are set correctly as well. If I run the test it works, just not from the powershell script. Any luck solving this issue yet?

    4. Hi Anthon

      Change:
      $password = Invoke-RestMethod -Uri $uri -Method POST -Body $body -ErrorAction Stop

      Into:
      $password = Invoke-RestMethod -Uri $uri -Method POST -Body $body -ContentType “application/json” -ErrorAction Stop

      Cheers from Belgium 😉

    1. Hi Derek,

      If a Local Admin account was created with this script, you will be able to log on to the device with those credentials when no internet connectivity is available.

      Best regards,

      John

  14. I have the same error as well.
    Invoke-RestMethod : {“error”:{“code”:”MethodNotAllowed”,”message”:”HTTP PUT not allowed”}}

  15. Amazing stuff!

    Can anyone advise how this can be scheduled to reset the password every 30 days like the on-prem implementation of LAPS would?

    Is this going to reset the password EVERY time the New-LocalAdmin powershell script executes? Or is it just a one-off per unique user that logs onto the machine?

    1. Hi House,

      If you need password rotation, you need to wrap the PowerShell script in a Win32 app and additionally create a registry key or something including the date the password was last updated. Build a detection script that checks if it can find a date in the registry that is within the 30 days, if not, the Win32 app will trigger to run again (and thus create a new password).

      Best regards,

      John

  16. Hi John,

    Great process and something that is needed, have votes for your user voice request.

    I’ve got this working and logging into the device has created the secret in the vault, I’m just wondering how often this scripts runs as I’ve restarted a couple of times since but no updates to the secret in the vault?

    Thanks,
    Gary.

    1. Hi Gary,

      The PowerShell scripts in Microsoft Intune are based on “run and forget”. When it succeeds, it doesn’t re-run. If you make changes to the PowerShell script in the Microsoft Endpoint Manager admin portal, the script will trigger again on all assigned devices. There is no rotation by default.

      Best regards,

      John

  17. We use it. But on some computers, when we try to run something from the local administrator, we get an error “user must change password at first logon”. How we can resolve it?

Leave a Reply

Your email address will not be published. Required fields are marked *