Jump to content


anyweb

Retire My PC - a self-service app to secure company data on old computers

Recommended Posts

Introduction

By now we should all be familiar with Windows Autopilot and how it is used to provision new computers, as explained below in Microsoft's diagram.

windows autopilot graphic.png

For every new computer delivered via the Windows Autopilot process there's usually an old or obsolete computer waiting to be retired or re-sold. Those old computers still have life left in them and are frequently sold back to the vendor who sold them as new 3 years previously, either to be re-used or re-sold around the world. However those old devices may still contain sensitive company data on them and you want to protect that from prying eyes.

Today your company may have an existing process where on site support staff clear the BitLocker protectors from the TPM chip to make extraction of that data as difficult as possible. The Retire My PC app aims to provide self-service ability to the end-user to retire their old PC quickly, easily and with minimum fuss and of course, to do so in a secure manner thereby protecting your companies data.

In this blog post I'll guide you through setting it up in your own environment.

RetireMyPc app.PNG

The Retire My PC app.

This app has the following features:

  • stops the ConfigMgr client agent service
  • stops the MBAM agent service
  • rotates the BitLocker key (optional)
  • WIPEs the BCD registry entries (optional)
  • joins a workgroup
  • clears the TPM protectors
  • adds a record of all this to Azure Tables
  • emails the log to a support inbox

Requirements: Before you get started please ensure that you've already setup a Sendgrid account (for sending emails) as I've explained in Step 4 of this blog post.

In this blog post you'll do the following actions:

  • Create an Azure Resource Group
  • Create a storage account
  • Copy access key connection string
  • Create an Azure table
  • Create a function app]
  • Configure the function app settings
  • Create some httptriggers
  • deploy the app via ConfigMgr
  • test and verify on a computer

Step 1. Create a Resource Group

login to https://portal.azure.com and click on Create a resource,

Azure - Create a Resource.PNG

in the search field type in Resource group and select Create Resource Group.

create resource group.PNG

Give it a suitable name like RetireMyPc and select a suitable region.

creating a resource group - select region.PNG

Step 2. Create a storage account

In the newly created resource group, click on the Create button, select Marketplace and search for Storage Account using the text field provided.

create marketplace.PNG

when you find Storage account, select it and then click Create.

storage account.PNG

In the Create a storage account wizard, give it a unique name, select the resource group you previously created and finally, select your applicable region as shown below (highlighted in yellow). When done, click on Review + create followed by Create.

creating a storage account.PNG

 

Step 3. Copy access key connection string

After creating the storage account, select it and then click on Access keys in the Security + Networking section and then click on Show keys and copy the Connection string of key1 as we'll need it later.

show access keys.PNG

Step 4. Create a table

In the Storage account you created in Step 2, click on the Tables under the Data storage heading in the left pane. Next, click on + Table to add a new table.

plus table.PNG

 

give the table a  name like devicestatus and then click OK to add the table.

table name.PNG

Next click on Storage Explorer (preview) and select the newly created devicestatus table. If it appears blank (appeared blank for me with Google Chrome) try using Mozilla Firefox or another web browser.

storage explorer (preview).png

 

As you can see there is no data in the table yet.

Step 5. Create a function app

In the RetireMyPc resource group, click on + Create to create a new resource, search for Function App and go ahead and Create a function app.

Create function app.PNG

In the Create Function App wizard, make sure to select the RetireMyPc resource group in the resource group drop down menu, then make sure to name the Function App Name, next select PowerShell core as the Runtime stack and finally select an applicable region.

create a function app wizard.PNG

and then click through the wizard, don't forget to point to the Storage Account you created previously before  clicking on Create to complete this wizard. After the Function app deployment is complete your Resource group should look something like this.

resource group populated.PNG

Step 6. Configure function app

After creating the RetireMyPc Function App, select Configuration from the Settings menu on the left. Click on + New application setting.

Configuration + New application setting.PNG

 

Give the new setting a suitable name like RetireMyPc_setting and paste in the Connection string you copied in step 3.

 

application setting edited.png

click OK to the add/Edit application setting wizard, it should now appear in the list of application settings.

app setting added.PNG

finally, click on Save application setting and then click on Continue when prompted.

Save application setting.PNG

Step 7. create some HttpTriggers

In this step you will create 2 Http Triggers for use by the RetireMyPc app. Select the RetireMyPc Function App you created above, and select Functions in the Functions menu, then click on + Add.

functions + add.PNG

Create a new Http Trigger from the template provided and click on Add when done.

create new http trigger.PNG

After the http trigger is created, click on Code + Test and paste in the following Powershell code to replace what was there previously.

