chore: add setup script to modify security defaults
This commit is contained in:
@ -1,25 +0,0 @@
|
||||
# Connect to Microsoft Graph
|
||||
Connect-MgGraph -Scopes "DeviceManagementConfiguration.ReadWrite.All", "Organization.Read.All", "Group.ReadWrite.All", "Directory.ReadWrite.All" -NoWelcome
|
||||
|
||||
$policies = Get-ChildItem ./policies/compliance
|
||||
|
||||
ForEach ($policie in $policies) {
|
||||
$PolicieName = $policie.name
|
||||
|
||||
$JsonData = Get-Content -Path ./policies/compliance/$PolicieName -Raw
|
||||
$JsonDataUpdated = $JsonData -replace '\$tenantId', $tenantId
|
||||
$PolicyObject = $JsonDataUpdated | ConvertFrom-Json
|
||||
|
||||
try {
|
||||
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies" # Using the beta version
|
||||
$response = Invoke-MgGraphRequest -Method POST -Uri $uri -Body ($PolicyObject | ConvertTo-Json -Depth 10)
|
||||
Write-Host "✅ $PolicieName - successfully imported!"
|
||||
#$response
|
||||
} catch {
|
||||
Write-Error "❌ An error occurred while importing the policy: $_"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Disconnect from Graph
|
||||
$null = Disconnect-Graph -ErrorAction SilentlyContinue
|
@ -10,7 +10,7 @@ if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Beta)) {
|
||||
|
||||
|
||||
# Connect to Microsoft Graph
|
||||
Connect-MgGraph -Scopes "DeviceManagementConfiguration.ReadWrite.All", "Organization.Read.All", "Group.ReadWrite.All", "Directory.ReadWrite.All" -NoWelcome
|
||||
Connect-MgGraph -Scopes "Device.ReadWrite.All","DeviceManagementConfiguration.ReadWrite.All", "Organization.Read.All", "Group.ReadWrite.All", "Directory.ReadWrite.All" -NoWelcome
|
||||
|
||||
# Get Tenant ID
|
||||
$tenant = Get-MgOrganization
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Ensure Microsoft.Graph module is installed
|
||||
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
|
||||
Write-Host "Installing Microsoft.Graph module..."
|
||||
Write-Host "✅ Installing Microsoft.Graph module..."
|
||||
Install-Module -Name Microsoft.Graph -Force -AllowClobber
|
||||
}
|
||||
|
||||
# Ensure ExchangeOnlineManagement module is installed
|
||||
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
|
||||
Write-Host "Installing ExchangeOnlineManagement Module..."
|
||||
if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
|
||||
Write-Host "✅ Installing ExchangeOnlineManagement Module..."
|
||||
Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber
|
||||
}
|
||||
|
||||
|
||||
# Import necessary modules
|
||||
Import-Module Microsoft.Graph.Authentication
|
||||
Import-Module Microsoft.Graph.Applications
|
||||
@ -21,7 +20,40 @@ Connect-MgGraph -Scopes "Application.ReadWrite.All", "Directory.ReadWrite.All",
|
||||
|
||||
# Application details
|
||||
$Name = "GraphAPI"
|
||||
$Scope = "Application.ReadWrite.All", "DeviceManagementApps.ReadWrite.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All", "Group.ReadWrite.All", "Policy.ReadWrite.ApplicationConfiguration", "User.ReadWrite.All"
|
||||
$Scope = "Application.ReadWrite.All", "DeviceManagementApps.ReadWrite.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All", "Group.ReadWrite.All", "Policy.ReadWrite.ApplicationConfiguration", "User.ReadWrite.All", "Policy.ReadWrite.AuthenticationMethod"
|
||||
|
||||
# -------------------------------
|
||||
# Check for existing apps with the same name
|
||||
# -------------------------------
|
||||
Write-Host "🔍 Checking for existing applications named '$Name'..."
|
||||
$existingApps = Get-MgApplication -All | Where-Object { $_.DisplayName -eq $Name }
|
||||
|
||||
if ($existingApps) {
|
||||
Write-Host "⚠️ Found $($existingApps.Count) existing application(s) with this name. Removing them..."
|
||||
foreach ($existingApp in $existingApps) {
|
||||
try {
|
||||
# Remove associated Service Principal first
|
||||
$sp = Get-MgServicePrincipal -Filter "AppId eq '$($existingApp.AppId)'" -ErrorAction SilentlyContinue
|
||||
if ($sp) {
|
||||
Remove-MgServicePrincipal -ServicePrincipalId $sp.Id -Confirm:$false
|
||||
Write-Host " ➜ Removed Service Principal: $($sp.Id)"
|
||||
}
|
||||
|
||||
# Remove the application
|
||||
Remove-MgApplication -ApplicationId $existingApp.Id -Confirm:$false
|
||||
Write-Host " ➜ Removed Application: $($existingApp.Id)"
|
||||
} catch {
|
||||
Write-Warning " ❌ Failed to remove application $($existingApp.Id): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Optional pause to ensure deletion propagates
|
||||
Write-Host "⏳ Waiting for application deletion..."
|
||||
Start-Sleep -Seconds 10
|
||||
} else {
|
||||
Write-Host "✅ No existing applications found with name '$Name'."
|
||||
}
|
||||
|
||||
|
||||
# Fetch Microsoft Graph Service Principal
|
||||
$graphSp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
|
||||
@ -76,11 +108,10 @@ if (-not $app) {
|
||||
Write-Host "✅ Application Created: $($app.Id)"
|
||||
|
||||
# Wait for the application to propagate
|
||||
Start-Sleep -Seconds 10
|
||||
Write-Host "⏳ Waiting for application propagation..."
|
||||
Start-Sleep -Seconds 10
|
||||
|
||||
# Create Service Principal
|
||||
Write-Host "Creating Service Principal..."
|
||||
$servicePrincipal = New-MgServicePrincipal -AppId $app.AppId
|
||||
|
||||
if (-not $servicePrincipal) {
|
||||
@ -147,32 +178,6 @@ Write-Host "🔑 Client ID: $($app.AppId)"
|
||||
Write-Host "🕵️♂️ Client Secret: $($clientSecret.SecretText)"
|
||||
Write-Host "----------------------------------"
|
||||
|
||||
#Connect-ExchangeOnline -ShowBanner:$false
|
||||
|
||||
$scope = "https://graph.microsoft.com/.default"
|
||||
$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
|
||||
|
||||
# Create the body for the token request
|
||||
$body = @{
|
||||
client_id = "$($app.AppId)"
|
||||
scope = $scope
|
||||
client_secret = "$($clientSecret.SecretText)"
|
||||
grant_type = "client_credentials"
|
||||
}
|
||||
|
||||
# Request the token
|
||||
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -ContentType "application/x-www-form-urlencoded" -Body $body
|
||||
$accessToken = $tokenResponse.access_token
|
||||
|
||||
# Use the token in subsequent requests
|
||||
$headers = @{
|
||||
Authorization = "Bearer $accessToken"
|
||||
}
|
||||
$templates = Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/deviceManagement/templates" -Headers $headers
|
||||
|
||||
# Output the templates
|
||||
$templates
|
||||
|
||||
$null = Disconnect-Graph -ErrorAction SilentlyContinue
|
||||
$null = Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
|
||||
|
||||
|
265
Setup.ps1
Normal file
265
Setup.ps1
Normal file
@ -0,0 +1,265 @@
|
||||
function Get-OrCreateGroup {
|
||||
param(
|
||||
[string]$DisplayName,
|
||||
[string]$MailNickname
|
||||
)
|
||||
$group = Get-MgGroup -Filter "displayName eq '$DisplayName'" -ConsistencyLevel eventual -CountVariable count
|
||||
if ($group) {
|
||||
Write-Host "🚫 Group '$DisplayName' already exists. Using existing GroupId: $($group.Id)"
|
||||
return $group
|
||||
}
|
||||
else {
|
||||
Write-Host "✅ Creating group '$DisplayName'..."
|
||||
return New-MgGroup -BodyParameter @{
|
||||
DisplayName = $DisplayName
|
||||
MailEnabled = $false
|
||||
MailNickname = $MailNickname
|
||||
SecurityEnabled = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Function to get or create CA policy
|
||||
function Get-OrCreatePolicy {
|
||||
param(
|
||||
[string]$DisplayName,
|
||||
[hashtable]$BodyParams
|
||||
)
|
||||
$policy = Get-MgIdentityConditionalAccessPolicy -Filter "displayName eq '$DisplayName'"
|
||||
if ($policy) {
|
||||
Write-Host "🚫 Policy '$DisplayName' already exists. Skipping creation."
|
||||
return $policy
|
||||
}
|
||||
else {
|
||||
Write-Host "✅ Creating policy '$DisplayName'..."
|
||||
return New-MgIdentityConditionalAccessPolicy -BodyParameter $BodyParams
|
||||
}
|
||||
}
|
||||
|
||||
function Get-OrCreateCountryNamedLocations {
|
||||
param(
|
||||
[string[]]$Countries # ISO codes
|
||||
)
|
||||
|
||||
$namedLocationIds = @()
|
||||
|
||||
foreach ($c in $Countries) {
|
||||
$displayName = "Country - $c"
|
||||
|
||||
# Check if it exists
|
||||
$existing = Get-MgIdentityConditionalAccessNamedLocation -Filter "displayName eq '$displayName'" -ErrorAction SilentlyContinue
|
||||
if ($existing) {
|
||||
Write-Host "🚫 Named Location for $c already exists: $($existing.Id)"
|
||||
$namedLocationIds += $existing.Id
|
||||
}
|
||||
else {
|
||||
# Create new Named Location
|
||||
$newLoc = New-MgIdentityConditionalAccessNamedLocation -BodyParameter @{
|
||||
"@odata.type" = "#microsoft.graph.countryNamedLocation"
|
||||
DisplayName = $displayName
|
||||
CountriesAndRegions = @($c)
|
||||
IncludeUnknownCountriesAndRegions = $false
|
||||
}
|
||||
Write-Host "✅ Created Named Location for $c $($newLoc.Id)"
|
||||
$namedLocationIds += $newLoc.Id
|
||||
}
|
||||
}
|
||||
|
||||
return $namedLocationIds
|
||||
}
|
||||
|
||||
Connect-MgGraph -NoWelcome -Scopes `
|
||||
"Policy.ReadWrite.SecurityDefaults", `
|
||||
"Policy.ReadWrite.ConditionalAccess", `
|
||||
"Policy.Read.All", `
|
||||
"Group.ReadWrite.All", `
|
||||
"Directory.ReadWrite.All", `
|
||||
"UserAuthenticationMethod.ReadWrite.All", `
|
||||
"Application.ReadWrite.All"
|
||||
|
||||
|
||||
Write-Host "Connected to Microsoft Graph"
|
||||
|
||||
Write-Host "✅ Disabling Security Defaults..."
|
||||
Update-MgPolicyIdentitySecurityDefaultEnforcementPolicy -BodyParameter @{ isEnabled = $false }
|
||||
|
||||
Write-Host "ℹ️ Checking/Creating Exclusion Groups..."
|
||||
|
||||
$teamsRoomsGroup = Get-OrCreateGroup -DisplayName "TeamsRoomsExclusions" -MailNickname "TeamsRoomsExcl"
|
||||
$osTravelGroup = Get-OrCreateGroup -DisplayName "OSTravelExclusions" -MailNickname "OSTravelExcl"
|
||||
|
||||
Write-Host "ℹ️ Teams Rooms GroupId: $($teamsRoomsGroup.Id)"
|
||||
Write-Host "ℹ️ OS Travel GroupId: $($osTravelGroup.Id)"
|
||||
|
||||
Write-Host "ℹ️ Checking/Creating Conditional Access Policy for Country Blocking..."
|
||||
|
||||
$AllowedCountries = @("AU","NZ","US","GB","DE","FR") # Add any ISO codes here
|
||||
|
||||
$allowedLocationIds = Get-OrCreateCountryNamedLocations -Countries $AllowedCountries
|
||||
|
||||
# 2. Wait for consistency
|
||||
foreach ($locId in $allowedLocationIds) {
|
||||
$retries = 0
|
||||
do {
|
||||
Start-Sleep -Seconds 2
|
||||
$check = Get-MgIdentityConditionalAccessNamedLocation -Filter "id eq '$locId'" -ErrorAction SilentlyContinue
|
||||
$retries++
|
||||
} while (-not $check -and $retries -lt 10)
|
||||
|
||||
if (-not $check) { throw "⚠️ Named Location $locId not found in directory." }
|
||||
}
|
||||
|
||||
$countryPolicyParams = @{
|
||||
DisplayName = "Block countries except allowed"
|
||||
State = "disabled"
|
||||
Conditions = @{
|
||||
Users = @{
|
||||
IncludeUsers = @("All")
|
||||
ExcludeGroups = @($osTravelGroup.Id)
|
||||
|
||||
}
|
||||
Locations = @{
|
||||
IncludeLocations = @("All")
|
||||
ExcludeLocations = $allowedLocationIds
|
||||
}
|
||||
Applications = @{
|
||||
IncludeApplications = @("All")
|
||||
ExcludeApplications = @()
|
||||
}
|
||||
ClientAppTypes = @("all")
|
||||
}
|
||||
GrantControls = @{
|
||||
Operator = "OR"
|
||||
BuiltInControls = @("block")
|
||||
}
|
||||
}
|
||||
|
||||
$Policy = Get-OrCreatePolicy -DisplayName "Block countries except allowed" -BodyParams $countryPolicyParams
|
||||
|
||||
# Define the policy from your JSON
|
||||
$policyParams = @{
|
||||
DisplayName = "Require multifactor authentication for admins"
|
||||
State = "enabled"
|
||||
Conditions = @{
|
||||
Users = @{
|
||||
IncludeRoles = @(
|
||||
"62e90394-69f5-4237-9190-012177145e10" # Global Administrator
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d" # Privileged Role Administrator
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c" # Security Reader
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de" # Exchange Administrator
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9" # SharePoint Administrator
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8" # Security Administrator
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe" # Helpdesk Administrator
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1" # User Administrator
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f" # Authentication Administrator
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3" # Conditional Access Administrator
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7" # Security Operator
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d" # Reports Reader
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13" # Billing Administrator
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814" # Cloud Application Administrator
|
||||
)
|
||||
ExcludeRoles = @(
|
||||
"d29b2b05-8046-44ba-8758-1e26182fcf32" # Directory Synchronization Accounts
|
||||
)
|
||||
}
|
||||
Applications = @{
|
||||
IncludeApplications = @("All")
|
||||
ExcludeApplications = @()
|
||||
}
|
||||
ClientAppTypes = @("all")
|
||||
}
|
||||
GrantControls = @{
|
||||
Operator = "OR"
|
||||
BuiltInControls = @("mfa")
|
||||
}
|
||||
}
|
||||
|
||||
# Create the Conditional Access policy
|
||||
$policy = Get-OrCreatePolicy -DisplayName "Require multifactor authentication for admins" -BodyParams $policyParams
|
||||
|
||||
# Define the policy from your JSON
|
||||
$policyParams = @{
|
||||
DisplayName = "Require multifactor authentication for all users"
|
||||
State = "enabled"
|
||||
Conditions = @{
|
||||
Users = @{
|
||||
IncludeUsers = @("All")
|
||||
ExcludeGroups = @($osTravelGroup.Id)
|
||||
ExcludeRoles = @(
|
||||
"d29b2b05-8046-44ba-8758-1e26182fcf32" # Directory Synchronization Accounts
|
||||
)
|
||||
}
|
||||
Applications = @{
|
||||
IncludeApplications = @("All")
|
||||
ExcludeApplications = @()
|
||||
}
|
||||
ClientAppTypes = @("all")
|
||||
}
|
||||
GrantControls = @{
|
||||
Operator = "OR"
|
||||
BuiltInControls = @("mfa")
|
||||
}
|
||||
}
|
||||
|
||||
# Create the Conditional Access policy
|
||||
$policy = Get-OrCreatePolicy -DisplayName "Require multifactor authentication for all users" -BodyParams $policyParams
|
||||
|
||||
# Define the policy from your JSON
|
||||
$policyParams = @{
|
||||
DisplayName = "Require multifactor authentication for Azure management"
|
||||
State = "enabled"
|
||||
Conditions = @{
|
||||
Users = @{
|
||||
IncludeUsers = @("All")
|
||||
ExcludeRoles = @(
|
||||
"d29b2b05-8046-44ba-8758-1e26182fcf32" # Directory Synchronization Accounts
|
||||
)
|
||||
}
|
||||
Applications = @{
|
||||
IncludeApplications = @(
|
||||
"797f4846-ba00-4fd7-ba43-dac1f8f63013" # Azure Management
|
||||
)
|
||||
ExcludeApplications = @()
|
||||
}
|
||||
ClientAppTypes = @("all")
|
||||
}
|
||||
GrantControls = @{
|
||||
Operator = "OR"
|
||||
BuiltInControls = @("mfa")
|
||||
}
|
||||
}
|
||||
|
||||
# Create the Conditional Access policy
|
||||
$policy = Get-OrCreatePolicy -DisplayName "Require multifactor authentication for Azure management" -BodyParams $policyParams
|
||||
|
||||
# Get Graph token for REST calls
|
||||
$token = (Get-MgContext).AccessToken
|
||||
$headers = @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" }
|
||||
|
||||
# Get all users
|
||||
$users = Get-MgUser -All -Property "id,userPrincipalName,accountEnabled,strongAuthenticationRequirements"
|
||||
|
||||
foreach ($u in $users) {
|
||||
# Skip disabled accounts
|
||||
if (-not $u.AccountEnabled) {
|
||||
Write-Host "🚫 Skipping disabled user $($u.UserPrincipalName)"
|
||||
continue
|
||||
}
|
||||
|
||||
# Skip if MFA already not set
|
||||
if (-not $u.StrongAuthenticationRequirements -or $u.StrongAuthenticationRequirements.Count -eq 0) {
|
||||
Write-Host "➖ No per-user MFA set for $($u.UserPrincipalName) – skipping"
|
||||
continue
|
||||
}
|
||||
|
||||
# Clear per-user MFA
|
||||
$body = @{ strongAuthenticationRequirements = @() } | ConvertTo-Json -Depth 3
|
||||
Invoke-RestMethod -Method PATCH `
|
||||
-Headers $headers `
|
||||
-Uri "https://graph.microsoft.com/v1.0/users/$($u.Id)" `
|
||||
-Body $body
|
||||
|
||||
Write-Host "✅ Disabled per-user MFA for $($u.UserPrincipalName)"
|
||||
}
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
# Check if the Microsoft Graph PowerShell SDK is installed
|
||||
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
|
||||
Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force
|
||||
}
|
||||
|
||||
# Check if the Microsoft Graph PowerShell SDK is installed
|
||||
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Beta)) {
|
||||
Install-Module -Name Microsoft.Graph.Beta -Scope CurrentUser -Force
|
||||
}
|
||||
|
||||
|
||||
# Connect to Microsoft Graph
|
||||
Connect-MgGraph -Scopes "DeviceManagementConfiguration.ReadWrite.All", "Organization.Read.All", "Group.ReadWrite.All", "Directory.ReadWrite.All" -NoWelcome
|
||||
|
||||
$params = @{
|
||||
"@odata.type"= "#microsoft.graph.windowsUpdateForBusinessConfiguration"
|
||||
"displayName"= "Win - OIB - WUfB Drivers - Ring 1 - Pilot - v3.0"
|
||||
"description"= "" # Empty string if description is not needed
|
||||
"automaticUpdateMode"= "autoInstallAndRebootAtMaintenanceTime" # You can adjust based on requirements
|
||||
"qualityUpdatesDeferralPeriodInDays"= 7 # Example deferral value
|
||||
"featureUpdatesDeferralPeriodInDays"= 30 # Example deferral value
|
||||
"allowMicrosoftUpdate"= $true # Enables updates for Microsoft products
|
||||
"roleScopeTagIds"= @("0") # Example role scope tag ID
|
||||
"assignments"= @() # Empty array for assignments
|
||||
}
|
||||
|
||||
# Use the display name from the parameters for identification
|
||||
$ring = $params.displayName
|
||||
|
||||
# Create the driver update profile
|
||||
$null = New-MgDeviceManagementDeviceConfiguration -BodyParameter $params
|
||||
Write-Host "✅ Successfully created $ring"
|
||||
|
||||
|
||||
$null = Disconnect-Graph -ErrorAction SilentlyContinue
|
Reference in New Issue
Block a user