Microsoft Graph API

Microsoft Graph API

Back in November Microsoft released v1.0 of graph API. Along with it is a large library of documentation. I have been hesitant about making a blog post about Graph for a while because the community seemed to be split on how PowerShell should interact with it. Now the dust is settling, I want to show you how I have worked with Graph API. This method allows for use without any modules. This means it translates to other apps as power automate. My approach is designed for security and separation of duty. However, it’s a long-winded process. In the next few blog posts, I will go over the Employee Directory Graph API I created for another company. The first step is to create a registered app. This post will go through the process of creating the App both manually and PowerShell.

Create an Azure App – Manual

The first step is to log into your Azure tenant. Then click on the Azure Active Directory on the left-hand side.

Then on the left-hand side, Click the App Registrations. You will be greeted by the owned applications. All manually added. If you add an app via PowerShell it will show up under all applications. Now click the New registration button.

Inside the Register An Application window, we will fill in the Name of the application. This is where you will select the supported account type. The way I do it is client by client. This the client owns the application. I don’t want the application to pull data from a different client. So, I radio check the “Account in this organizational directory only **** – single tenant. Then I click register.

After you click register, you will be brought to the application page. Here you want to gather some pieces of information and document them. You will want the Application ID and the Directory ID. Next, we will click the Certificate and Secrets on the left-hand side.

In the certificates and secrets screen, we want to click the New Client Secret. The reason we are doing this is that we want security. This will create that security. Without the key this generates others will not be able to access this application remotely.

What I like to do at this point is create the key with the Application name and the word Key. In this example, we are using Employee Directory Key. The maximum time span you can select is 24 months, aka 2 years. This means in 2 years, you will have to regenerate a key and update your applications accordingly. Once you select the time span you want this key to live, click add.

Now you have the Key created, this is the ONE AND ONLY CHANCE to grab the key value. After this there is no way that I am aware of that you can grab that key. So make sure you click the copy to clipboard button and store it somewhere safe. Best to be in the same location as the application ID and the Tenant ID.

Now you have created an Azure Registered Application manually, let us look at how to do it with PowerShell.

Create an Azure App – PowerShell

The Powershell process for this is much faster and less prone to human errors.

Connect-AzureAD
$AppName = (Read-Host -Prompt "Applicaiton Name")
$azureADAppReg = New-AzureADApplication -DisplayName $AppName -AvailableToOtherTenants $false
$start = (Get-Date)
$end = (Get-Date).AddYears(2)
$Info = New-AzureADApplicationPasswordCredential -CustomKeyIdentifier "($AppName) Key" -ObjectId $azureADAppReg.ObjectId -EndDate $end -StartDate $start
[pscustomobject]@{
    AppName  = $appname
    AppID    = $azureADAppReg.AppID
    Keyname  = "$Appname Key"
    KeyValue = $Info.Value
}
Disconnect-AzureAD

First we connect to Azure AD using the Connect-AzureAD commandlet. Then we grab the name of the application from the user with the read-host commandlet. Then we create the application using the New-azureadapplication command. We give it the name of the application we want. And we say it’s not available to other tenants. Make sure you place the return info into a variable to be used later. This will produce your app ID and object ID.

Next we setup the date and times for a year by creating a start and end time with get-date commands. We will use those two dates to create the secret key. The commandlet is new-azureadapplicationpasswordcredential. The Custom Key Identifier will be the application name plus the word key. We reference the Object ID from the previous azure command for the object ID. We tell it the end date and the start dates. This will produce the secret key. So make sure once again to put that invariable.

Next, we create a custom ps object to display our information. For the App name its the app name. For the AppID we want the first commands appid. For the key name, we use the app name and the word key. Finally, for the key-value, we use the second azure command value output.

Finally, we disconnect from azure AD with the command Disconnect-AzureAd.

Assign Graph API Permissions

Sadly, I have not found a PowerShell method to add graph API permissions. The reason I believe this is the case is because of the nature of these permissions. They must have consent. Basically, you have to click the button manually. So let us add the user.read.all permissions to our employee directory application. Go to the application in your tenant. On the left-hand side, click the API permissions button. Then click Add Permissions

The type of application permissions will appear. You will want to search out Graph API and click on it. Luckily it’s the first item on the list.