#######################################################################################################################################
# use this code in a http trigger as part of a function app
# for more details see https://www.windows-noob.com/forums/topic/22489-retire-my-pc-a-self-service-app-to-secure-company-data-on-old-computers/
# Niall Brady,2021/06/27
#######################################################################################################################################

using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata, $inputTable)
$Tenant = "windowsnoob.com"
$triggerName = "ADD data TO Azure Table"
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$ComputerName = $Request.Query.ComputerName
if (-not $ComputerName) {
    $ComputerName = $Request.Body.ComputerName
}
$UserName = $Request.Query.UserName
if (-not $UserName) {
    $UserName = $Request.Body.UserName
}
$Model = $Request.Query.Model
if (-not $Model) {
    $Model = $Request.Body.Model
}
$Manufacturer = $Request.Query.Manufacturer
if (-not $Manufacturer) {
    $Manufacturer = $Request.Body.Manufacturer
}
$Serial = $Request.Query.Serial
if (-not $Serial) {
    $Serial = $Request.Body.Serial
}
$DateRetired = $Request.Query.DateRetired
if (-not $DateRetired) {
    $DateRetired = $Request.Body.DateRetired
}
$Status = $Request.Query.Status
if (-not $Status) {
    $Status = $Request.Body.Status
}

    $a = Get-Date
    $body = $body + "$a ------------------------------------`n"
    $a = Get-Date
    $body = $body + "$a Starting the following trigger:  '$triggerName'.`n"
    $a = Get-Date
    $body = $body + "$a Connected to tenant: '$Tenant'.`n"

if ($ComputerName) {
    $a = Get-Date
    $body = $body + "$a Adding this computer to Azure Tables: '$ComputerName'.`n"
#}

#fix the date
$NewDate = $(get-date($DateRetired) -UFormat '+%Y-%m-%dT%H:%M:%S.000Z')

$a = Get-Date
$body = $body + "$a Get next row key based on the last entry in the Storage Table....`n"
$nextRowKey=$([int]$(($inputTable.RowKey|measure -Maximum).Maximum)+1)
$a = Get-Date
$body = $body + "$a nextRowKey = '$nextRowKey'.`n" # this will be the row key that we insert in this operation

# Input row into DB
$tableStorageItems = @()

#insert the NEW data
$tableStorageItems += [PSObject]@{
  PartitionKey = "1"  
  RowKey = $nextRowKey.ToString()
  ComputerName = $ComputerName
  UserName = $UserName
  Model = $Model
  Manufacturer = $Manufacturer
  Serial = $Serial
  DateRetired = $NewDate
  Status = $Status
}
# insert the data
$Result = Push-OutputBinding -Name outputTable -Value $tableStorageItems
$body = $body + " Adding the data returned (usually blank...): $Result `n"

}
$a = get-date
$body = $body + "$a Exiting Azure function.`n"
$a = Get-Date
$body = $body + "$a ------------------------------------`n"

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

Note: In the code above, replace $Tenant = "windows-noob.com" with your tenant name and then click on Save.

 

Step 8. Integrate the HttpTrigger

Select the HttpTrigger and click on Integration and then click on + Add input under Inputs.

+ add input.PNG

 

In the window that appears on the right of your console, for Binding Type select Azure Table Storage. For the Table name enter the name of the table created earlier which was devicestatus. For the storage account connection select the RetireMyPc_setting from the drop down menu as shown below.

Create Input for http trigger integration.PNG

click on OK.

Repeat the above process for the HttpTrigger Output.

create outpurt.PNG

Once done, your HttpTrigger1 integration should look like this with both Azure Table Storage (inputTable) and Azure Table Storage (outputTable) configured.

httptrigger1 integration.PNG

Step 9. Test HttpTrigger

In the Code + Test section, click on Test/Run and paste in the following Input.

{
"ComputerName": "MYCOMPUTER",
"UserName": "niall",
"Model": "Surface Book 2",
"Manufacturer": "Microsoft",
"Serial": "1234567890",
"DateRetired": "2021-06-27T14:20:06.000Z",
"Status": "OK"
}

If you did everything correctly you should see the following type of output in the right pane. Notice how it states your tenant name and "Adding this computer to Azure Tables:"

http trigger 1 output.PNG

Finally, check the Azure Tables in your storage account and you should see the data you just added. Success !

tables populated.PNG

Step 10. Add/Configure second HttpTrigger

Now that you know how this works, please add an additional Http Trigger called HttpTrigger2 with the following code:

#######################################################################################################################################
# use this code in a http trigger as part of a function app
# for more details see https://www.windows-noob.com/forums/topic/22489-retire-my-pc-a-self-service-app-to-secure-company-data-on-old-computers/
# Niall Brady,2021/06/27
#######################################################################################################################################

