AzureAD Conditional Access ‘Block All except’ selected Apps and MyApps

Azure AD conditional access policies are a key part of O365 security. A common requirement is to be able to apply a policy to all applications, with exceptions for a few selected allocations. The most common use case is as a ‘block’ rule, with the exceptions used to whitelist applications. While you can do this already, by using ‘All’ in the list of applications the policy applies to, this has the side effect of blocking the MyApps portal. Microsoft have recently added MyApps as an application that can be added to CA policies, but it currently (June 2021) doesn’t work. This link is to the Microsoft feedback site where people are asking for this to be fixed :

https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/19738183-support-conditional-access-for-myapps-microsoft-co

The only way to create a policy is to add all of the applications that should be blocked to the ‘includeApplications’ list of the policy manually. This quickly gets impossible to managed in a tenant with many applications. It also means that where there are several different policies, with exceptions for different groups or conditions, they must all be updated manually when a new Enterprise Application is added to the tenant.

This workaround uses Powershell to make calls to the Microsoft Graph REST API to maintain the list of applications in the CA Policies.

WARNING: It is possible to create a CA Policy that blocks ALL access to a Tenant, including Global Administrators. As this code manipulates CA policies it should be used with caution!

Setup

There are a few things that you need to setup in your AzureAD to get this to work:

  • Create an Application Registration, used to assign permissions for the Powershell script to access the Graph REST API
  • Note the Application ID
  • Create a Client secret and make a note of it. (AzureAD only displays this at creation)
  • Assign the following Application API Permissions, in the Graph section:
    • Application.Read.All
    • Policy.Read.All
    • Policy.ReadWrite.ConditionalAccess
  • Create an Enterprise Application. The name doesn’t actually matter, but I used ‘AAAL – Automated All Apps List’. Make a note of the Application ID
  • Create a Conditional Access policy for testing. I’d suggest creating it in Report only mode. Add the AAAL application to the Included applications and one or two applications to ‘Exclude’.
  • Edit the script and update the App Id (for the app registration), Client secret and the AAAL Application ID.

##############################################################################
# Variables
##############################################################################

$AppId = '< Application ID from Azure AD App Registration>'
$AppSecret = '< Application ID from Azure AD App Registration>'

# App ID of AAAL Enterprise App
$AAALiD = "< Application ID of AAAL Enterprise Application"

# Add System.Web for urlencode
Add-Type -AssemblyName System.Web

##############################################################################
# Garph REST API Token
##############################################################################
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$Scope = "https://graph.microsoft.com/.default"

# Create body
$Body = @{
    client_id = $AppId
	client_secret = $AppSecret
	scope = $Scope
	grant_type = 'client_credentials'
}

# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    Body = $Body
    Uri = $Url
}

# Request the token
$Request = Invoke-RestMethod @PostSplat

# Create header
$Header = @{
    Authorization = "$($Request.token_type) $($Request.access_token)"
}

##########################################################################
# Applications
##########################################################################
# Fetch all Applications from AAD
$Uri = "https://graph.microsoft.com/v1.0/applications"
$Applications = $null
$Applications = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

##########################################################################
# CA Policies
##########################################################################
# Fetch Guest CA Policies
$Uri = "https://graph.microsoft.com/V1.0/identity/conditionalAccess/policies"
$CAPolicies = $null
$CAPolicies = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"
foreach($CAPolicy in $CAPolicies.value){
# Search for AAAL and Expand
$Include = $null
$Include = $CAPolicy.conditions.applications.includeApplications
if($Include -contains $AAALiD){
    $Exclude = $null
    $Exclude = $CAPolicy.conditions.applications.excludeApplications
    # Comment out the following line Add to exiting list, Uncomment to replace
    # e.g. if you want O365 Apps in the list as well as Ent Aps 
    $Include = @($AAALiD)
    foreach($Application in $Applications.value){
        if($Exclude -notcontains $Application.appId){
            # Add if not on the include list already
            if($Include -notcontains $Application.appId){
                  $Include += $Application.appId
                  }
            }
        }
    write-output "Updating Policy " $CApolicy.displayName
    #Patch the CA Policy
    $Uri = "https://graph.microsoft.com/V1.0/identity/conditionalAccess/policies/" + $CAPolicy.id 
    $RequestBody = @{
                    conditions =@{
                                applications =@{
                                              includeApplications = @( $Include )
                                               }
                                 }
                    }                
    $RequestBodyJSON = $RequestBody | ConvertTo-Json -Depth 3 
    $PatchedPolicy = Invoke-RestMethod -Method Patch -Headers $Header -Uri $Uri -Body $RequestBodyJSON -ContentType "application/json"
    }
}

Sharp Dust Sensor Update – Adding a Temperature sensor.

Bruce Ford suggested that I looked at a paper on using these sensors for PM monitoring. One point of interest is that the sensor output has some link to ambient temperature, so this update adds a TMP36 sensor to the stripboard.

It’s a fairly simple addition, one 3-pin device and a wire to the A1 input on the Nano.

Parts List:

In addition to the parts listed in the previous post : http://connectranet.co.uk/wp/2016/09/23/sharp-dust-sensor-arduino-nano-and-2×16-lcd

  • TMP36 Sensor (TO-92 Package)

http://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf

 Diagram:

 

Add the TMP36 to the strip board…

Pin 1  to row 8 of the stripboard (+5v)

Pin2 to row 7 (temp sensor output)

Pin3 to row 9 (Gnd)

then add a wire from Row 7 to A1 on the Nano.

 

The Temperature can then be read from A1 using AnolgRead(1).

The output voltage of the TMP36 is 10mV / degree + 0.5v so 25c = 0.750v

temp = ((analogRead(1) * 5 /1024)-0.5)*100;

using the Temperature reading

I don’t have any information or calibration data that suggests how to adjust the reading to account for temperature so the next step is to try  sealing the sensor with clear tape to keep the dust level inside constant, then vary the temperature to see how this varies the dust sensor output.

 

 

 

 

 

 

 

Sharp Dust Sensor – Arduino Nano and 2×16 LCD

Parts List:

  • Sharp Dust Sensor GP2Y1010AU0F
  • Arduino Nano (or pretty much any other 5v Arduino)
  • 2×16 LCD
  • Connector for sharp sensor (JST S6B-ZR-SM4A-TF)
  • Stripboard (25 x 9, lengthways copper strips)
  • 150 ohm resistor
  • 220uF capacitor
  • 10k trimmer pot
  • ribbon cable
  • wire
  • (Optional) Small 5v cooling fan
  • (Optional) 3 pin male header, for fan connection
  • (Optional) 9 pin female header, makes testing easier

Diagram:

dust-sensor-stripboard-v01_bb

Wiring Details

Stripboard Connections:

Row 1 (+5v)

  • LCD Pin 2 (VDD)
  • LCD Pin 15 (A)
  • Sensor Pin 6
  • Arduino +5v
  • Wire jumper to row 8
  • Resistor lead 1
  • Trimmer Pin 1

Row 2 (LCD Contrast)

  • LCD Pin 3 (VO)
  • Trimmer Pin 2

Row 3 (GND)

  • Trimmer Pin 3
  • Wire jumper to row 9

Row 4 (Sensor Output)

  • Sensor Pin 5
  • Arduino A0

Row 5 (Sensor LED Supply)

  • Sensor Pin 1
  • Resistor lead 2
  • Capacitor +ve

Row 6 (Sensor LED control)

  • Sensor Pin 3
  • Arduino D2

Row 7 (Not Connected)

  • Male header Pin 1 (NC)

Row 8 (+5v, for Fan)

  • Wire jumper to row 1
  • Male header Pin 2 (+5v)

Row 9 (GND)

  • LCD Pin 1 (VSS)
  • LCD Pin 5 (R_W)
  • LCD Pin 16 (K)
  • Sensor Pin 2
  • Sensor Pin 4
  • Arduino GND
  • Male header Pin 3 (GND)
  • Capacitor -ve
  • Wire jumper to row 3

Arduino to LCD Connections:

  • Arduino D7 to LCD Pin 4 (RS)
  • Arduino D8 to LCD Pin 6 (E)
  • Arduino D9 to LCD Pin 11 (D4)
  • Arduino D10 to LCD Pin 12 (D5)
  • Arduino D11 to LCD Pin 13 (D6)
  • Arduino D12 to LCD pin 14 (D7)

 

Sharp Dust Sensor with Arduino – ISR Code to correct timing

This code works on an Arduino Nano connected to a Sharp Dust Sensor and a 2 line LCD. It shows how to get the correct timing for the pulse that controls the LED in the sensor. The display shows the minimum and maximum voltages measured from the sensor output, in 5 second sampling periods.

This version doesn’t do any conversion of the output voltage to a dust density.

 

/*
 Interface to Sharp GP2Y1010AU0F Particle Sensor
 Modified from Program by Christopher Nafis 
 Written April 2012

 Changes (Sept 2016):
 LCD Display
 ISR code for ADC
 
 */
#include <LiquidCrystal.h>
#include <stdlib.h>

const byte adcPin = 0;
// Variables that can be changed by an ISR must be declared as volatile
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;

int dustPin=0;
int ledPower=2;
int delayTime=280;
int delayTime2=40;
float offTime=9580;
int dustVal=0;
int i=0;
int c=0;
int adcMin=1023;
int adcMax=0;
float ppm=0.0;
char s[32];
float voltage = 0.0;
float vMin = 0.0;
float vMax = 0.0;
float dustdensity = 0.0;
float ppmpercf = 0.0;

// Define the pins used by the LCD
LiquidCrystal lcd(7,8,9,10,11,12);

void setup(){ 
 pinMode(ledPower,OUTPUT);
 lcd.begin(16,2);
 lcd.print("Starting");
 delay(1000);
 i=0;
 c=0;
 ppm=0.0;
 // set the analog reference (high two bits of ADMUX) and select the
 // channel (low 4 bits). this also sets ADLAR (left-adjust result)
 // to 0 (the default).
 ADMUX = bit (REFS0) | (adcPin & 0x07);
 }