Once you click the Microsoft Graph permissions button. You will be greeted with a choice of Delegated Permissions or Application Permissions. Delegated permissions require a user to sign in to the account for the application to run. The Application permissions do not. If you want this to be an automated process, select application. Under select permissions search out the User.Read.All. This will allow the application to read user information like phone numbers, addresses, usernames, etc. It doesn’t give permissions to read emails or anything else like that. Check the box for the permissions you want and click Add permissions.

Once you add the permissions you will notice under the status, “Not Granted for”. This means the application can’t use those permissions yet. You will need to click Grant admin consent for… to add the permissions. Once you click that button, you will be prompted with a confirm, just click the confirm to allow access. Once you allow access, the status will turn green and you will be ready to go.

Now we have granted access, we are free to create with the application. Stay tuned for next week.

Powershell – Open File Dialog

Powershell – Open File Dialog

I was building a script for my manager the other day and inside the script, he wanted us to triple check the naming of a file path. He went as far as to have us copy the file path from the browser. Paste it, Then click rename on the file itself and copy that and paste it. It was extremely time-consuming and micromanagement. I finally got tired of it as it was about 5 file paths. Using the 80/20 rule, I made a function for this within the script that opened the file dialog box and allowed the user to select the file and have it programmatically push the names into the Variable.

The Script

function Get-SHDOpenFileDialog {
    [cmdletbinding()]
    param (
        [string]$InitialDirectory = "$Env:USERPROFILE",
        [string]$Title = "Please Select A file",
        [string]$Filter = "All files (*.*)| *.*",
        [switch]$MultiSelect
    )
    Add-Type -AssemblyName System.Windows.Forms
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog 
    $FileBrowser.InitialDirectory = "$InitialDirectory"
    $FileBrowser.Filter = "$Filter"
    $FileBrowser.Title = "$Title"
    if ($MultiSelect) {
        $FileBrowser.Multiselect = $true
    } else {
        $FileBrowser.Multiselect = $false
    }

    $FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true })) | Out-Null
    $FileBrowser.Filenames
    $FileBrowser.dispose()
}

The Breakdown

Let us break down this script and explain it a little better. Our parameters are as the following:

  • InitialDirectory [string]- This is the directory we will open the Open Dialog in. By default we use the user’s profile.
  • Title [string]- This will be the title on the Open Dialog box. We have this because when you execute the function mutiple times in a single script, this can give some direction.
  • Filter [string]- This is the filter that you will sort by. We have it defaulting to all files with all extensions. If you want to guide the user even more, you can change the filter to something like “XML Files (*.xml)| *.xml” for xml files.
  • Multiselect [switch]- This determines if you want to limit the system to a single file select, which is default or allow mulitple select. This way if you want to select 5 files at once, you can. This is more for the advanced user. If you want to guide the user, then don’t flip this switch.

Now we have our parameters, it’s time to build our assembly. We do this by adding the add-type command. We are loading the Forms assembly.

Add-Type -AssemblyName System.Windows.Forms

Now we have loaded the assembly, we need to create the dialog box. To do this we will create a new-object. System > Windows > Forms > OpenFileDialog. From there we build out the properties.

$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog 

Now the object is created, we need to add the properties. The first three properties we are filling are from our parameters. The InitialDirectory, Filter, and Title.

$FileBrowser.InitialDirectory = $InitialDirectory
$FileBrowser.Filter = $Filter
$FileBrowser.Title = "$Title"

Then we ask if the MultiSelect flag is checked. If it is we set the property to true, if not, we set it to false.

if ($MultiSelect) {
    $FileBrowser.Multiselect = $true
} else {
    $FileBrowser.Multiselect = $false
}

Next, we need to show the dialog box. One thing I ran into was the dialog box would open behind my VS code. In VB.net we set forms to TopMost. I tried a few variations of this with the open file dialog box, but this dialog box doesn’t have the TopMost property. So, what we do is open a form inside the form to force the form to the front. The System > Windows > Forms > Form has the TopMost property. So, inside the showdialog method, we plop the new-object into it.

$FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true }))

However, there is catch! This command outputs the button you click. So if you click cancel, you get a cancel as output, if you click a file and click ok, you get an OK and the file name as the next part of the array it produces. The way to resolve this is pipping the command into an out-null.

$FileBrowser.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true })) | Out-Null

Now we have selected the file/files, we need to display them. The data is stored in the filenames property. So we use the $FileBrowser.Filenames to display that information.

$FileBrowser.Filenames

Finally, we need to dispose of the open file dialog. This way we don’t have hundreds of invisible forms clogging up our system.

$FileBrowser.dispose()

I hope that makes since. Here are some Examples