using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata, $inputTable)
$Tenant = "windowsnoob.com"
$triggerName = "READ data FROM Azure Table"
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$nextRowKey = $Request.Query.nextRowKey
if (-not $nextRowKey) {
    $nextRowKey = $Request.Body.nextRowKey
}
$CheckComputerName = $Request.Query.CheckComputerName
if (-not $CheckComputerName) {
    $CheckComputerName = $Request.Body.CheckComputerName
}

    $a = Get-Date
    $body = $body + "$a ------------------------------------`n"
    $a = Get-Date
    $body = $body + "$a Starting the following trigger:  '$triggerName'.`n"
    $a = Get-Date
    $body = $body + "$a Connected to tenant: '$Tenant'.`n"

if ($nextRowKey -and $CheckComputerName) {
    $a = Get-Date
    $body = $body + "$a Checking the following row: '$nextRowKey'.`n"
    $body = $body + "$a Looking for this computername: '$CheckComputerName'.`n"
   
#}

#Put all table rows into $table
$table=""
foreach($row in $inputTable){
    $table+="$($row.PartitionKey) - $($row.RowKey) - $($row.ComputerName) - $($row.UserName) - $($row.Model) - $($row.Manufacturer) - $($row.Serial) - $($row.DateRetired)- $($row.Status)
   "
}
# print out the results...
# $body = $body + $table

#validate section
#$body = $body + "Validate: $($($inputTable|Where-Object -Property RowKey -EQ 12).ComputerName)"
$a = Get-Date
$found = $($($inputTable|Where-Object -Property RowKey -EQ $nextRowKey).ComputerName)
$body = $body + "$a ComputerName found: $found`n"
if ($found -match $CheckComputerName) {
    $a = Get-Date
    $body = $body + "$a FOUND a MATCH :-)`n"
}
else
{
    $a = Get-Date
    $body = $body + "$a sorry, did not find a match :-(`n"
}
}
$a = get-date
$body = $body + "$a Exiting Azure function.`n"
$a = Get-Date
$body = $body + "$a ------------------------------------`n"

# show the output to the browser...Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

and configure the integration the same way as you did with the first trigger.

httptrigger2 configured.PNG

 

The second trigger can be tested with a different input, pasted here.

{
"nextRowKey": "1",
"CheckComputerName": "MYCOMPUTER"
}

Confirm that it finds the previously added data before moving on, notice how it states "FOUND A MATCH", this proves it is working.

found a match.PNG

 

Step 11. Download the script

Note: The SecureWipe.ps1 script used in this guide can only be downloaded by logged-on members of windows-noob.com, so if you haven't already done so, create an account and login.

    Download the RetireMyPC Powershell script (txt file) and save it as SecureWipe.ps1

    SecureWipe.txt

     

    Step 12. edit the script

    The script won't work until you've made some edits. In Azure, locate HttpTrigger1 and click on Get Function URL, copy it.

    httptrigger1 get function url.PNG

    Edit line 428 of SecureWipe.ps1 and insert the copied URL of httptrigger1

    insert httptrigger1 URL.PNG

    Edit line 463 for the copied url of httptrigger2

    line 463 for http trigger 2.PNG

    Edit line 595 and insert your sendgrid API key

    sendgrid api key line.PNG

    Modify the following lines otherwise i'll be getting your emails...

    line 578 and 579.PNG

    Step 12. Deploy the app via ConfigMgr

    Locate ServiceUI.exe from the C:\Program Files\Microsoft Deployment Toolkit\Templates\Distribution\Tools\x64 folder in MDT files as explained in Step 2 here. For licensing reasons I cannot include it here.

    Create a package/program with the following text

    ServiceUI.exe -process:explorer.exe %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File securewipe.ps1

    Your finished package (or app, it's up to you to decide which...) should contain at least securewipe.ps1 and ServiceUI.exe (the install.cmd and uninstall.cmd are not needed here).

    package of files.png

    Deploy the package with a purpose of AVAILABLE so that it shows up in your users Software Center

    retire my pc in software center.png

    Job done, go ahead and start testing it, to see the app in action please review my video here.

    Troubleshooting

    If you want to test this in a non-destructive way locate the following variable $BrickTheDevice and set it to $false

    brickthedevice.PNG

    The app logs to C:\Users\<USERNAME>\AppData\Local\Temp\win.ap.securewipe.log

     


     

     

    Share this post


    Link to post
    Share on other sites

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest
    Reply to this topic...

    ×   Pasted as rich text.   Paste as plain text instead

      Only 75 emoji are allowed.

    ×   Your link has been automatically embedded.   Display as a link instead

    ×   Your previous content has been restored.   Clear editor

    ×   You cannot paste images directly. Upload or insert images from URL.

    Loading...


    ×
    ×
    • Create New...