// ADC complete Interupt Service Routine
ISR (ADC_vect)
 {
 byte low, high;
 // read the result registers and store value in adcReading
 // Must read ADCL first
 low = ADCL;
 high = ADCH;
 adcReading = (high << 8) | low;
 adcDone = true; 
 } 
// end of ADC_vect
 

void loop(){
 do {
 i=i+1;
 digitalWrite(ledPower,LOW); // power on the LED
 delayMicroseconds(delayTime); // wait 280uS
 // Check the conversion hasn't been started already
 if (!adcStarted)
 {
 adcStarted = true;
 // start the conversion
 ADCSRA |= bit (ADSC) | bit (ADIE);
 } 
 delayMicroseconds(delayTime2); // wait another 40uS or so to give 320uS pulse width
 digitalWrite(ledPower,HIGH); // turn the LED off
 // give the ADC time to complete then process the reading
 delayMicroseconds(100); 
 
 if (adcDone) 
// adcDone is set to True by the ISR, called when the conversion is complete.
 {
 adcStarted = false;
 if (adcMin > adcReading) adcMin = adcReading; // 5 second Minimum
 if (adcMax < adcReading) adcMax = adcReading; // 5 second Maximum
 c = c+1; // count of readings
 adcDone = false;
 }
 
 delayMicroseconds(offTime); // wait for the remainder of the 10mS cycle time
 
 } while (i <500); //repeat for 5 seconds
 
 // Convert ADC max/min readings in last 5 seconds (0-1023) to voltages
 vMin = adcMin * 5.0 / 1024;
 vMax = adcMax * 5.0 / 1024;
 String dataString = "vMin:";
 dataString += dtostrf(vMin, 9, 4, s);
 lcd.setCursor(0,1);
 lcd.print(dataString);
 dataString = "vMax:";
 dataString += dtostrf(vMax, 9, 4, s);
 lcd.setCursor(0,0);
 lcd.print(dataString);
 i=0;
 c=0;
 adcMin=1023;
 adcMax=0;
 
 }

Sharp Dust Sensor with Arduino – Getting the timing right.

There are various articles and posts about using the Sharp Gp2Y1010AU0F sensor for air quality monitoring. The original seems to be Chris Nafis in 2012 http://www.howmuchsnow.com/arduino/airquality/, but there are several other posts covering this interesting and cheap device.

The Sharp data sheet for the sensor explains how it works and has diagrams showing the required driving circuit, details of the timing required and a graph of the typical output.

https://www.sparkfun.com/datasheets/Sensors/gp2y1010au_e.pdf

There’s also a slightly more informative application note available from Sharp.

http://www.sharp-world.com/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf

Both of these show the timing required for the sensor LED as a 10mS pulse cycle time with a .32mS pulse width. They also state that the output voltage should be measured .28mS after the start of the LED pulse.

The original code, on which all the other versions I’ve seen are based, used the analogRead() function to measure the output voltage from the sensor using the Arduino’s analouge to digital converter (ADC). In outline the sampling loop of the code goes like this…

  • Turn LED ON
  • Wait 280uS
  • Sample sensor output voltage
  • Wait 40uS
  • Turn LED OFF
  • Wait 9680uS
  • Repeat

At first glance this would seem to meet the timings specified by Sharp. However, analogRead() takes 13 clock cycles to complete an ADC conversion and these are ADC clock cycles which are prescaled from the 16MHz system clock by a factor of 128. This means that analogRead() takes at least 104uS to return a value, so the pulse width ends up being far in excess of the 320uS required. The following screen shot from an oscilloscope shows this.

dustsensor1

The yellow trace shows the LED pulse (0v=LED On, +5v =LED Off), and the  cyan trace the output from the sensor.

Having the longer pulse does seem to distort the output, though starting analogRead() at 280uS would still be measuring the maximum value so perhaps it doesn’t change the result. To check if this is the case, I used a signal generator to get the exact timing specified for the LED which, as can be seen below, makes the peak value a little higher.

dustsensorint1

So, having established that the timing does have an impact on the sensor output values I looked at what could be done to get the timing right. The ADC prescaling factor defaults to 128 but this can be reduced. This makes the conversion faster but at the expense of accuracy. When used with a prescaling factor of 64 analogRead() takes around 52uS, so starting the conversion at 280uS would allow the LED to be turned off at about 350uS. Although the main problem is the time that analogRead() takes, all the  other instructions in the code take time too. Fiddling around with the settings and checking the effect on the ‘scope about the closest to the 280/320 ‘ideal’ settings are acheived with a delay of 250uS between turning the LED on and the analogRead(), then turning the LED off as soon as it returns(i.e. post read delay of zero).

Although you can’t get the timing right using the analogRead() function, it can be done with a 16MHz Arduino and using the default ADC prescaling of 128 for full accuracy. The solution is to attach an interrupt service routine to the ADC completed interrupt to read the value. This means that control returns while the ADC conversion is in progress so the LED can be turned off at the right time. The conversion can be started at the right time, but the ADC has to be controlled directly rather than by calling analogRead(). Arduino code showing how to do this will be in my next post.