Examples

PS C:\> Get-SHDOpenFileDialog -Title "Select the required files" -MultiSelect -InitialDirectory c:\temp  

The Output:

C:\temp\tempshow.csv
C:\temp\Moo.txt

Example with Filter

PS C:\> Get-SHDOpenFileDialog -Title "Select the CSV file" -MultiSelect -InitialDirectory c:\temp -Filter "CSV Files (*.csv)| *.csv"

The Output:

C:\temp\tempshow.csv
C:\temp\ACC_Active.csv

Fact Prank with Powershell

Fact Prank with Powershell

I love a good prank. Sometimes pranks need to challenge us. I coded a good prank and it requires a scheduled task and a little bit of PowerShell. The idea behind the prank is the computer will alert you with a beep, then read a random fact to you. Then it closes off with a set of beeps. It looks at this over and over and over again. We will call this script facts.ps1.

Facts.ps1

$random = Get-Random -minimum 20 -maximum 60
Start-Sleep -s $random
[system.console]::beep(600, 2000)
Add-Type -AssemblyName System.Speech
$voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
$Facts = (Invoke-WebRequest -Uri https://therandomadmin.com/nextcloud/index.php/s/9rmSoM2ppY5ggia/download).tostring() -split "[`n]"
$random = Get-Random $Facts
$voice.Speak($random)

Let’s break it down so you can appreciate the greatness of this command. The first thing we do is get a random number between 20 and 60 seconds. We do this so the anti-virus doesn’t attack it from starting by the scheduled task time. Then we start sleeping for that amount of time. Once we are done sleeping, we sound the alarm. The [System.Console]::beep(Pitch,Length) does this part.

Next, we import the speech assembly to read off the Fact. That is done by the Add-Type command. Then we set up the voice by using the new-object command and create the System > Speech > Synthesis > Speech Synthesizer. I remember back in high school this was amazing new technology. I wish it would grow.

Now we get the fact sheet from the web. You can download this file and host it wherever you want. I stole it from various sites myself. We grab it with the invoke-webrequest command. Then we convert it to a string. We split said string up by each new string. Now we have the information, we grab a random item by using the get-random command. Then, we dump that into the $voice we made a while ago. It speaks it out to the user. Finally, 3 final beeps to let the user know it’s done and over.

Now we need to take the next steps:

  • Get the script on the target computer
  • Setup a scheduled task to have it run.

To get it on the other persons computer you can put it into a next cloud and download it from there. Use the invoke-webrequest and download the file to the c:\temp\facts.ps1. Then we create two variables, taskname, and facts. The task name is going to be called “Facts” and the Facts needs to be wherever you downloaded the file.

Invoke-WebRequest -Uri "https://rndadhdman.com/index.php/s/mTA6YaDZ7YXAo9E/download" -OutFile c:\temp\Facts.ps1
$taskName = "Facts"
$Facts = "C:\temp\Facts.ps1"

Then we are going to set up action for our scheduled task with the New-ScheduledTaskAction commandlet. The execute will be powershell.exe. The arguments will be execution policy bypass, Noprofile, nologo, noninteractive, windows style will be hidden, and the file is of course the $facts.

$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-executionpolicy bypass -noprofile -nologo -noninteractive -WindowStyle Hidden -file $Facts -Force"

Next we create the trigger with the New-ScheduledTaskTrigger commandlet. This is where the fun can be exciting. We can do this one, daily, minutely, oh yeah. We only want to do this once, so we will select once in this case, but we want it to repeat every minute or so. We do that with the new-timespan commandlet.

$taskTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(10) -RepetitionInterval (New-TimeSpan -Minutes 1)

Finally, we put it together with the register-scheduledtask commandlet. The task name will be the $taskname, the action will be $action, and the trigger will be $tasktrigger.

Register-ScheduledTask -TaskName $taskName -Action $Action -Trigger $taskTrigger -Force

For the rest of the day, their computer will read off odd strange facts.

I hope you enjoyed it.

Batch – Chrome and Self-built URLs

Batch – Chrome and Self-built URLs

A previous job of mine had a unique issue. Unstandardized google chromes and self-generated icons. Both of these issues caused a lot of heartaches. Today I will show you how I solved the issue of unstandardized google chromes with batch. At the time the company’s default browser was IE. The site that needed chrome was an in-house product. I was pushing towards standardization of Google chrome 64-bit enterprise. However, management did fully agree with my thinking. So a compromise was created. I created a shortcut link on the desktop that pointed to a batch file. This batch file determined which version of Chrome was installed and loaded that chrome to the special internal page. Here is the script for this icon:

The Script

if exist "C:\Program Files\Google\Chrome\Application\chrome.exe" (
	start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Google\Chrome\Application\chrome.exe" "https://www.bolding.us"
) else (
	if exist "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" (
		start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "https://www.bolding.us"
	) else (
		msg "%username%" Chrome Not Installed
	)
)

It’s a simple batch script. We first ask if the 64-bit path exists. If it does, then we open chrome to a hyperlink. If it doesn’t, we ask if the x86 version exists. If it does, we open chrome to the hyperlink. If it doesn’t, we tell the user it doesn’t exist. Pretty simple? Yeah, it’s very simple. So, the desktop shortcut would point to the batch file. The batch file would execute and load up the chrome page.

Taking it another step

This process helped a lot when it came to the next issue. The next issue was each location had a custom-hosted site on that location’s Server. There were tablets that had to float between each location. This means, that the icon on the tablet must open the custom website link at center 1 and at center 101. Thankfully each center had it’s own subnet. Each server that hosted this site hosted it at 10.x.x.5. This means all I had to do is find out the Subnet and make a .5 address in the link. Believe it or not, that’s not as hard as you would think. We are taking the script from above and pretty much piping the new URL into it.

The Script

@echo off
rem All i need to do now is grab only the IP address with batch. 
rem ipconfig | find "IPv4"
for /f "usebackq tokens=2 delims=:" %%f in (`ipconfig ^| findstr /c:"IPv4 Address"`) do set "ip=%%f"

for /f "tokens=1-4 delims=. " %%a in ("%ip%") do (
set octetA=%%a
set octetB=%%b
set octetC=%%c
set octetD=%%d
)

if exist "C:\Program Files\Google\Chrome\Application\chrome.exe" (
	start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files\Google\Chrome\Application\chrome.exe" "http://%octetA%.%octetB%.%octetC%.5/Login.aspx"
) else (
	if exist "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" (
		start "CMD" /D "C:\Windows\System32\" /max "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "http://%octetA%.%octetB%.%octetC%.5/Login.aspx"
	) else (
		msg "%username%" Chrome Not Installed
	)
)
exit

Inside the script you see that we are breaking down the IPv4 address. So, if you are running IPv6, this isn’t going to work. We place each Octect into it’s on veraible. Then we build the site at the link level. Bamn, done with the script.

The next part is to create the icon. Which i’ll leave that to you. I hope this little brain twister is helpful for what it is worth.

Group Policy How-To – Folders

Group Policy How-To – Folders

Whenever we get a new client, one of the first things we do is create a new folder on the root of C called temp. If that client has a group policy we create a policy just for that. This guide is a simple one, it’s how to create a folder with group policy. This post will build on other posts. Let’s get started.

Creating the Policy

  1. Start Group Policy.
  2. Expand to your Group Policy Objects
  3. Right Click and Select new Policy.
  4. Name the Policy. I suggest, “Folder – Temp”
  5. Right Click the “Folder – Temp” Policy and click edit
  6. Naviage to Computer Configuration > Preferences > Windows Settings > Folders.
  7. Right Click in the right plane
  8. Click New > Folder
  9. In the Path location, enter the path you want. In this case it will be c:\temp.
  10. Click OK.

Now we have the policy made, we need to decide who is getting the policy. We want every computer to get this policy. Closeout the edit group policy and click on the policy. Then click on the delegation tab. Here we see our delegations. Right now every user and computer will execute this policy. We want to change that.

Delegating the Policy

  1. Click on the policy in question “Folder – Temp”
  2. Click the delegation tab.
  3. Click Authenticated users
  4. Click advanced
  5. Click autenticated users in this window.
  6. Uncheck apply group policy.
  7. Click the add button
  8. Search for Domain Computers
  9. Give Domain computers Read and Apply by checking the check box on read and apply group policy.
  10. Click Apply
  11. Click Ok.

Linking the policy

Now the policy will only apply to Domain Computers inside whatever OU you link it to. Now we need to link it to an OU. Since we are doing this to all computers on the domain, we can link it at the top level. We do this by right-clicking the domain and clicking the Link existing gpo object. We search for the GPO in question and click it. Then we click ok. Within the next few days, the c:\temp folder will be created.

That is how you create a folder on all computers in your domain. If you have any questions, feel free to reach out.