├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── script-request.md ├── pull_request_template.md ├── scripts │ └── check-script-updates.js └── workflows │ ├── daily-script-notifications.yml │ └── script-analysis.yml ├── .gitignore ├── Azure_Deployment.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── azure-deployment-templates.json ├── azure-templates ├── app-deployment-failure-alert-azure-deployment.json ├── apple-token-expiration-alert-azure-deployment.json ├── check-apple-token-validity-azure-deployment.json ├── check-bitlocker-keys-azure-deployment.json ├── check-policy-changes-azure-deployment.json ├── check-unassigned-policies-azure-deployment.json ├── cleanup-autopilot-devices-azure-deployment.json ├── device-compliance-drift-alert-azure-deployment.json ├── get-application-inventory-report-azure-deployment.json ├── get-device-compliance-report-azure-deployment.json ├── get-devices-by-scopetag-azure-deployment.json ├── get-duplicate-applications-azure-deployment.json ├── get-stale-devices-azure-deployment.json ├── rotate-bitlocker-keys-azure-deployment.json ├── stale-device-cleanup-alert-azure-deployment.json ├── sync-devices-azure-deployment.json └── wipe-devices-azure-deployment.json ├── grant-permissions-managed-identity.ps1 ├── permissions.json ├── scripts ├── apps │ ├── get-application-inventory-report.ps1 │ └── get-duplicate-applications.ps1 ├── compliance │ └── get-device-compliance-report.ps1 ├── devices │ ├── cleanup-autopilot-devices.ps1 │ ├── get-devices-by-scopetag.ps1 │ └── get-stale-devices.ps1 ├── monitoring │ ├── check-apple-token-validity.ps1 │ ├── check-applecare-warranty-status.sh │ ├── check-available-msupdate-updates.sh │ ├── check-bitlocker-keys.ps1 │ ├── check-msupdate-status.sh │ ├── check-network-requirements.sh │ ├── check-policy-changes.ps1 │ ├── check-unassigned-policies.ps1 │ ├── check-xprotect-status.sh │ ├── last-reboot.sh │ └── local-admins.sh ├── notification │ ├── README.md │ ├── app-deployment-failure-alert.ps1 │ ├── apple-token-expiration-alert.ps1 │ ├── device-compliance-drift-alert.ps1 │ └── stale-device-cleanup-alert.ps1 ├── operational │ ├── sync-devices.ps1 │ └── wipe-devices.ps1 └── security │ └── rotate-bitlocker-keys.ps1 ├── templates ├── NOTIFICATION_TEMPLATE_GUIDE.md ├── README.md ├── macos-script-template.sh ├── notification-script-template.ps1 └── script-template.ps1 └── testresults.json /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report a bug in an existing script 4 | title: '[BUG] Script Name - Brief Description' 5 | labels: ['bug'] 6 | assignees: '' 7 | --- 8 | 9 | ## 🐛 Bug Report 10 | 11 | ### Script Information 12 | **Which script is affected?** 13 | - Script name: 14 | - Script path: `scripts/category/script-name.ps1` 15 | - Script version: 16 | 17 | ### Bug Description 18 | **Describe the bug:** 19 | 20 | 21 | ### Expected Behavior 22 | **What should happen?** 23 | 24 | 25 | ### Actual Behavior 26 | **What actually happens?** 27 | 28 | 29 | ### Steps to Reproduce 30 | **How can we reproduce this issue?** 31 | 1. Run the script with parameters: `script-name.ps1 -Parameter "value"` 32 | 2. 33 | 3. 34 | 4. 35 | 36 | ### Error Messages 37 | **What error messages do you see?** 38 | ``` 39 | Paste error messages here 40 | ``` 41 | 42 | ### Environment Information 43 | **PowerShell Version:** 44 | ``` 45 | $PSVersionTable 46 | ``` 47 | 48 | **Modules Installed:** 49 | - Microsoft.Graph.Authentication: 50 | - Other relevant modules: 51 | 52 | **Azure/Intune Environment:** 53 | - Tenant type: (Production/Test) 54 | - Intune license: 55 | - Number of devices: 56 | 57 | ### Parameters Used 58 | **What parameters did you use when running the script?** 59 | ```powershell 60 | ./script-name.ps1 -Parameter1 "value1" -Parameter2 "value2" 61 | ``` 62 | 63 | ### Permissions 64 | **What permissions/roles do you have?** 65 | - Azure AD Role: 66 | - Intune Role: 67 | - Graph API Permissions: 68 | 69 | ### Screenshots 70 | **If applicable, add screenshots to help explain your problem** 71 | 72 | ### Additional Context 73 | **Any other information that might be helpful:** 74 | 75 | 76 | ### Workaround 77 | **Have you found any workaround for this issue?** 78 | 79 | 80 | --- 81 | 82 | **Before submitting:** 83 | - [ ] I have checked that this issue hasn't already been reported 84 | - [ ] I have tested this in a lab/test environment first 85 | - [ ] I have the minimum required permissions for this script 86 | - [ ] I have reviewed the script documentation and examples -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/script-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Script Request 3 | about: Suggest a new script for the IntuneAutomation project 4 | title: '[SCRIPT REQUEST] ' 5 | labels: ['enhancement', 'script-request'] 6 | assignees: '' 7 | --- 8 | 9 | ## 📋 Script Request 10 | 11 | ### Script Purpose 12 | **What should this script accomplish?** 13 | 14 | 15 | ### Category 16 | **Which category does this script belong to?** 17 | - [ ] Operational (device operations, sync, restart, etc.) 18 | - [ ] Apps (application management, deployment, reporting) 19 | - [ ] Compliance (compliance reporting, remediation) 20 | - [ ] Security (security policies, conditional access) 21 | - [ ] Devices (device lifecycle, enrollment, configuration) 22 | 23 | ### Use Case 24 | **Describe your use case:** 25 | 26 | 27 | ### Expected Functionality 28 | **What should the script do?** 29 | 30 | 31 | - [ ] Function 1 32 | - [ ] Function 2 33 | - [ ] Function 3 34 | 35 | ### Input Parameters 36 | **What parameters should the script accept?** 37 | 38 | 39 | - Parameter 1: (description) 40 | - Parameter 2: (description) 41 | - Parameter 3: (description) 42 | 43 | ### Expected Output 44 | **What should the script output or return?** 45 | 46 | 47 | ### Graph API Permissions 48 | **Do you know which Graph API permissions would be required?** 49 | 50 | 51 | ### Priority 52 | **How important is this script to you?** 53 | - [ ] Critical - Needed urgently 54 | - [ ] High - Would be very helpful 55 | - [ ] Medium - Nice to have 56 | - [ ] Low - When time permits 57 | 58 | ### Additional Context 59 | **Any other information that would be helpful:** 60 | 61 | 62 | ### Volunteering 63 | **Are you willing to contribute this script yourself?** 64 | - [ ] Yes, I can write this script 65 | - [ ] Yes, with some guidance 66 | - [ ] No, I need someone else to write it 67 | 68 | --- 69 | 70 | **Note:** Please check existing scripts in the repository to ensure this functionality doesn't already exist. -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request: [Category] Script Name 2 | 3 | ## 📋 Description 4 | 5 | **What does this script do?** 6 | 7 | 8 | **Problem/Use Case:** 9 | 10 | 11 | ## 📁 Script Details 12 | 13 | **Category:** `scripts/[category]/` 14 | **Script Name:** `script-name.ps1` 15 | **Version:** `1.0` 16 | 17 | **Required Permissions:** 18 | 19 | - Permission.ReadWrite.All 20 | - Permission.Read.All 21 | 22 | **Minimum Role:** 23 | 24 | 25 | ## ✅ Testing Checklist 26 | 27 | - [ ] **Lab Testing**: Script has been thoroughly tested in a lab environment 28 | - [ ] **Parameter Testing**: All parameter combinations have been tested 29 | - [ ] **Error Handling**: Error scenarios have been tested and handled gracefully 30 | - [ ] **Permissions**: Script works with minimum required permissions 31 | - [ ] **Rate Limiting**: Script handles Graph API throttling appropriately 32 | - [ ] **Edge Cases**: Common edge cases have been considered and tested 33 | 34 | ## 🔍 Code Quality Checklist 35 | 36 | - [ ] **Template Used**: Started with `templates/script-template.ps1` 37 | - [ ] **Header Complete**: All header sections are filled out correctly 38 | - [ ] **CmdletBinding**: Script uses `[CmdletBinding()]` 39 | - [ ] **Parameter Validation**: Proper parameter attributes and validation 40 | - [ ] **Error Handling**: Comprehensive try-catch blocks implemented 41 | - [ ] **Graph Connection**: Proper authentication and disconnection 42 | - [ ] **Helper Functions**: Includes pagination helper function 43 | - [ ] **Comments**: Code is well-commented and readable 44 | - [ ] **Naming**: Consistent PascalCase naming convention 45 | 46 | ## 📖 Documentation Checklist 47 | 48 | - [ ] **Synopsis**: Clear one-line description 49 | - [ ] **Description**: Detailed explanation of functionality 50 | - [ ] **Examples**: At least 2 working examples provided 51 | - [ ] **Parameters**: All parameters documented with help text 52 | - [ ] **Notes**: Important information and requirements listed 53 | - [ ] **Tags**: Appropriate category tags assigned 54 | - [ ] **Changelog**: Version history documented 55 | 56 | ## 🚀 Usage Examples 57 | 58 | **Example 1:** 59 | ```powershell 60 | ./script-name.ps1 -Parameter1 "value1" 61 | ``` 62 | 63 | 64 | **Example 2:** 65 | ```powershell 66 | ./script-name.ps1 -Parameter1 "value1" -Parameter2 "value2" 67 | ``` 68 | 69 | 70 | ## 🔒 Security Considerations 71 | 72 | - [ ] **Least Privilege**: Requests only minimum required permissions 73 | - [ ] **Input Validation**: All user inputs are validated 74 | - [ ] **No Hardcoded Secrets**: No credentials or sensitive data hardcoded 75 | - [ ] **Safe Operations**: Script performs safe operations only 76 | 77 | ## 📊 Test Results 78 | 79 | **Environment Tested:** 80 | - PowerShell Version: 81 | - Graph Module Version: 82 | - Tenant Type: (Lab/Test) 83 | - Device Count: 84 | 85 | **Test Summary:** 86 | 87 | 88 | ## 🔄 Breaking Changes 89 | 90 | - [ ] **No Breaking Changes**: This script doesn't break existing functionality 91 | - [ ] **New Dependencies**: List any new module dependencies 92 | - [ ] **Permission Changes**: List any new permission requirements 93 | 94 | ## 📝 Additional Notes 95 | 96 | 97 | 98 | ## 🔗 Related Issues 99 | 100 | Closes #[issue-number] 101 | Related to #[issue-number] 102 | 103 | --- 104 | 105 | **Reviewer Notes:** 106 | 107 | 108 | ## 📋 Reviewer Checklist 109 | 110 | - [ ] **Code Review**: Code follows project standards and best practices 111 | - [ ] **Testing**: Testing appears thorough and appropriate 112 | - [ ] **Documentation**: Documentation is complete and accurate 113 | - [ ] **Security**: Security considerations have been addressed 114 | - [ ] **Functionality**: Script performs as described 115 | - [ ] **Category**: Script is in the correct category folder -------------------------------------------------------------------------------- /.github/workflows/daily-script-notifications.yml: -------------------------------------------------------------------------------- 1 | name: Daily Script Update Notifications 2 | 3 | on: 4 | schedule: 5 | # Runs daily at 8 AM UTC 6 | - cron: "0 8 * * *" 7 | workflow_dispatch: # Allow manual trigger for testing 8 | 9 | jobs: 10 | check-and-notify: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 # Get full history for proper version comparison 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: "20" 23 | 24 | - name: Install dependencies 25 | run: | 26 | npm init -y 27 | npm install @supabase/supabase-js resend glob 28 | 29 | - name: Check for script updates and send notifications 30 | env: 31 | SUPABASE_URL: ${{ secrets.SUPABASE_URL }} 32 | SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }} 33 | RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }} 34 | FROM_EMAIL: ${{ secrets.FROM_EMAIL }} 35 | run: | 36 | node .github/scripts/check-script-updates.js 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CLAUDE.md 2 | 3 | .claude/ -------------------------------------------------------------------------------- /Azure_Deployment.md: -------------------------------------------------------------------------------- 1 | # Azure Automation Deployment Guide 2 | 3 | This guide explains how to deploy IntuneAutomation scripts as Azure Automation runbooks using the one-click deployment feature. 4 | 5 | ## 🚀 Quick Start 6 | 7 | 1. Visit [intuneautomation.com](https://intuneautomation.com) 8 | 2. Browse to any script detail page 9 | 3. Click the **"Deploy to Azure"** button 10 | 4. Follow the Azure portal deployment wizard 11 | 12 | ## 📋 Prerequisites 13 | 14 | ### Azure Automation Account Setup 15 | 16 | 1. **Create an Automation Account** (if you don't have one) 17 | 18 | ```bash 19 | az automation account create \ 20 | --resource-group "rg-automation" \ 21 | --name "aa-intune-automation" \ 22 | --location "East US" 23 | ``` 24 | 25 | 2. **Enable System-assigned Managed Identity** 26 | 27 | - Go to your Automation Account in Azure portal 28 | - Navigate to **Identity** > **System assigned** 29 | - Set Status to **On** and save 30 | 31 | 3. **Import Required PowerShell Modules** 32 | - Go to **Modules** > **Browse Gallery** 33 | - Search and import: `Microsoft.Graph.Authentication` 34 | - Import any additional modules required by specific scripts 35 | 36 | ### Microsoft Graph Permissions 37 | 38 | Assign the required permissions to your Automation Account's Managed Identity: 39 | 40 | #### Using Azure Portal 41 | 42 | 1. Go to **Azure Active Directory** > **Enterprise applications** 43 | 2. Search for your Automation Account name 44 | 3. Go to **API permissions** > **Add a permission** 45 | 4. Select **Microsoft Graph** > **Application permissions** 46 | 5. Add required permissions (see script documentation) 47 | 6. Grant admin consent 48 | 49 | #### Using PowerShell 50 | 51 | ```powershell 52 | # Connect to Microsoft Graph 53 | Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All" 54 | 55 | # Get the Managed Identity 56 | $automationAccount = "aa-intune-automation" 57 | $managedIdentity = Get-MgServicePrincipal -Filter "displayName eq '$automationAccount'" 58 | 59 | # Get Microsoft Graph service principal 60 | $graphServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'" 61 | 62 | # Common permissions for most scripts 63 | $permissions = @( 64 | "DeviceManagementManagedDevices.Read.All", 65 | "DeviceManagementManagedDevices.ReadWrite.All" 66 | ) 67 | 68 | foreach ($permission in $permissions) { 69 | $appRole = $graphServicePrincipal.AppRoles | Where-Object { $_.Value -eq $permission } 70 | New-MgServicePrincipalAppRoleAssignment ` 71 | -ServicePrincipalId $managedIdentity.Id ` 72 | -PrincipalId $managedIdentity.Id ` 73 | -ResourceId $graphServicePrincipal.Id ` 74 | -AppRoleId $appRole.Id 75 | } 76 | ``` 77 | 78 | ## 🔄 How Dual Environment Support Works 79 | 80 | All scripts automatically detect their execution environment: 81 | 82 | ```powershell 83 | # Environment detection 84 | if ($PSPrivateMetadata.JobId.Guid) { 85 | Write-Output "Running inside Azure Automation Runbook" 86 | $IsRunbook = $true 87 | } else { 88 | Write-Output "Running locally in IDE or terminal" 89 | $IsRunbook = $false 90 | } 91 | 92 | # Smart authentication 93 | if ($IsRunbook) { 94 | # Azure Automation - Use Managed Identity 95 | Connect-MgGraph -Identity -NoWelcome 96 | } else { 97 | # Local execution - Use interactive authentication 98 | Connect-MgGraph -Scopes $Scopes -NoWelcome 99 | } 100 | ``` 101 | 102 | ## 📝 Deployment Process 103 | 104 | ### Step 1: Click Deploy to Azure 105 | 106 | The website generates an ARM template and redirects you to Azure portal. 107 | 108 | ### Step 2: Configure Deployment Parameters 109 | 110 | In the Azure portal deployment form: 111 | 112 | #### **Important: Resource Group Selection** 113 | 114 | - **Select the resource group** that contains your existing Automation Account 115 | - ⚠️ **Do NOT create a new resource group** unless you want to create a new Automation Account 116 | 117 | #### **Automation Account Name** 118 | 119 | - **Enter the exact name** of your existing Automation Account 120 | - ❌ **No dropdown available** - you must type the name manually 121 | - ✅ **Case sensitive** - ensure exact spelling and capitalization 122 | 123 | #### **Runbook Name** 124 | 125 | - **Customize if needed** (defaults to script name) 126 | - This will be the name of the new runbook created in your Automation Account 127 | 128 | #### **Example Configuration:** 129 | 130 | ``` 131 | Subscription: Production Subscription 132 | Resource Group: rg-automation (where your Automation Account exists) 133 | Automation Account Name: aa-intune-automation (your existing account) 134 | Runbook Name: rotate-bitlocker-keys (or customize) 135 | ``` 136 | 137 | ### Step 3: Deploy 138 | 139 | - Click **Review + create** 140 | - Verify all parameters are correct 141 | - Click **Create** to deploy the runbook 142 | 143 | ### Step 4: Publish and Test 144 | 145 | After successful deployment: 146 | 147 | 1. Go to your Automation Account > **Runbooks** 148 | 2. Find your newly created runbook (it will be in **Draft** status) 149 | 3. Click **Publish** to make it available for execution 150 | 4. Test the runbook using **Start** 151 | 152 | ## ⚠️ Common Deployment Issues 153 | 154 | ### "Resource Not Found" Error 155 | 156 | **Cause**: Automation Account name doesn't exist in the selected resource group 157 | **Solution**: 158 | 159 | - Verify the Automation Account name is spelled correctly 160 | - Ensure you selected the correct resource group 161 | - Check that the Automation Account exists in that resource group 162 | 163 | ### "Insufficient Permissions" Error 164 | 165 | **Cause**: You don't have permissions to create resources in the Automation Account 166 | **Solution**: 167 | 168 | - Ensure you have **Automation Contributor** role on the resource group 169 | - Or **Automation Operator** role specifically on the Automation Account 170 | 171 | ### "Runbook Already Exists" Error 172 | 173 | **Cause**: A runbook with the same name already exists 174 | **Solution**: 175 | 176 | - Change the **Runbook Name** parameter to something unique 177 | - Or delete the existing runbook if you want to replace it 178 | 179 | ## 🔧 Common Permissions by Script Category 180 | 181 | ### Security Scripts 182 | 183 | - `DeviceManagementManagedDevices.ReadWrite.All` 184 | - `BitlockerKey.ReadBasic.All` 185 | 186 | ### Device Management 187 | 188 | - `DeviceManagementManagedDevices.Read.All` 189 | - `DeviceManagementManagedDevices.ReadWrite.All` 190 | 191 | ### Compliance Scripts 192 | 193 | - `DeviceManagementManagedDevices.Read.All` 194 | - `DeviceManagementConfiguration.Read.All` 195 | 196 | ### App Management 197 | 198 | - `DeviceManagementApps.Read.All` 199 | - `DeviceManagementApps.ReadWrite.All` 200 | 201 | ## 🚨 Troubleshooting 202 | 203 | ### "Authentication needed" Error 204 | 205 | **Cause**: Managed Identity not enabled or permissions not assigned 206 | **Solution**: 207 | 208 | 1. Enable Managed Identity in Automation Account 209 | 2. Assign required Microsoft Graph permissions 210 | 211 | ### "Module not found" Error 212 | 213 | **Cause**: Required PowerShell modules not imported 214 | **Solution**: 215 | 216 | 1. Go to Automation Account > **Modules** 217 | 2. Import `Microsoft.Graph.Authentication` 218 | 3. Import any script-specific modules 219 | 220 | ### "Insufficient privileges" Error 221 | 222 | **Cause**: Missing Microsoft Graph permissions 223 | **Solution**: 224 | 225 | 1. Check script documentation for required permissions 226 | 2. Assign missing permissions to Managed Identity 227 | 3. Grant admin consent 228 | 229 | ### Runbook Fails to Start 230 | 231 | **Cause**: Various issues with runbook configuration 232 | **Solution**: 233 | 234 | 1. Check runbook is published 235 | 2. Verify all required modules are imported 236 | 3. Review Activity Logs for detailed errors 237 | 238 | ## 📊 Monitoring and Logging 239 | 240 | ### View Runbook Execution 241 | 242 | 1. Go to Automation Account > **Jobs** 243 | 2. Click on a job to view detailed logs 244 | 3. Check **Output** and **Errors** tabs 245 | 246 | ### Set Up Alerts 247 | 248 | ```powershell 249 | # Example: Alert on runbook failures 250 | $actionGroup = Get-AzActionGroup -ResourceGroupName "rg-monitoring" -Name "ag-alerts" 251 | 252 | $condition = New-AzMetricAlertRuleV2Criteria ` 253 | -MetricName "TotalJob" ` 254 | -Operator GreaterThan ` 255 | -Threshold 0 ` 256 | -TimeAggregation Total 257 | 258 | New-AzMetricAlertRuleV2 ` 259 | -Name "Runbook-Failures" ` 260 | -ResourceGroupName "rg-automation" ` 261 | -WindowSize 00:05:00 ` 262 | -Frequency 00:01:00 ` 263 | -TargetResourceId "/subscriptions/{subscription-id}/resourceGroups/rg-automation/providers/Microsoft.Automation/automationAccounts/aa-intune-automation" ` 264 | -Condition $condition ` 265 | -ActionGroupId $actionGroup.Id ` 266 | -Severity 2 267 | ``` 268 | 269 | ## 🔄 Scheduling Runbooks 270 | 271 | ### Using Azure Portal 272 | 273 | 1. Go to runbook > **Schedules** 274 | 2. Click **Add a schedule** 275 | 3. Configure frequency and parameters 276 | 277 | ### Using PowerShell 278 | 279 | ```powershell 280 | # Create a daily schedule 281 | $schedule = New-AzAutomationSchedule ` 282 | -AutomationAccountName "aa-intune-automation" ` 283 | -ResourceGroupName "rg-automation" ` 284 | -Name "Daily-Device-Check" ` 285 | -StartTime (Get-Date).AddHours(1) ` 286 | -DayInterval 1 287 | 288 | # Link schedule to runbook 289 | Register-AzAutomationScheduledRunbook ` 290 | -AutomationAccountName "aa-intune-automation" ` 291 | -ResourceGroupName "rg-automation" ` 292 | -RunbookName "get-stale-devices" ` 293 | -ScheduleName "Daily-Device-Check" 294 | ``` 295 | 296 | ## 🎯 Best Practices 297 | 298 | ### Security 299 | 300 | - Use least-privilege permissions 301 | - Regularly review and audit permissions 302 | - Monitor runbook execution logs 303 | 304 | ### Performance 305 | 306 | - Import only required modules 307 | - Use efficient PowerShell patterns 308 | - Consider execution time limits 309 | 310 | ### Maintenance 311 | 312 | - Keep modules updated 313 | - Test scripts in development environment 314 | - Document custom modifications 315 | 316 | ## 📚 Additional Resources 317 | 318 | - [Azure Automation Documentation](https://docs.microsoft.com/en-us/azure/automation/) 319 | - [Microsoft Graph Permissions Reference](https://docs.microsoft.com/en-us/graph/permissions-reference) 320 | - [PowerShell in Azure Automation](https://docs.microsoft.com/en-us/azure/automation/automation-powershell) 321 | 322 | ## 🤝 Support 323 | 324 | - **Issues**: Report on [GitHub Issues](https://github.com/ugurkocde/IntuneAutomation/issues) 325 | - **Discussions**: Join [GitHub Discussions](https://github.com/ugurkocde/IntuneAutomation/discussions) 326 | - **Website**: Visit [intuneautomation.com](https://intuneautomation.com) 327 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to IntuneAutomation 2 | 3 | Thank you for your interest in contributing to the IntuneAutomation project! This guide will help you create high-quality PowerShell scripts that benefit the community. 4 | 5 | ## 🚀 Quick Start 6 | 7 | 1. **Fork the repository** and clone it to your local machine 8 | 2. **Create a new branch** for your contribution 9 | 3. **Use the script template** from `templates/script-template.ps1` 10 | 4. **Follow the coding standards** outlined below 11 | 5. **Test your script thoroughly** 12 | 6. **Submit a pull request** 13 | 14 | ## 📁 Project Structure 15 | 16 | ``` 17 | IntuneAutomation/ 18 | ├── scripts/ 19 | │ ├── operational/ # Device operations (sync, restart, wipe) 20 | │ ├── apps/ # Application management (install, uninstall, reporting) 21 | │ ├── compliance/ # Compliance reporting and remediation 22 | │ ├── security/ # Security operations (policies, conditional access) 23 | │ └── devices/ # Device management (enrollment, configuration) 24 | ├── templates/ # Templates for contributors 25 | ├── .github/ # GitHub templates and workflows 26 | ├── CONTRIBUTING.md # This file 27 | ├── LICENSE 28 | └── README.md 29 | ``` 30 | 31 | ## 📝 Script Categories 32 | 33 | ### Operational (`scripts/operational/`) 34 | Scripts for day-to-day device operations: 35 | - Device synchronization 36 | - Remote actions (restart, shutdown, lock) 37 | - Bulk operations 38 | - Device troubleshooting 39 | 40 | ### Apps (`scripts/apps/`) 41 | Application management scripts: 42 | - App deployment automation 43 | - Application reporting 44 | - License management 45 | - App removal/cleanup 46 | 47 | ### Compliance (`scripts/compliance/`) 48 | Compliance and reporting scripts: 49 | - Compliance reporting 50 | - Policy assessment 51 | - Remediation scripts 52 | - Audit reports 53 | 54 | ### Security (`scripts/security/`) 55 | Security-focused scripts: 56 | - Security policy management 57 | - Conditional access automation 58 | - Threat protection configuration 59 | - Security reporting 60 | 61 | ### Devices (`scripts/devices/`) 62 | Device lifecycle management: 63 | - Device enrollment automation 64 | - Configuration profile management 65 | - Device inventory 66 | - Hardware/software reporting 67 | 68 | ## 🛠️ Coding Standards 69 | 70 | ### Script Header Requirements 71 | 72 | Every script must include a comprehensive header with the following sections: 73 | 74 | ```powershell 75 | <# 76 | .TITLE 77 | [Descriptive title - what the script does] 78 | 79 | .SYNOPSIS 80 | [One-line summary of functionality] 81 | 82 | .DESCRIPTION 83 | [Detailed description including use cases and prerequisites] 84 | 85 | .TAGS 86 | [Category],[Subcategory] (e.g., Operational,Devices) 87 | 88 | .MINROLE 89 | [Minimum required role - e.g., Intune Administrator] 90 | 91 | .PERMISSIONS 92 | [Required Graph API permissions - comma separated] 93 | 94 | .AUTHOR 95 | [Your name or GitHub username] 96 | 97 | .VERSION 98 | [Version number - start with 1.0] 99 | 100 | .CHANGELOG 101 | [Version history with descriptions] 102 | 103 | .EXAMPLE 104 | [Usage examples with descriptions] 105 | 106 | .NOTES 107 | [Additional information, requirements, limitations] 108 | #> 109 | ``` 110 | 111 | ### PowerShell Best Practices 112 | 113 | 1. **Use CmdletBinding**: All scripts should use `[CmdletBinding()]` 114 | 2. **Parameter Validation**: Use proper parameter attributes and validation 115 | 3. **Error Handling**: Implement comprehensive try-catch blocks 116 | 4. **Information Output**: Use `Write-Information` for progress updates 117 | 5. **Consistent Naming**: Use PascalCase for variables and functions 118 | 6. **Comments**: Include inline comments for complex logic 119 | 7. **Modules**: Check for and import required modules 120 | 8. **Graph Connection**: Always include authentication and disconnection 121 | 122 | ### Code Structure 123 | 124 | 1. **Header**: Complete script documentation 125 | 2. **Parameters**: Well-defined parameters with validation 126 | 3. **Module Checks**: Verify required modules are available 127 | 4. **Authentication**: Microsoft Graph connection 128 | 5. **Helper Functions**: Reusable functions (include pagination helper) 129 | 6. **Main Logic**: Core script functionality 130 | 7. **Error Handling**: Comprehensive error management 131 | 8. **Cleanup**: Proper disconnection and cleanup 132 | 9. **Summary**: Execution summary output 133 | 134 | ### Required Elements 135 | 136 | - **Rate Limiting**: Handle Graph API throttling 137 | - **Pagination**: Use the provided `Get-MgGraphAllPages` function 138 | - **Progress Feedback**: Show real-time progress to users 139 | - **Parameter Validation**: Validate all inputs 140 | - **Graceful Failures**: Handle errors without breaking the system 141 | - **Documentation**: Clear examples and usage instructions 142 | 143 | ## 🔒 Security Considerations 144 | 145 | 1. **Least Privilege**: Request only the minimum required permissions 146 | 2. **Input Validation**: Validate all user inputs 147 | 3. **Secure Practices**: Never hardcode credentials or sensitive data 148 | 4. **Graph Permissions**: Clearly document all required permissions 149 | 5. **Role Requirements**: Specify minimum role requirements 150 | 151 | ## 🧪 Testing Guidelines 152 | 153 | Before submitting your script: 154 | 155 | 1. **Test in a lab environment** - Never test in production first 156 | 2. **Test with different parameters** - Verify all parameter combinations 157 | 3. **Test error scenarios** - Ensure graceful failure handling 158 | 4. **Test permissions** - Verify script works with minimum required permissions 159 | 5. **Test rate limiting** - Ensure script handles API throttling 160 | 6. **Document test results** - Include testing information in your PR 161 | 162 | ## 📋 Script Template Usage 163 | 164 | 1. Copy `templates/script-template.ps1` to the appropriate category folder 165 | 2. Rename the file to describe its function (e.g., `get-device-inventory.ps1`) 166 | 3. Update all header sections with your script's information 167 | 4. Replace placeholder functions with your logic 168 | 5. Update the required modules and permissions 169 | 6. Test thoroughly before submitting 170 | 171 | ## 🔄 Submission Process 172 | 173 | ### Before You Submit 174 | 175 | - [ ] Script follows the template structure 176 | - [ ] All header sections are complete and accurate 177 | - [ ] Script has been tested in a lab environment 178 | - [ ] Required permissions are documented 179 | - [ ] Examples are provided and tested 180 | - [ ] Error handling is implemented 181 | - [ ] Code follows PowerShell best practices 182 | 183 | ### Pull Request Guidelines 184 | 185 | 1. **Branch Naming**: Use descriptive branch names (e.g., `feature/device-compliance-report`) 186 | 2. **Commit Messages**: Write clear, descriptive commit messages 187 | 3. **PR Title**: Use format: `[Category] Script Name - Brief Description` 188 | 4. **PR Description**: Include: 189 | - What the script does 190 | - Testing performed 191 | - Any special considerations 192 | - Screenshots if applicable 193 | 194 | ### PR Template Checklist 195 | 196 | When you submit a PR, ensure you've completed the checklist in the PR template. 197 | 198 | ## 🏷️ Tagging Guidelines 199 | 200 | Use consistent tags for categorizing scripts: 201 | 202 | **Primary Categories:** 203 | - `Operational` - Day-to-day operations 204 | - `Apps` - Application management 205 | - `Compliance` - Compliance and reporting 206 | - `Security` - Security operations 207 | - `Devices` - Device management 208 | 209 | **Secondary Tags:** 210 | - `Reporting` - Generates reports 211 | - `Bulk` - Bulk operations 212 | - `Remediation` - Fixes issues 213 | - `Automation` - Automates processes 214 | - `Monitoring` - Monitoring/alerting 215 | 216 | ## 🤝 Community Guidelines 217 | 218 | - **Be Respectful**: Treat all contributors with respect 219 | - **Share Knowledge**: Help others learn and improve 220 | - **Quality Focus**: Prioritize code quality and documentation 221 | - **Security First**: Always consider security implications 222 | - **Test Thoroughly**: Test your contributions properly 223 | 224 | ## 📞 Getting Help 225 | 226 | - **Issues**: Create an issue for bugs or feature requests 227 | - **Discussions**: Use GitHub Discussions for questions 228 | - **Documentation**: Check existing scripts for examples 229 | - **Template**: Always start with the provided template 230 | 231 | ## 🏆 Recognition 232 | 233 | Contributors will be: 234 | - Listed in script headers as authors 235 | - Recognized in release notes 236 | - Added to the project contributors list 237 | 238 | Thank you for contributing to IntuneAutomation! Your scripts help IT professionals worldwide manage their environments more effectively. 239 | 240 | ## 📚 Additional Resources 241 | 242 | - [Microsoft Graph PowerShell SDK Documentation](https://docs.microsoft.com/en-us/powershell/microsoftgraph/) 243 | - [Microsoft Graph API Reference](https://docs.microsoft.com/en-us/graph/api/overview) 244 | - [PowerShell Best Practices](https://docs.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations) 245 | - [Intune PowerShell Samples](https://github.com/microsoftgraph/powershell-intune-samples) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Ugur Koc | Microsoft MVP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Intune Automation Logo 3 |

4 | 5 |

6 | A collection of PowerShell scripts for automating Microsoft Intune device management tasks. 7 |

8 | 9 |

10 | Quick Start • 11 | Scripts • 12 | Prerequisites • 13 | Contributing • 14 | Website 15 |

16 | 17 | --- 18 | 19 | ## 🚀 Overview 20 | 21 | This repository contains PowerShell scripts designed to help IT administrators automate common Microsoft Intune management operations. All scripts use the Microsoft Graph API and are organized by functional category for easy discovery and use. 22 | 23 | ### ✨ Key Features 24 | 25 | - **📱 Device Management**: Automated device operations and lifecycle management 26 | - **🔒 Security & Compliance**: Automated compliance reporting and security operations 27 | - **📦 Application Management**: Streamlined app deployment and management 28 | - **📊 Monitoring & Reporting**: Comprehensive monitoring and analytics tools 29 | - **☁️ Azure Integration**: Native support for Azure Automation runbooks 30 | - **🔐 Security-First**: Built with enterprise security best practices 31 | 32 | ## 🚀 Quick Start 33 | 34 | ### Option 1: Local Execution (Recommended for Testing) 35 | 36 | The simplest way to use these scripts is to **download and run them locally**: 37 | 38 | 1. **📥 Download the scripts** you need from this repository 39 | 2. **📦 Install required modules** (the scripts will prompt you if needed) 40 | 3. **▶️ Run the scripts** directly from PowerShell with appropriate parameters 41 | 4. **📋 Review the output** and logs for results 42 | 43 | Most scripts are designed to work immediately without additional setup beyond the required PowerShell modules. 44 | 45 | **Example:** 46 | ```powershell 47 | # Download a script and run it 48 | .\Get-DeviceComplianceReport.ps1 -TenantId "your-tenant-id" 49 | ``` 50 | 51 | ### Option 2: Azure Automation (unattended execution and scheduling) 52 | 53 | For **scheduling**, **unattended execution**, or **more complex automation**, you can deploy these scripts as Azure Automation Runbooks: 54 | 55 | #### Step 1: Create Managed Identity 56 | Create a **User Assigned Managed Identity** in your Azure tenant through the Azure Portal. 57 | 58 | #### Step 2: Grant Permissions 59 | Use our setup script to grant necessary Microsoft Graph permissions: 60 | 61 | ```powershell 62 | # Grant default Intune permissions to your managed identity 63 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "YourManagedIdentityName" 64 | 65 | # Or grant custom permissions for specific use cases 66 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "YourManagedIdentityName" -CustomPermissions @("User.Read.All", "Group.Read.All") 67 | ``` 68 | 69 | #### Step 3: Configure Azure Automation 70 | 1. **Assign the managed identity** to your Azure Automation Account 71 | 2. **Import the scripts** as runbooks 72 | 3. **Schedule execution** as needed 73 | 74 | This approach enables: 75 | - **⏰ Scheduled execution** (daily, weekly, etc.) 76 | - **🤖 Unattended operations** without user interaction 77 | - **📈 Centralized logging** and monitoring 78 | - **🔗 Integration** with other Azure services 79 | 80 | ### Default Permissions for Automation 81 | 82 | The `grant-permissions-managed-identity.ps1` script grants these Microsoft Graph API permissions by default: 83 | 84 | | Permission | Description | 85 | |------------|-------------| 86 | | `DeviceManagementManagedDevices.ReadWrite.All` | Full device management access | 87 | | `DeviceManagementConfiguration.ReadWrite.All` | Configuration policy management | 88 | | `DeviceManagementApps.ReadWrite.All` | Application management | 89 | | `DeviceManagementServiceConfig.ReadWrite.All` | Service configuration | 90 | | `DeviceManagementRBAC.ReadWrite.All` | Role-based access control | 91 | | `DeviceManagementManagedDevices.PrivilegedOperations.All` | Advanced device operations | 92 | 93 | These permissions cover most common Intune automation scenarios. 94 | 95 | ## 📁 Scripts Overview 96 | 97 | ``` 98 | IntuneAutomation/ 99 | ├── 🔧 grant-permissions-managed-identity.ps1 # Setup script for managed identity permissions 100 | ├── 📂 scripts/ 101 | │ ├── 🔄 operational/ # Device operations (restart, wipe, sync) 102 | │ ├── 📱 apps/ # Application management and deployment 103 | │ ├── ✅ compliance/ # Compliance reporting and remediation 104 | │ ├── 🔒 security/ # Security operations and policies 105 | │ ├── 💻 devices/ # Device management and inventory 106 | │ └── 📊 monitoring/ # Monitoring, reporting, and analytics 107 | ├── 📄 templates/ # Script templates for contributors 108 | ├── 📋 LICENSE 109 | ├── 🤝 CONTRIBUTING.md 110 | └── 📖 README.md 111 | ``` 112 | 113 | ### Popular Scripts 114 | 115 | - **Device Operations**: Bulk device actions, automated device cleanup 116 | - **Compliance Reporting**: Automated compliance dashboards and alerts 117 | - **App Management**: Silent app deployment and update automation 118 | - **Security Monitoring**: Threat detection and response automation 119 | 120 | > 💡 **Tip**: Each script category includes detailed documentation and usage examples. 121 | 122 | ## 📋 Prerequisites 123 | 124 | ### If you are running the scripts locally 125 | - **PowerShell 5.1** or later (PowerShell 7+ recommended) 126 | - **Microsoft Graph PowerShell modules**: 127 | - `Microsoft.Graph.Authentication` 128 | - **You have to sign in with an Intune Admin account** 129 | 130 | ### If you are running the scripts in Azure Automation as a Runbook 131 | - **Azure Automation Account** 132 | - **User Assigned Managed Identity** with the following permissions: 133 | - `DeviceManagementManagedDevices.ReadWrite.All` 134 | - `DeviceManagementConfiguration.ReadWrite.All` 135 | - `DeviceManagementApps.ReadWrite.All` 136 | - `DeviceManagementServiceConfig.ReadWrite.All` 137 | - `DeviceManagementRBAC.ReadWrite.All` 138 | - `DeviceManagementManagedDevices.PrivilegedOperations.All` 139 | 140 | > 💡 **Tip**: Check `grant-permissions-managed-identity.ps1` for more details and how to grant the permissions 141 | 142 | - **Your Environment in the Azure Automation Account has to have the following modules installed:** 143 | - `Az.Accounts` 144 | - `Az.Resources` 145 | - `Microsoft.Graph.Applications` 146 | - `Microsoft.Graph.Authentication` 147 | 148 | ### Authentication Methods Supported 149 | - **Interactive Authentication** (default for local execution) 150 | - **Managed Identity** (recommended for Azure Automation) 151 | 152 | ## 🤝 Contributing 153 | 154 | We welcome contributions from the community! Whether you're fixing bugs, improving existing scripts, or adding new automation tools, your contributions help IT professionals worldwide. 155 | 156 | ### Quick Start for Contributors 157 | 158 | 1. **🍴 Fork the repository** and clone it locally 159 | 2. **📝 Use our script template**: Copy `templates/script-template.ps1` to get started 160 | 3. **📏 Follow our guidelines**: Read [CONTRIBUTING.md](CONTRIBUTING.md) for detailed instructions 161 | 4. **🧪 Test thoroughly**: Always test your scripts in a lab environment first 162 | 5. **🔄 Submit a pull request**: Use our PR template for faster reviews 163 | 164 | See our [Contributing Guide](CONTRIBUTING.md) for detailed instructions, coding standards, and submission guidelines. 165 | 166 | ## ❓ Support & FAQ 167 | 168 | ### Common Issues 169 | 170 | **Q: I have an Idea for a new script but I need someone to implement it?** 171 | A: Open an issue and let me know. I'll be happy to implement it. 172 | 173 | **Q: Scripts fail with authentication errors** 174 | A: Ensure you have the required Microsoft Graph permissions and modules installed. 175 | 176 | **Q: Can I use these scripts with GCC High/DoD tenants?** 177 | A: Yes, but you may need to modify the Graph API endpoints for government clouds. 178 | 179 | **Q: Are these scripts suitable for production use?** 180 | A: Yes, but always test in a lab environment first and follow your organization's change management processes. 181 | 182 | ### Getting Help 183 | 184 | - 📖 Check the [documentation](https://intuneautomation.com) 185 | - 🐛 Report issues on [GitHub Issues](../../issues) 186 | - 💬 Join discussions on [GitHub Discussions](../../discussions) 187 | 188 | ## 📜 License 189 | 190 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 191 | 192 | ## 👨‍💻 Author 193 | 194 | **Ugur Koc** - Microsoft MVP 195 | - 🌐 Website: [https://ugurkoc.de](https://ugurkoc.de) 196 | - 🐦 X: [@ugurkocde](https://x.com/ugurkocde) 197 | - 💼 LinkedIn: [Ugur Koc](https://www.linkedin.com/in/ugurkocde/) 198 | 199 | --- 200 | 201 |

202 | ⭐ If this project helps you, please give it a star! ⭐ 203 |

204 | -------------------------------------------------------------------------------- /azure-deployment-templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "generated": "2025-06-05T07:13:33Z", 3 | "templates": { 4 | "get-devices-by-scopetag": { 5 | "author": "Ugur Koc", 6 | "description": "This script connects to Microsoft Graph and retrieves all managed devices from Intune,", 7 | "scriptPath": "scripts/devices/get-devices-by-scopetag.ps1", 8 | "tags": [ 9 | "Devices", 10 | "Compliance" 11 | ], 12 | "version": "1.0", 13 | "title": "Get Devices by Scope Tag Report", 14 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fget-devices-by-scopetag-azure-deployment.json", 15 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/get-devices-by-scopetag-azure-deployment.json", 16 | "permissions": [ 17 | "DeviceManagementManagedDevices.Read.All", 18 | "DeviceManagementRBAC.Read.All" 19 | ] 20 | }, 21 | "get-device-compliance-report": { 22 | "author": "Ugur Koc", 23 | "description": "This script connects to Microsoft Graph, retrieves managed devices and their compliance status,", 24 | "scriptPath": "scripts/compliance/get-device-compliance-report.ps1", 25 | "tags": [ 26 | "Devices", 27 | "Compliance", 28 | "Reporting" 29 | ], 30 | "version": "1.0", 31 | "title": "Device Compliance Report", 32 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fget-device-compliance-report-azure-deployment.json", 33 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/get-device-compliance-report-azure-deployment.json", 34 | "permissions": [ 35 | "DeviceManagementManagedDevices.Read.All", 36 | "DeviceManagementConfiguration.Read.All" 37 | ] 38 | }, 39 | "rotate-bitlocker-keys": { 40 | "author": "Ugur Koc", 41 | "description": "This script connects to Intune via Graph API and rotates the BitLocker keys for all managed Windows devices.", 42 | "scriptPath": "scripts/security/rotate-bitlocker-keys.ps1", 43 | "tags": [ 44 | "Security", 45 | "Operational" 46 | ], 47 | "version": "1.0", 48 | "title": "Rotate BitLocker Keys", 49 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2frotate-bitlocker-keys-azure-deployment.json", 50 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/rotate-bitlocker-keys-azure-deployment.json", 51 | "permissions": [ 52 | "DeviceManagementManagedDevices.ReadWrite.All" 53 | ] 54 | }, 55 | "sync-devices": { 56 | "author": "Ugur Koc", 57 | "description": "This script connects to Microsoft Graph and triggers synchronization operations on targeted devices.", 58 | "scriptPath": "scripts/operational/sync-devices.ps1", 59 | "tags": [ 60 | "Operational", 61 | "Devices" 62 | ], 63 | "version": "1.0", 64 | "title": "Sync Devices", 65 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fsync-devices-azure-deployment.json", 66 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/sync-devices-azure-deployment.json", 67 | "permissions": [ 68 | "DeviceManagementManagedDevices.ReadWrite.All", 69 | "DeviceManagementManagedDevices.Read.All", 70 | "Group.Read.All", 71 | "GroupMember.Read.All" 72 | ] 73 | }, 74 | "check-bitlocker-keys": { 75 | "author": "Ugur Koc", 76 | "description": "This script connects to Microsoft Graph API, retrieves all Windows devices from Intune,", 77 | "scriptPath": "scripts/monitoring/check-bitlocker-keys.ps1", 78 | "tags": [ 79 | "Monitoring", 80 | "Security" 81 | ], 82 | "version": "1.0", 83 | "title": "BitLocker Key Storage Checker", 84 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fcheck-bitlocker-keys-azure-deployment.json", 85 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/check-bitlocker-keys-azure-deployment.json", 86 | "permissions": [ 87 | "DeviceManagementManagedDevices.Read.All", 88 | "BitlockerKey.Read.All" 89 | ] 90 | }, 91 | "get-application-inventory-report": { 92 | "author": "Ugur Koc", 93 | "description": "This script connects to Microsoft Graph, retrieves all managed devices and their installed applications,", 94 | "scriptPath": "scripts/apps/get-application-inventory-report.ps1", 95 | "tags": [ 96 | "Apps", 97 | "Reporting" 98 | ], 99 | "version": "1.0", 100 | "title": "Application Inventory Report", 101 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fget-application-inventory-report-azure-deployment.json", 102 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/get-application-inventory-report-azure-deployment.json", 103 | "permissions": [ 104 | "DeviceManagementManagedDevices.Read.All", 105 | "DeviceManagementApps.Read.All" 106 | ] 107 | }, 108 | "get-stale-devices": { 109 | "author": "Ugur Koc", 110 | "description": "This script connects to Microsoft Graph and retrieves all managed devices from Intune,", 111 | "scriptPath": "scripts/devices/get-stale-devices.ps1", 112 | "tags": [ 113 | "Operational", 114 | "Devices" 115 | ], 116 | "version": "1.0", 117 | "title": "Get Stale Intune Devices", 118 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fget-stale-devices-azure-deployment.json", 119 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/get-stale-devices-azure-deployment.json", 120 | "permissions": [ 121 | "DeviceManagementManagedDevices.Read.All" 122 | ] 123 | }, 124 | "get-duplicate-applications": { 125 | "author": "Ugur Koc", 126 | "description": "This script connects to Microsoft Graph, retrieves all applications uploaded to Intune,", 127 | "scriptPath": "scripts/apps/get-duplicate-applications.ps1", 128 | "tags": [ 129 | "Apps", 130 | "Reporting" 131 | ], 132 | "version": "1.0", 133 | "title": "Duplicate Applications Report", 134 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fget-duplicate-applications-azure-deployment.json", 135 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/get-duplicate-applications-azure-deployment.json", 136 | "permissions": [ 137 | "DeviceManagementApps.Read.All" 138 | ] 139 | }, 140 | "wipe-devices": { 141 | "author": "Ugur Koc", 142 | "description": "This script connects to Microsoft Graph and triggers remote wipe operations on targeted devices.", 143 | "scriptPath": "scripts/operational/wipe-devices.ps1", 144 | "tags": [ 145 | "Operational", 146 | "Devices" 147 | ], 148 | "version": "1.0", 149 | "title": "Wipe Devices", 150 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fwipe-devices-azure-deployment.json", 151 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/wipe-devices-azure-deployment.json", 152 | "permissions": [ 153 | "DeviceManagementManagedDevices.ReadWrite.All", 154 | "DeviceManagementManagedDevices.Read.All", 155 | "Group.Read.All", 156 | "GroupMember.Read.All" 157 | ] 158 | }, 159 | "apple-token-expiration-alert": { 160 | "author": "Ugur Koc", 161 | "description": "This script is designed to run as a scheduled Azure Automation runbook that monitors the expiration", 162 | "scriptPath": "scripts/notification/apple-token-expiration-alert.ps1", 163 | "tags": [ 164 | "Notification" 165 | ], 166 | "version": "1.0", 167 | "title": "Apple Token Expiration Alert Notification", 168 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fapple-token-expiration-alert-azure-deployment.json", 169 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/apple-token-expiration-alert-azure-deployment.json", 170 | "permissions": [ 171 | "DeviceManagementServiceConfig.Read.All", 172 | "DeviceManagementConfiguration.Read.All", 173 | "Mail.Send" 174 | ] 175 | }, 176 | "stale-device-cleanup-alert": { 177 | "author": "Ugur Koc", 178 | "description": "This script is designed to run as a scheduled Azure Automation runbook that monitors devices in", 179 | "scriptPath": "scripts/notification/stale-device-cleanup-alert.ps1", 180 | "tags": [ 181 | "Notification" 182 | ], 183 | "version": "1.0", 184 | "title": "Stale Device Cleanup Alert Notification", 185 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fstale-device-cleanup-alert-azure-deployment.json", 186 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/stale-device-cleanup-alert-azure-deployment.json", 187 | "permissions": [ 188 | "DeviceManagementManagedDevices.Read.All", 189 | "Mail.Send" 190 | ] 191 | }, 192 | "check-unassigned-policies": { 193 | "author": "Ugur Koc", 194 | "description": "This script connects to Microsoft Graph and retrieves all device configuration policies", 195 | "scriptPath": "scripts/monitoring/check-unassigned-policies.ps1", 196 | "tags": [ 197 | "Monitoring" 198 | ], 199 | "version": "1.0", 200 | "title": "Unassigned Policies Monitor", 201 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fcheck-unassigned-policies-azure-deployment.json", 202 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/check-unassigned-policies-azure-deployment.json", 203 | "permissions": [ 204 | "DeviceManagementConfiguration.Read.All" 205 | ] 206 | }, 207 | "cleanup-autopilot-devices": { 208 | "author": "Ugur Koc", 209 | "description": "This script connects to Microsoft Graph and identifies Windows Autopilot devices that are", 210 | "scriptPath": "scripts/devices/cleanup-autopilot-devices.ps1", 211 | "tags": [ 212 | "Operational", 213 | "Devices" 214 | ], 215 | "version": "1.0", 216 | "title": "Cleanup Orphaned Autopilot Devices", 217 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fcleanup-autopilot-devices-azure-deployment.json", 218 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/cleanup-autopilot-devices-azure-deployment.json", 219 | "permissions": [ 220 | "DeviceManagementServiceConfig.ReadWrite.All", 221 | "DeviceManagementManagedDevices.Read.All" 222 | ] 223 | }, 224 | "device-compliance-drift-alert": { 225 | "author": "Ugur Koc", 226 | "description": "This script is designed to run as a scheduled Azure Automation runbook that monitors device compliance", 227 | "scriptPath": "scripts/notification/device-compliance-drift-alert.ps1", 228 | "tags": [ 229 | "Notification" 230 | ], 231 | "version": "1.0", 232 | "title": "Device Compliance Drift Alert Notification", 233 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fdevice-compliance-drift-alert-azure-deployment.json", 234 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/device-compliance-drift-alert-azure-deployment.json", 235 | "permissions": [ 236 | "DeviceManagementManagedDevices.Read.All", 237 | "DeviceManagementConfiguration.Read.All", 238 | "Mail.Send" 239 | ] 240 | }, 241 | "app-deployment-failure-alert": { 242 | "author": "Ugur Koc", 243 | "description": "This script is designed to run as a scheduled Azure Automation runbook that monitors application", 244 | "scriptPath": "scripts/notification/app-deployment-failure-alert.ps1", 245 | "tags": [ 246 | "Notification" 247 | ], 248 | "version": "1.0", 249 | "title": "App Deployment Failure Alert Notification", 250 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fapp-deployment-failure-alert-azure-deployment.json", 251 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/app-deployment-failure-alert-azure-deployment.json", 252 | "permissions": [ 253 | "DeviceManagementApps.Read.All", 254 | "DeviceManagementManagedDevices.Read.All", 255 | "Mail.Send" 256 | ] 257 | }, 258 | "check-policy-changes": { 259 | "author": "Ugur Koc", 260 | "description": "This script connects to Microsoft Graph and retrieves recent changes to Policies", 261 | "scriptPath": "scripts/monitoring/check-policy-changes.ps1", 262 | "tags": [ 263 | "Monitoring" 264 | ], 265 | "version": "1.0", 266 | "title": "Policy Changes Monitor", 267 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fcheck-policy-changes-azure-deployment.json", 268 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/check-policy-changes-azure-deployment.json", 269 | "permissions": [ 270 | "DeviceManagementApps.Read.All", 271 | "DeviceManagementConfiguration.Read.All" 272 | ] 273 | }, 274 | "check-apple-token-validity": { 275 | "author": "Ugur Koc", 276 | "description": "This script connects to Microsoft Graph and retrieves all Apple Device Enrollment Program (DEP) tokens", 277 | "scriptPath": "scripts/monitoring/check-apple-token-validity.ps1", 278 | "tags": [ 279 | "Monitoring" 280 | ], 281 | "version": "1.0", 282 | "title": "Apple Token Validity Checker", 283 | "deployUrl": "https://portal.azure.com/#create/Microsoft.Template/uri/https%3a%2f%2fraw.githubusercontent.com%2fugurkocde%2fIntuneAutomation%2fmain%2fazure-templates%2fcheck-apple-token-validity-azure-deployment.json", 284 | "templateUrl": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/azure-templates/check-apple-token-validity-azure-deployment.json", 285 | "permissions": [ 286 | "DeviceManagementServiceConfig.Read.All", 287 | "DeviceManagementConfiguration.Read.All" 288 | ] 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /azure-templates/app-deployment-failure-alert-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/notification/app-deployment-failure-alert.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "app-deployment-failure-alert", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script is designed to run as a scheduled Azure Automation runbook that monitors application", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/apple-token-expiration-alert-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/notification/apple-token-expiration-alert.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "apple-token-expiration-alert", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script is designed to run as a scheduled Azure Automation runbook that monitors the expiration", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/check-apple-token-validity-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/monitoring/check-apple-token-validity.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "check-apple-token-validity", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and retrieves all Apple Device Enrollment Program (DEP) tokens", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/check-bitlocker-keys-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/monitoring/check-bitlocker-keys.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "check-bitlocker-keys", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph API, retrieves all Windows devices from Intune,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/check-policy-changes-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/monitoring/check-policy-changes.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "check-policy-changes", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and retrieves recent changes to Policies", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/check-unassigned-policies-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/monitoring/check-unassigned-policies.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "check-unassigned-policies", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and retrieves all device configuration policies", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/cleanup-autopilot-devices-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/devices/cleanup-autopilot-devices.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "cleanup-autopilot-devices", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and identifies Windows Autopilot devices that are", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/device-compliance-drift-alert-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/notification/device-compliance-drift-alert.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "device-compliance-drift-alert", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script is designed to run as a scheduled Azure Automation runbook that monitors device compliance", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/get-application-inventory-report-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/apps/get-application-inventory-report.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "get-application-inventory-report", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph, retrieves all managed devices and their installed applications,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/get-device-compliance-report-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/compliance/get-device-compliance-report.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "get-device-compliance-report", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph, retrieves managed devices and their compliance status,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/get-devices-by-scopetag-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/devices/get-devices-by-scopetag.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "get-devices-by-scopetag", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and retrieves all managed devices from Intune,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/get-duplicate-applications-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/apps/get-duplicate-applications.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "get-duplicate-applications", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph, retrieves all applications uploaded to Intune,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/get-stale-devices-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/devices/get-stale-devices.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "get-stale-devices", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and retrieves all managed devices from Intune,", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/rotate-bitlocker-keys-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/security/rotate-bitlocker-keys.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "rotate-bitlocker-keys", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Intune via Graph API and rotates the BitLocker keys for all managed Windows devices.", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/stale-device-cleanup-alert-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/notification/stale-device-cleanup-alert.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "stale-device-cleanup-alert", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script is designed to run as a scheduled Azure Automation runbook that monitors devices in", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/sync-devices-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/operational/sync-devices.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "sync-devices", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and triggers synchronization operations on targeted devices.", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /azure-templates/wipe-devices-azure-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "logVerbose": false, 4 | "runbookType": "PowerShell", 5 | "logProgress": false, 6 | "scriptUri": "https://raw.githubusercontent.com/ugurkocde/IntuneAutomation/main/scripts/operational/wipe-devices.ps1" 7 | }, 8 | "outputs": { 9 | "runbookName": { 10 | "type": "string", 11 | "value": "[parameters('runbookName')]" 12 | }, 13 | "runbookUrl": { 14 | "type": "string", 15 | "value": "[concat('https://portal.azure.com/#@', subscription().tenantId, '/resource/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Automation/automationAccounts/', parameters('automationAccountName'), '/runbooks/', parameters('runbookName'))]" 16 | }, 17 | "automationAccountName": { 18 | "type": "string", 19 | "value": "[parameters('automationAccountName')]" 20 | }, 21 | "scriptSourceUrl": { 22 | "type": "string", 23 | "value": "[variables('scriptUri')]" 24 | }, 25 | "deploymentInstructions": { 26 | "type": "string", 27 | "value": "Runbook created and published successfully! If the runbook content appears empty, manually import from the source URL provided in the outputs. Next steps: 1) Ensure your Automation Account has Managed Identity enabled, 2) Assign required Microsoft Graph permissions to the Managed Identity, 3) Test the runbook." 28 | } 29 | }, 30 | "parameters": { 31 | "runbookName": { 32 | "defaultValue": "wipe-devices", 33 | "type": "string", 34 | "metadata": { 35 | "description": "Name for the new runbook (will be created in the Automation Account)" 36 | } 37 | }, 38 | "automationAccountName": { 39 | "type": "string", 40 | "metadata": { 41 | "description": "Name of your existing Azure Automation Account (must exist in the selected resource group)" 42 | } 43 | }, 44 | "runbookDescription": { 45 | "defaultValue": "This script connects to Microsoft Graph and triggers remote wipe operations on targeted devices.", 46 | "type": "string", 47 | "metadata": { 48 | "description": "Description of the runbook" 49 | } 50 | }, 51 | "location": { 52 | "defaultValue": "[resourceGroup().location]", 53 | "type": "string", 54 | "metadata": { 55 | "description": "Location for the runbook deployment" 56 | } 57 | } 58 | }, 59 | "resources": [ 60 | { 61 | "location": "[parameters('location')]", 62 | "type": "Microsoft.Automation/automationAccounts/runbooks", 63 | "name": "[concat(parameters('automationAccountName'), '/', parameters('runbookName'))]", 64 | "apiVersion": "2023-11-01", 65 | "properties": { 66 | "logVerbose": "[variables('logVerbose')]", 67 | "runbookType": "[variables('runbookType')]", 68 | "logProgress": "[variables('logProgress')]", 69 | "description": "[parameters('runbookDescription')]", 70 | "publishContentLink": { 71 | "uri": "[variables('scriptUri')]" 72 | } 73 | } 74 | } 75 | ], 76 | "contentVersion": "1.0.0.0", 77 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 78 | } 79 | -------------------------------------------------------------------------------- /grant-permissions-managed-identity.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory = $true, HelpMessage = "Display name of the User Assigned Managed Identity")] 3 | [string]$ManagedIdentityDisplayName, 4 | 5 | [Parameter(Mandatory = $false, HelpMessage = "Custom permissions to assign (if not provided, default Intune permissions will be used)")] 6 | [string[]]$CustomPermissions = @(), 7 | 8 | [Parameter(Mandatory = $false, HelpMessage = "Enable verbose logging")] 9 | [switch]$EnableVerboseLogging, 10 | 11 | [Parameter(Mandatory = $false, HelpMessage = "Skip module installation check")] 12 | [switch]$SkipModuleCheck 13 | ) 14 | 15 | #------------------------------------------------------------------------------ 16 | # Configuration and Variables 17 | #------------------------------------------------------------------------------ 18 | 19 | # Default Intune permissions if no custom permissions are provided 20 | $DefaultIntunePermissions = @( 21 | "DeviceManagementManagedDevices.ReadWrite.All", 22 | "DeviceManagementManagedDevices.Read.All", 23 | "DeviceManagementConfiguration.ReadWrite.All", 24 | "DeviceManagementConfiguration.Read.All", 25 | "DeviceManagementApps.ReadWrite.All", 26 | "DeviceManagementApps.Read.All", 27 | "DeviceManagementServiceConfig.ReadWrite.All", 28 | "DeviceManagementServiceConfig.Read.All", 29 | "DeviceManagementRBAC.ReadWrite.All", 30 | "DeviceManagementManagedDevices.PrivilegedOperations.All", 31 | "BitlockerKey.Read.All", 32 | "Group.Read.All", 33 | "GroupMember.Read.All" 34 | ) 35 | 36 | # Use custom permissions if provided, otherwise use default Intune permissions 37 | $PermissionsToAssign = if ($CustomPermissions.Count -gt 0) { $CustomPermissions } else { $DefaultIntunePermissions } 38 | 39 | # Microsoft Graph App ID (constant) 40 | $GraphAppId = "00000003-0000-0000-c000-000000000000" 41 | 42 | #------------------------------------------------------------------------------ 43 | # Helper Functions 44 | #------------------------------------------------------------------------------ 45 | 46 | function Write-Log { 47 | param( 48 | [string]$Message, 49 | [string]$Level = "INFO" 50 | ) 51 | 52 | $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 53 | $logMessage = "[$timestamp] [$Level] $Message" 54 | 55 | switch ($Level) { 56 | "ERROR" { Write-Error $logMessage } 57 | "WARNING" { Write-Warning $logMessage } 58 | "VERBOSE" { if ($EnableVerboseLogging) { Write-Host $logMessage -ForegroundColor Cyan } } 59 | default { Write-Host $logMessage -ForegroundColor Green } 60 | } 61 | } 62 | 63 | function Install-RequiredModule { 64 | param( 65 | [string]$ModuleName 66 | ) 67 | 68 | try { 69 | Write-Log "Checking for module: $ModuleName" -Level "VERBOSE" 70 | 71 | if (-not(Get-Module -ListAvailable -Name $ModuleName)) { 72 | Write-Log "Installing module: $ModuleName" 73 | Install-Module -Name $ModuleName -Scope CurrentUser -AllowClobber -Force -ErrorAction Stop 74 | } 75 | 76 | Write-Log "Importing module: $ModuleName" -Level "VERBOSE" 77 | Import-Module $ModuleName -ErrorAction Stop 78 | Write-Log "Successfully loaded module: $ModuleName" 79 | 80 | } 81 | catch { 82 | Write-Log "Failed to install/import module $ModuleName`: $($_.Exception.Message)" -Level "ERROR" 83 | throw 84 | } 85 | } 86 | 87 | function Connect-ToServices { 88 | try { 89 | Write-Log "Connecting to Azure Account..." 90 | Connect-AzAccount -UseDeviceAuthentication -ErrorAction Stop 91 | 92 | Write-Log "Connecting to Microsoft Graph..." 93 | Connect-MgGraph -Scopes "AppRoleAssignment.ReadWrite.All", "Application.Read.All" -UseDeviceCode -ErrorAction Stop 94 | 95 | Write-Log "Successfully connected to Azure and Microsoft Graph" 96 | 97 | } 98 | catch { 99 | Write-Log "Failed to connect to services: $($_.Exception.Message)" -Level "ERROR" 100 | throw 101 | } 102 | } 103 | 104 | function Get-ManagedIdentityServicePrincipal { 105 | param([string]$DisplayName) 106 | 107 | try { 108 | Write-Log "Looking for managed identity: $DisplayName" 109 | $managedIdentity = Get-AzADServicePrincipal -DisplayName $DisplayName -ErrorAction Stop 110 | 111 | if (-not $managedIdentity) { 112 | throw "Managed Identity with display name '$DisplayName' not found" 113 | } 114 | 115 | Write-Log "Found managed identity: $($managedIdentity.DisplayName) (ID: $($managedIdentity.Id))" 116 | return $managedIdentity 117 | 118 | } 119 | catch { 120 | Write-Log "Failed to retrieve managed identity: $($_.Exception.Message)" -Level "ERROR" 121 | throw 122 | } 123 | } 124 | 125 | function Get-GraphServicePrincipal { 126 | try { 127 | Write-Log "Getting Microsoft Graph service principal..." 128 | $graphSPN = Get-MgServicePrincipal -Filter "AppId eq '$GraphAppId'" -ErrorAction Stop 129 | 130 | if (-not $graphSPN) { 131 | throw "Microsoft Graph service principal not found" 132 | } 133 | 134 | Write-Log "Found Microsoft Graph service principal (ID: $($graphSPN.Id))" 135 | return $graphSPN 136 | 137 | } 138 | catch { 139 | Write-Log "Failed to retrieve Graph service principal: $($_.Exception.Message)" -Level "ERROR" 140 | throw 141 | } 142 | } 143 | 144 | function Grant-AppRoleAssignment { 145 | param( 146 | [object]$ManagedIdentity, 147 | [object]$GraphServicePrincipal, 148 | [string[]]$Permissions 149 | ) 150 | 151 | $successCount = 0 152 | $failureCount = 0 153 | 154 | foreach ($permission in $Permissions) { 155 | try { 156 | Write-Log "Processing permission: $permission" -Level "VERBOSE" 157 | 158 | # Find the app role for this permission 159 | $appRole = $GraphServicePrincipal.AppRoles | 160 | Where-Object { $_.Value -eq $permission -and $_.AllowedMemberTypes -contains "Application" } 161 | 162 | if (-not $appRole) { 163 | Write-Log "App role not found for permission: $permission" -Level "WARNING" 164 | $failureCount++ 165 | continue 166 | } 167 | 168 | # Check if assignment already exists 169 | $existingAssignment = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentity.Id | 170 | Where-Object { $_.AppRoleId -eq $appRole.Id -and $_.ResourceId -eq $GraphServicePrincipal.Id } 171 | 172 | if ($existingAssignment) { 173 | Write-Log "Permission '$permission' already assigned, skipping..." -Level "VERBOSE" 174 | $successCount++ 175 | continue 176 | } 177 | 178 | # Create the app role assignment 179 | $bodyParam = @{ 180 | PrincipalId = $ManagedIdentity.Id 181 | ResourceId = $GraphServicePrincipal.Id 182 | AppRoleId = $appRole.Id 183 | } 184 | 185 | New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentity.Id -BodyParameter $bodyParam -ErrorAction Stop 186 | Write-Log "Successfully assigned permission: $permission" 187 | $successCount++ 188 | 189 | } 190 | catch { 191 | Write-Log "Failed to assign permission '$permission': $($_.Exception.Message)" -Level "ERROR" 192 | $failureCount++ 193 | } 194 | } 195 | 196 | Write-Log "Permission assignment completed. Success: $successCount, Failures: $failureCount" 197 | 198 | if ($failureCount -gt 0) { 199 | Write-Log "Some permissions failed to assign. Please review the errors above." -Level "WARNING" 200 | } 201 | } 202 | 203 | #------------------------------------------------------------------------------ 204 | # Main Script Execution 205 | #------------------------------------------------------------------------------ 206 | 207 | try { 208 | Write-Log "Starting Managed Identity Permission Assignment Script" 209 | Write-Log "Target Managed Identity: $ManagedIdentityDisplayName" 210 | Write-Log "Permissions to assign: $($PermissionsToAssign -join ', ')" 211 | 212 | # Install required modules 213 | if (-not $SkipModuleCheck) { 214 | Write-Log "Installing/checking required modules..." 215 | $requiredModules = @("Az.Accounts", "Az.Resources", "Microsoft.Graph.Applications") 216 | 217 | foreach ($module in $requiredModules) { 218 | Install-RequiredModule -ModuleName $module 219 | } 220 | } 221 | else { 222 | Write-Log "Skipping module installation check as requested" 223 | } 224 | 225 | # Connect to services 226 | Connect-ToServices 227 | 228 | # Get the managed identity 229 | $managedIdentity = Get-ManagedIdentityServicePrincipal -DisplayName $ManagedIdentityDisplayName 230 | 231 | # Get the Microsoft Graph service principal 232 | $graphServicePrincipal = Get-GraphServicePrincipal 233 | 234 | # Grant permissions 235 | Grant-AppRoleAssignment -ManagedIdentity $managedIdentity -GraphServicePrincipal $graphServicePrincipal -Permissions $PermissionsToAssign 236 | 237 | Write-Log "Script completed successfully!" 238 | 239 | } 240 | catch { 241 | Write-Log "Script execution failed: $($_.Exception.Message)" -Level "ERROR" 242 | exit 1 243 | } 244 | 245 | #------------------------------------------------------------------------------ 246 | # Example Usage: 247 | #------------------------------------------------------------------------------ 248 | <# 249 | # Basic usage with default Intune permissions: 250 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "MyIntuneAutomation" 251 | 252 | # With custom permissions: 253 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "MyAutomation" -CustomPermissions @("User.Read.All", "Group.Read.All") 254 | 255 | # With verbose logging: 256 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "MyAutomation" -EnableVerboseLogging 257 | 258 | # Skip module check (if modules are already installed): 259 | .\grant-permissions-managed-identity.ps1 -ManagedIdentityDisplayName "MyAutomation" -SkipModuleCheck 260 | #> -------------------------------------------------------------------------------- /permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "DeviceManagementManagedDevices.ReadWrite.All": { 3 | "displayName": "Read and write Microsoft Intune devices", 4 | "description": "Allows the app to read and write the properties of devices managed by Microsoft Intune, without a signed-in user. Does not allow high impact operations such as remote wipe and password reset on the device's owner" 5 | }, 6 | "DeviceManagementManagedDevices.Read.All": { 7 | "displayName": "Read Microsoft Intune devices", 8 | "description": "Allows the app to read the properties of devices managed by Microsoft Intune, without a signed-in user." 9 | }, 10 | "DeviceManagementConfiguration.ReadWrite.All": { 11 | "displayName": "Read and write Microsoft Intune device configuration and policies", 12 | "description": "Allows the app to read and write properties of Microsoft Intune-managed device configuration and device compliance policies and their assignment to groups, without a signed-in user." 13 | }, 14 | "DeviceManagementConfiguration.Read.All": { 15 | "displayName": "Read Microsoft Intune device configuration and policies", 16 | "description": "Allows the app to read properties of Microsoft Intune-managed device configuration and device compliance policies and their assignment to groups, without a signed-in user." 17 | }, 18 | "DeviceManagementApps.ReadWrite.All": { 19 | "displayName": "Read and write Microsoft Intune apps", 20 | "description": "Allows the app to read and write the properties, group assignments and status of apps, app configurations and app protection policies managed by Microsoft Intune, without a signed-in user." 21 | }, 22 | "DeviceManagementApps.Read.All": { 23 | "displayName": "Read Microsoft Intune apps", 24 | "description": "Allows the app to read the properties, group assignments and status of apps, app configurations and app protection policies managed by Microsoft Intune, without a signed-in user." 25 | }, 26 | "DeviceManagementServiceConfig.ReadWrite.All": { 27 | "displayName": "Read and write Microsoft Intune configuration", 28 | "description": "Allows the app to read and write Microsoft Intune service properties including device enrollment and third party service connection configuration, without a signed-in user." 29 | }, 30 | "DeviceManagementServiceConfig.Read.All": { 31 | "displayName": "Read Microsoft Intune configuration", 32 | "description": "Allows the app to read Microsoft Intune service properties including device enrollment and third party service connection configuration, without a signed-in user." 33 | }, 34 | "DeviceManagementRBAC.ReadWrite.All": { 35 | "displayName": "Read and write Microsoft Intune RBAC settings", 36 | "description": "Allows the app to read and write the properties relating to the Microsoft Intune Role-Based Access Control (RBAC) settings, without a signed-in user." 37 | }, 38 | "DeviceManagementManagedDevices.PrivilegedOperations.All": { 39 | "displayName": "Perform user-impacting remote actions on Microsoft Intune devices", 40 | "description": "Allows the app to perform remote high impact actions such as wiping the device or resetting the passcode on devices managed by Microsoft Intune, without a signed-in user." 41 | }, 42 | "BitlockerKey.Read.All": { 43 | "displayName": "Read all BitLocker keys", 44 | "description": "Allows an app to read BitLocker keys for all devices, without a signed-in user. Allows read of the recovery key." 45 | }, 46 | "Group.Read.All": { 47 | "displayName": "Read all groups", 48 | "description": "Allows the app to read group properties and memberships, and read\u00a0conversations for all groups, without a signed-in user." 49 | }, 50 | "GroupMember.Read.All": { 51 | "displayName": "Read all group memberships", 52 | "description": "Allows the app to read memberships and basic group properties for all groups without a signed-in user." 53 | }, 54 | "Mail.Send": { 55 | "displayName": "Send mail", 56 | "description": "Allows the app to send mail as the signed-in user, without a signed-in user." 57 | } 58 | } -------------------------------------------------------------------------------- /scripts/monitoring/check-applecare-warranty-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Check AppleCare Warranty Status 4 | # SYNOPSIS: Checks Apple warranty and AppleCare status on macOS devices 5 | # DESCRIPTION: This script checks the warranty status of Apple devices by reading the local 6 | # warranty information stored by macOS. It retrieves the coverage end date 7 | # and displays it in a user-friendly format. The script is designed to work 8 | # with Intune-managed macOS devices as a custom attribute. 9 | # TAGS: Monitoring,Device 10 | # PLATFORM: macOS 11 | # MIN_OS_VERSION: 10.15 12 | # AUTHOR: Ugur Koc 13 | # VERSION: 1.0 14 | # LASTUPDATE: 2025-06-02 15 | # CHANGELOG: 16 | # 1.0 - Initial release 17 | # 18 | # EXAMPLE: 19 | # ./check-applecare-warranty-status.sh 20 | # Checks the warranty status and outputs coverage expiration dates 21 | # 22 | # NOTES: 23 | # - Script reads warranty information from macOS system files 24 | # - No external dependencies required 25 | # - Designed for Intune custom attributes (single line output) 26 | # - Works when run as root or user context 27 | # - For more scripts and guides, visit: IntuneMacAdmins.com 28 | # - Source: https://community.jamf.com/t5/jamf-pro/collecting-warranty-status/m-p/298357#M263560 29 | 30 | # ============================================================================ 31 | # VARIABLES AND INITIALIZATION 32 | # ============================================================================ 33 | 34 | # Function to check warranty for a specific user 35 | check_user_warranty() { 36 | local user_home="$1" 37 | local warrantyDir="$user_home/Library/Application Support/com.apple.NewDeviceOutreach" 38 | 39 | # Check if the directory exists 40 | if [ ! -d "$warrantyDir" ]; then 41 | return 1 42 | fi 43 | 44 | # Find warranty files 45 | local warrantyFiles 46 | warrantyFiles=$(find "$warrantyDir" -maxdepth 1 -name "*_Warranty*" -type f 2>/dev/null) 47 | 48 | if [ -z "$warrantyFiles" ]; then 49 | return 1 50 | fi 51 | 52 | # Get the most recent warranty file 53 | local latestFile 54 | latestFile=$(echo "$warrantyFiles" | xargs ls -t 2>/dev/null | head -n1) 55 | 56 | if [ -z "$latestFile" ]; then 57 | return 1 58 | fi 59 | 60 | # Read the coverage end date 61 | local expires 62 | expires=$(defaults read "$latestFile" coverageEndDate 2>/dev/null || echo "") 63 | 64 | if [ -n "$expires" ]; then 65 | # Convert epoch to ISO-8601 format for better compatibility 66 | local ACexpires 67 | ACexpires=$(date -r "$expires" '+%Y-%m-%d' 2>/dev/null || echo "") 68 | 69 | if [ -n "$ACexpires" ]; then 70 | # Check if warranty has expired 71 | local currentDate 72 | currentDate=$(date +%s) 73 | if [ "$expires" -lt "$currentDate" ]; then 74 | echo "Expired: $ACexpires" 75 | else 76 | echo "Expires: $ACexpires" 77 | fi 78 | return 0 79 | fi 80 | fi 81 | 82 | return 1 83 | } 84 | 85 | # ============================================================================ 86 | # MAIN SCRIPT LOGIC 87 | # ============================================================================ 88 | 89 | # Try to get warranty information 90 | # First, try the current console user 91 | loggedInUser=$(stat -f "%Su" /dev/console 2>/dev/null) 92 | 93 | if [ -n "$loggedInUser" ] && [ "$loggedInUser" != "root" ] && [ "$loggedInUser" != "_windowserver" ]; then 94 | # Check logged in user's warranty 95 | userHome=$(dscl . -read /Users/"$loggedInUser" NFSHomeDirectory 2>/dev/null | awk '{print $2}') 96 | if [ -n "$userHome" ] && [ -d "$userHome" ]; then 97 | result=$(check_user_warranty "$userHome") 98 | if [ -n "$result" ]; then 99 | echo "$result" 100 | exit 0 101 | fi 102 | fi 103 | fi 104 | 105 | # If no console user or warranty not found, check all user directories 106 | for userHome in /Users/*; do 107 | # Skip system directories 108 | if [[ "$userHome" == "/Users/Shared" ]] || [[ "$userHome" == "/Users/Guest" ]]; then 109 | continue 110 | fi 111 | 112 | if [ -d "$userHome" ]; then 113 | result=$(check_user_warranty "$userHome") 114 | if [ -n "$result" ]; then 115 | echo "$result" 116 | exit 0 117 | fi 118 | fi 119 | done 120 | 121 | # If we get here, no warranty information was found 122 | echo "No warranty information" 123 | exit 0 -------------------------------------------------------------------------------- /scripts/monitoring/check-available-msupdate-updates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Check Available Microsoft Updates 4 | # SYNOPSIS: Checks for available updates for Microsoft applications via MAU 5 | # DESCRIPTION: This script uses Microsoft AutoUpdate (MAU) to check for available updates 6 | # for Microsoft Office applications and other Microsoft software on macOS. 7 | # It runs the msupdate command in the context of the logged-in user to ensure 8 | # proper access to user-specific update information. Results are formatted 9 | # for Intune custom attributes to provide visibility into pending updates. 10 | # TAGS: Monitoring,Updates 11 | # PLATFORM: macOS 12 | # MIN_OS_VERSION: 10.15 13 | # AUTHOR: Ugur Koc 14 | # VERSION: 1.0 15 | # LASTUPDATE: 2025-06-04 16 | # CHANGELOG: 17 | # 1.0 - Initial release 18 | # 19 | # EXAMPLE: 20 | # ./check-available-msupdate-updates.sh 21 | # Checks for available Microsoft application updates and outputs the list 22 | # 23 | # NOTES: 24 | # - Requires Microsoft AutoUpdate to be installed on the device 25 | # - Runs msupdate in the context of the logged-in user 26 | # - Designed for Intune custom attributes (single line output) 27 | # - Returns "No updates available" or lists available updates 28 | # - For more scripts and guides, visit: IntuneMacAdmins.com 29 | 30 | # ============================================================================ 31 | # VARIABLES AND INITIALIZATION 32 | # ============================================================================ 33 | 34 | # Path to msupdate CLI 35 | MSUPDATE="/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate" 36 | 37 | # Get the current console user (if needed) 38 | loggedInUser=$(stat -f "%Su" /dev/console 2>/dev/null) 39 | 40 | # ============================================================================ 41 | # FUNCTIONS 42 | # ============================================================================ 43 | 44 | # Function to output result (for Intune custom attributes) 45 | output_result() { 46 | # For Intune custom attributes, output should be a single line 47 | echo "$1" 48 | exit 0 49 | } 50 | 51 | # Function to check if MAU is installed 52 | check_mau_installed() { 53 | if [[ ! -f "$MSUPDATE" ]]; then 54 | output_result "Microsoft AutoUpdate not installed" 55 | fi 56 | } 57 | 58 | # Function to check if msupdate is executable 59 | check_msupdate_executable() { 60 | if [[ ! -x "$MSUPDATE" ]]; then 61 | output_result "Error: msupdate not executable" 62 | fi 63 | } 64 | 65 | # Function to validate user context 66 | validate_user_context() { 67 | if [ -z "$loggedInUser" ] || [ "$loggedInUser" = "root" ] || [ "$loggedInUser" = "_windowserver" ]; then 68 | output_result "Error: No user logged in" 69 | fi 70 | } 71 | 72 | # ============================================================================ 73 | # MAIN SCRIPT LOGIC 74 | # ============================================================================ 75 | 76 | main() { 77 | # Check if MAU is installed 78 | check_mau_installed 79 | 80 | # Check if msupdate is executable 81 | check_msupdate_executable 82 | 83 | # Validate user context 84 | validate_user_context 85 | 86 | # Get user ID for the logged-in user 87 | local user_id 88 | user_id=$(id -u "$loggedInUser" 2>/dev/null) 89 | 90 | if [[ -z "$user_id" ]]; then 91 | output_result "Error: Unable to get user ID" 92 | fi 93 | 94 | # Run msupdate using the user's launchctl session 95 | local raw_output 96 | if ! raw_output=$(launchctl asuser "$user_id" sudo -u "$loggedInUser" "$MSUPDATE" --list 2>&1); then 97 | # Check for specific error messages 98 | if echo "$raw_output" | grep -q "Failed to connect"; then 99 | output_result "Error: Failed to connect to MAU service" 100 | else 101 | output_result "Error: Unable to check for updates" 102 | fi 103 | fi 104 | 105 | # Check if "No updates available" is in the output 106 | if echo "$raw_output" | grep -q "No updates available"; then 107 | output_result "No updates available" 108 | else 109 | # Process available updates for single-line output 110 | # Extract update information and format for Intune 111 | local updates 112 | updates=$(echo "$raw_output" | grep -E "^\s*[A-Za-z]" | grep -v "Updates available:" | tr '\n' ' ' | sed 's/ */ /g' | xargs) 113 | 114 | if [[ -n "$updates" ]]; then 115 | output_result "Updates available: $updates" 116 | else 117 | output_result "Updates available (check MAU for details)" 118 | fi 119 | fi 120 | } 121 | 122 | # ============================================================================ 123 | # ERROR HANDLING 124 | # ============================================================================ 125 | 126 | # For Intune custom attributes - handle errors gracefully 127 | trap 'output_result "Error: Script failed"' ERR 128 | 129 | # ============================================================================ 130 | # SCRIPT EXECUTION 131 | # ============================================================================ 132 | 133 | # Only run main if script is executed directly (not sourced) 134 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 135 | main "$@" 136 | fi 137 | 138 | # Exit successfully (if not using output_result) 139 | exit 0 140 | -------------------------------------------------------------------------------- /scripts/monitoring/check-msupdate-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Check Microsoft AutoUpdate Status 4 | # SYNOPSIS: Checks the status of Microsoft AutoUpdate (MAU) on macOS devices 5 | # DESCRIPTION: This script retrieves and displays the current status of Microsoft AutoUpdate (MAU), 6 | # including the installed version, update channel configuration, and the timestamp 7 | # of the last update check. The information is formatted for Intune custom attributes 8 | # to provide visibility into the update configuration of Microsoft applications. 9 | # TAGS: Monitoring,Updates 10 | # PLATFORM: macOS 11 | # MIN_OS_VERSION: 10.15 12 | # AUTHOR: Ugur Koc 13 | # VERSION: 1.0 14 | # LASTUPDATE: 2025-06-04 15 | # CHANGELOG: 16 | # 1.0 - Initial release 17 | # 18 | # EXAMPLE: 19 | # ./check-msupdate-status.sh 20 | # Outputs MAU version, channel, and last update check time 21 | # 22 | # NOTES: 23 | # - Requires Microsoft AutoUpdate to be installed on the device 24 | # - Uses the msupdate CLI tool provided with MAU 25 | # - Designed for Intune custom attributes (single line output) 26 | # - Output format: MAU Version: X.X.X | Channel: ChannelName | Last Update Check: DateTime 27 | # - For more scripts and guides, visit: IntuneMacAdmins.com 28 | 29 | # ============================================================================ 30 | # VARIABLES AND INITIALIZATION 31 | # ============================================================================ 32 | 33 | # Path to msupdate CLI 34 | MSUPDATE="/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate" 35 | 36 | # ============================================================================ 37 | # FUNCTIONS 38 | # ============================================================================ 39 | 40 | # Function to output result (for Intune custom attributes) 41 | output_result() { 42 | # For Intune custom attributes, output should be a single line 43 | echo "$1" 44 | exit 0 45 | } 46 | 47 | # Function to check if MAU is installed 48 | check_mau_installed() { 49 | if [[ ! -f "$MSUPDATE" ]]; then 50 | output_result "Microsoft AutoUpdate not installed" 51 | fi 52 | } 53 | 54 | # Function to check if msupdate is executable 55 | check_msupdate_executable() { 56 | if [[ ! -x "$MSUPDATE" ]]; then 57 | output_result "Error: msupdate not executable" 58 | fi 59 | } 60 | 61 | # ============================================================================ 62 | # MAIN SCRIPT LOGIC 63 | # ============================================================================ 64 | 65 | main() { 66 | # Check if MAU is installed 67 | check_mau_installed 68 | 69 | # Check if msupdate is executable 70 | check_msupdate_executable 71 | 72 | # Get the configuration output 73 | local config_output 74 | if ! config_output=$("$MSUPDATE" --config 2>/dev/null) || [[ -z "$config_output" ]]; then 75 | output_result "Error: Unable to retrieve MAU configuration" 76 | fi 77 | 78 | # Extract MAU version 79 | local mau_version 80 | mau_version=$(echo "$config_output" | grep "AutoUpdateVersion =" | awk -F'"' '{print $2}') 81 | if [[ -z "$mau_version" ]]; then 82 | mau_version="Unknown" 83 | fi 84 | 85 | # Extract channel name 86 | local channel_name 87 | channel_name=$(echo "$config_output" | grep "ChannelName = " | head -n 1 | awk -F'=' '{print $2}' | sed 's/;//' | xargs) 88 | if [[ -z "$channel_name" ]]; then 89 | channel_name="Unknown" 90 | fi 91 | 92 | # Extract last update check time 93 | local last_check 94 | last_check=$(echo "$config_output" | grep "LastCheckForUpdates =" | awk -F'"' '{print $2}') 95 | if [[ -z "$last_check" ]]; then 96 | last_check="Never" 97 | fi 98 | 99 | # Format and output the result 100 | output_result "MAU Version: $mau_version | Channel: $channel_name | Last Update Check: $last_check" 101 | } 102 | 103 | # ============================================================================ 104 | # ERROR HANDLING 105 | # ============================================================================ 106 | 107 | # For Intune custom attributes - handle errors gracefully 108 | trap 'output_result "Error: Script failed"' ERR 109 | 110 | # ============================================================================ 111 | # SCRIPT EXECUTION 112 | # ============================================================================ 113 | 114 | # Only run main if script is executed directly (not sourced) 115 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 116 | main "$@" 117 | fi 118 | 119 | # Exit successfully (if not using output_result) 120 | exit 0 121 | -------------------------------------------------------------------------------- /scripts/monitoring/check-network-requirements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Check Network Requirements 4 | # SYNOPSIS: Checks connectivity to Apple security and software update services 5 | # DESCRIPTION: This script validates connectivity to Apple's critical security and software update 6 | # services by testing TCP connections to required endpoints. It checks both security 7 | # services (OCSP, CRL, PPQ) and OS/software update services. Results are formatted 8 | # for Intune custom attributes to provide visibility into network connectivity issues 9 | # that may prevent proper device security and update functionality. 10 | # TAGS: Monitoring,Network 11 | # PLATFORM: macOS 12 | # MIN_OS_VERSION: 10.15 13 | # AUTHOR: Ugur Koc 14 | # VERSION: 1.0 15 | # LASTUPDATE: 2025-06-04 16 | # CHANGELOG: 17 | # 1.0 - Initial release 18 | # 19 | # EXAMPLE: 20 | # ./check-network-requirements.sh 21 | # Checks connectivity to Apple services and outputs reachability status 22 | # 23 | # NOTES: 24 | # - Tests TCP connectivity on port 443 for all Apple service endpoints 25 | # - Groups results by security services and update services 26 | # - Designed for Intune custom attributes (single line output) 27 | # - Uses nc (netcat) for TCP connection testing with 2 second timeout 28 | # - No external dependencies required beyond standard macOS tools 29 | # - For more scripts and guides, visit: IntuneMacAdmins.com 30 | 31 | # ============================================================================ 32 | # VARIABLES AND INITIALIZATION 33 | # ============================================================================ 34 | 35 | # Initialize unreachable arrays for each category 36 | security_unreachable=() 37 | update_unreachable=() 38 | 39 | # ============================================================================ 40 | # FUNCTIONS 41 | # ============================================================================ 42 | 43 | # Function to output result (for Intune custom attributes) 44 | output_result() { 45 | # For Intune custom attributes, output should be a single line 46 | echo "$1" 47 | exit 0 48 | } 49 | 50 | # Function to check TCP connectivity 51 | check_tcp_connection() { 52 | local domain="$1" 53 | local port="$2" 54 | 55 | # Use nc (netcat) to test TCP connection with 2 second timeout 56 | if nc -zw2 "$domain" "$port" 2>/dev/null; then 57 | return 0 58 | else 59 | return 1 60 | fi 61 | } 62 | 63 | # Function to check required commands 64 | check_prerequisites() { 65 | # Check for netcat availability 66 | if ! command -v nc >/dev/null 2>&1; then 67 | output_result "Error: Required command 'nc' not found" 68 | fi 69 | } 70 | 71 | # ============================================================================ 72 | # MAIN SCRIPT LOGIC 73 | # ============================================================================ 74 | 75 | main() { 76 | # Check prerequisites 77 | check_prerequisites 78 | 79 | # Check security services 80 | if ! check_tcp_connection "ocsp.apple.com" "443"; then 81 | security_unreachable+=("ocsp.apple.com") 82 | fi 83 | 84 | if ! check_tcp_connection "crl.apple.com" "443"; then 85 | security_unreachable+=("crl.apple.com") 86 | fi 87 | 88 | if ! check_tcp_connection "ppq.apple.com" "443"; then 89 | security_unreachable+=("ppq.apple.com") 90 | fi 91 | 92 | if ! check_tcp_connection "api.apple-cloudkit.com" "443"; then 93 | security_unreachable+=("api.apple-cloudkit.com") 94 | fi 95 | 96 | # Check OS/Software update services 97 | if ! check_tcp_connection "osrecovery.apple.com" "443"; then 98 | update_unreachable+=("osrecovery.apple.com") 99 | fi 100 | 101 | if ! check_tcp_connection "oscdn.apple.com" "443"; then 102 | update_unreachable+=("oscdn.apple.com") 103 | fi 104 | 105 | if ! check_tcp_connection "swcdn.apple.com" "443"; then 106 | update_unreachable+=("swcdn.apple.com") 107 | fi 108 | 109 | if ! check_tcp_connection "swdist.apple.com" "443"; then 110 | update_unreachable+=("swdist.apple.com") 111 | fi 112 | 113 | if ! check_tcp_connection "swdownload.apple.com" "443"; then 114 | update_unreachable+=("swdownload.apple.com") 115 | fi 116 | 117 | if ! check_tcp_connection "swscan.apple.com" "443"; then 118 | update_unreachable+=("swscan.apple.com") 119 | fi 120 | 121 | if ! check_tcp_connection "updates.cdn-apple.com" "443"; then 122 | update_unreachable+=("updates.cdn-apple.com") 123 | fi 124 | 125 | # Format the output 126 | result="" 127 | 128 | # Check security services status 129 | if [ ${#security_unreachable[@]} -eq 0 ]; then 130 | result="Security services: All reachable" 131 | else 132 | security_list=$( 133 | IFS=, 134 | echo "${security_unreachable[*]}" 135 | ) 136 | result="Security services unreachable: $security_list" 137 | fi 138 | 139 | # Add update services status 140 | if [ ${#update_unreachable[@]} -eq 0 ]; then 141 | result="$result | Update services: All reachable" 142 | else 143 | update_list=$( 144 | IFS=, 145 | echo "${update_unreachable[*]}" 146 | ) 147 | result="$result | Update services unreachable: $update_list" 148 | fi 149 | 150 | # Output the result 151 | output_result "$result" 152 | } 153 | 154 | # ============================================================================ 155 | # ERROR HANDLING 156 | # ============================================================================ 157 | 158 | # For Intune custom attributes - handle errors gracefully 159 | trap 'output_result "Error: Script failed"' ERR 160 | 161 | # ============================================================================ 162 | # SCRIPT EXECUTION 163 | # ============================================================================ 164 | 165 | # Only run main if script is executed directly (not sourced) 166 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 167 | main "$@" 168 | fi 169 | 170 | # Exit successfully (if not using output_result) 171 | exit 0 172 | -------------------------------------------------------------------------------- /scripts/monitoring/check-xprotect-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Check XProtect and Security Status 4 | # SYNOPSIS: Checks XProtect, XProtect Remediator, MRT, and system security settings 5 | # DESCRIPTION: This script retrieves the current versions of macOS security components including 6 | # XProtect, XProtect Remediator, and MRT (Malware Removal Tool). Additionally, 7 | # it checks the status of critical security features like System Integrity Protection 8 | # (SIP), Gatekeeper, and FileVault. Results are formatted for Intune custom attributes 9 | # to provide comprehensive visibility into device security posture. 10 | # TAGS: Monitoring,Security 11 | # PLATFORM: macOS 12 | # MIN_OS_VERSION: 10.15 13 | # AUTHOR: Ugur Koc 14 | # VERSION: 1.0 15 | # LASTUPDATE: 2025-06-04 16 | # CHANGELOG: 17 | # 1.0 - Initial release 18 | # 19 | # EXAMPLE: 20 | # ./check-xprotect-status.sh 21 | # Outputs XProtect versions and security settings status 22 | # 23 | # NOTES: 24 | # - Requires root privileges to access certain security settings 25 | # - Checks XProtect, XProtect Remediator, MRT versions 26 | # - Reports SIP, Gatekeeper, and FileVault status 27 | # - Designed for Intune custom attributes (single line output) 28 | # - For more scripts and guides, visit: IntuneMacAdmins.com 29 | 30 | # ============================================================================ 31 | # VARIABLES AND INITIALIZATION 32 | # ============================================================================ 33 | 34 | # Get macOS version 35 | os_version=$(sw_vers -productVersion) 36 | 37 | # ============================================================================ 38 | # FUNCTIONS 39 | # ============================================================================ 40 | 41 | # Function to output result (for Intune custom attributes) 42 | output_result() { 43 | # For Intune custom attributes, output should be a single line 44 | echo "$1" 45 | exit 0 46 | } 47 | 48 | # Function to check if running as root 49 | check_root() { 50 | if [[ $EUID -ne 0 ]]; then 51 | output_result "Error: Root access required" 52 | fi 53 | } 54 | 55 | # Function to check plist value with error handling 56 | get_plist_value() { 57 | local plist="$1" 58 | local key="$2" 59 | 60 | if [ ! -f "$plist" ]; then 61 | return 1 62 | fi 63 | 64 | local value 65 | if ! value=$(/usr/libexec/PlistBuddy -c "Print :$key" "$plist" 2>/dev/null); then 66 | return 1 67 | fi 68 | echo "$value" 69 | } 70 | 71 | # Function to check XProtect version 72 | check_xprotect() { 73 | local xprotect_meta="/Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/Info.plist" 74 | local xprotect_version="" 75 | 76 | if [ -f "$xprotect_meta" ]; then 77 | xprotect_version=$(get_plist_value "$xprotect_meta" "CFBundleShortVersionString") 78 | fi 79 | 80 | echo -n "XProtect: v${xprotect_version:-Unknown}" 81 | } 82 | 83 | # Function to check XProtect Remediator 84 | check_xprotect_remediator() { 85 | local remediator_meta="/Library/Apple/System/Library/CoreServices/XProtect.app/Contents/Info.plist" 86 | local remediator_version="" 87 | 88 | if [ -f "$remediator_meta" ]; then 89 | remediator_version=$(get_plist_value "$remediator_meta" "CFBundleShortVersionString") 90 | fi 91 | 92 | echo -n " | XProtect Remediator: v${remediator_version:-Unknown}" 93 | } 94 | 95 | # Function to check MRT version 96 | check_mrt() { 97 | local mrt_meta="/Library/Apple/System/Library/CoreServices/MRT.app/Contents/Info.plist" 98 | local mrt_version="" 99 | 100 | if [ -f "$mrt_meta" ]; then 101 | mrt_version=$(get_plist_value "$mrt_meta" "CFBundleShortVersionString") 102 | fi 103 | 104 | echo -n " | MRT: v${mrt_version:-Unknown}" 105 | } 106 | 107 | # Function to check system security settings 108 | check_system_security() { 109 | local security_status="" 110 | 111 | # Check SIP status 112 | local sip_output 113 | sip_output=$(csrutil status 2>&1) 114 | if echo "$sip_output" | grep -q "System Integrity Protection status: enabled"; then 115 | security_status="SIP:Enabled" 116 | else 117 | security_status="SIP:Disabled" 118 | fi 119 | 120 | # Check Gatekeeper status 121 | if spctl --status 2>&1 | grep -q "enabled"; then 122 | security_status="$security_status,GK:Enabled" 123 | else 124 | security_status="$security_status,GK:Disabled" 125 | fi 126 | 127 | # Check FileVault status 128 | if fdesetup status | grep -q "On"; then 129 | security_status="$security_status,FV:Enabled" 130 | else 131 | security_status="$security_status,FV:Disabled" 132 | fi 133 | 134 | echo -n " | Security: $security_status" 135 | } 136 | 137 | # ============================================================================ 138 | # MAIN SCRIPT LOGIC 139 | # ============================================================================ 140 | 141 | main() { 142 | # Check root access 143 | check_root 144 | 145 | # Build output string 146 | local result="" 147 | 148 | # Add macOS version 149 | result="macOS: $os_version | " 150 | 151 | # Add XProtect information 152 | result="${result}$(check_xprotect)" 153 | result="${result}$(check_xprotect_remediator)" 154 | result="${result}$(check_mrt)" 155 | result="${result}$(check_system_security)" 156 | 157 | # Output the result 158 | output_result "$result" 159 | } 160 | 161 | # ============================================================================ 162 | # ERROR HANDLING 163 | # ============================================================================ 164 | 165 | # For Intune custom attributes - handle errors gracefully 166 | trap 'output_result "Error: Script failed"' ERR 167 | 168 | # ============================================================================ 169 | # SCRIPT EXECUTION 170 | # ============================================================================ 171 | 172 | # Only run main if script is executed directly (not sourced) 173 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 174 | main "$@" 175 | fi 176 | 177 | # Exit successfully (if not using output_result) 178 | exit 0 179 | -------------------------------------------------------------------------------- /scripts/monitoring/last-reboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: Get Last Reboot Time 4 | # SYNOPSIS: Retrieves the last system reboot time on macOS 5 | # DESCRIPTION: This script extracts the system boot time from the kernel and formats it 6 | # in a human-readable format. It uses the sysctl command to query the 7 | # kern.boottime value, which contains the timestamp of the last system boot. 8 | # The output is formatted for Intune custom attributes to provide visibility 9 | # into device uptime and reboot patterns. 10 | # TAGS: Monitoring,Device 11 | # PLATFORM: macOS 12 | # MIN_OS_VERSION: 10.15 13 | # AUTHOR: Ugur Koc 14 | # VERSION: 1.0 15 | # LASTUPDATE: 2025-06-04 16 | # CHANGELOG: 17 | # 1.0 - Initial release 18 | # 19 | # EXAMPLE: 20 | # ./last-reboot.sh 21 | # Outputs the last reboot time in YYYY-MM-DD HH:MM:SS format 22 | # 23 | # NOTES: 24 | # - Uses sysctl to query kern.boottime 25 | # - No external dependencies required 26 | # - Designed for Intune custom attributes (single line output) 27 | # - Time is displayed in local system timezone 28 | # - For more scripts and guides, visit: IntuneMacAdmins.com 29 | 30 | # ============================================================================ 31 | # VARIABLES AND INITIALIZATION 32 | # ============================================================================ 33 | 34 | # ============================================================================ 35 | # FUNCTIONS 36 | # ============================================================================ 37 | 38 | # Function to output result (for Intune custom attributes) 39 | output_result() { 40 | # For Intune custom attributes, output should be a single line 41 | echo "$1" 42 | exit 0 43 | } 44 | 45 | # Function to validate sysctl availability 46 | check_prerequisites() { 47 | if ! command -v sysctl >/dev/null 2>&1; then 48 | output_result "Error: sysctl command not found" 49 | fi 50 | } 51 | 52 | # Function to get boot time 53 | get_boot_time() { 54 | # Get the boot time from sysctl 55 | local boot_info 56 | boot_info=$(sysctl -n kern.boottime 2>/dev/null) 57 | 58 | if [[ -z "$boot_info" ]]; then 59 | output_result "Error: Unable to retrieve boot time" 60 | fi 61 | 62 | # Extract the timestamp (sec value) 63 | local timestamp 64 | timestamp=$(echo "$boot_info" | awk '{print $4}' | tr -d ',') 65 | 66 | # Validate timestamp 67 | if [[ ! "$timestamp" =~ ^[0-9]+$ ]]; then 68 | output_result "Error: Invalid boot time format" 69 | fi 70 | 71 | echo "$timestamp" 72 | } 73 | 74 | # ============================================================================ 75 | # MAIN SCRIPT LOGIC 76 | # ============================================================================ 77 | 78 | main() { 79 | # Check prerequisites 80 | check_prerequisites 81 | 82 | # Get the boot timestamp 83 | local timestamp 84 | timestamp=$(get_boot_time) 85 | 86 | # Convert timestamp to formatted date 87 | local formatted_date 88 | formatted_date=$(date -r "$timestamp" "+%Y-%m-%d %H:%M:%S" 2>/dev/null) 89 | 90 | if [[ -z "$formatted_date" ]]; then 91 | output_result "Error: Unable to format boot time" 92 | fi 93 | 94 | # Calculate uptime for additional context 95 | local current_time 96 | current_time=$(date +%s) 97 | local uptime_seconds=$((current_time - timestamp)) 98 | local uptime_days=$((uptime_seconds / 86400)) 99 | local uptime_hours=$(((uptime_seconds % 86400) / 3600)) 100 | 101 | # Format output with uptime information 102 | if [[ $uptime_days -gt 0 ]]; then 103 | output_result "Last Reboot: $formatted_date (${uptime_days}d ${uptime_hours}h ago)" 104 | else 105 | output_result "Last Reboot: $formatted_date (${uptime_hours}h ago)" 106 | fi 107 | } 108 | 109 | # ============================================================================ 110 | # ERROR HANDLING 111 | # ============================================================================ 112 | 113 | # For Intune custom attributes - handle errors gracefully 114 | trap 'output_result "Error: Script failed"' ERR 115 | 116 | # ============================================================================ 117 | # SCRIPT EXECUTION 118 | # ============================================================================ 119 | 120 | # Only run main if script is executed directly (not sourced) 121 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 122 | main "$@" 123 | fi 124 | 125 | # Exit successfully (if not using output_result) 126 | exit 0 127 | -------------------------------------------------------------------------------- /scripts/monitoring/local-admins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: List Local Admin Users 4 | # SYNOPSIS: Lists all users with local administrator privileges on macOS 5 | # DESCRIPTION: This script retrieves the list of users who are members of the local admin 6 | # group on macOS. It uses the Directory Service command line utility (dscl) 7 | # to query the admin group membership. The output is formatted for Intune 8 | # custom attributes to provide visibility into privileged access on managed devices. 9 | # TAGS: Monitoring,Security 10 | # PLATFORM: macOS 11 | # MIN_OS_VERSION: 10.15 12 | # AUTHOR: Ugur Koc 13 | # VERSION: 1.0 14 | # LASTUPDATE: 2025-06-04 15 | # CHANGELOG: 16 | # 1.0 - Initial release 17 | # 18 | # EXAMPLE: 19 | # ./local-admins.sh 20 | # Outputs the list of local admin users 21 | # 22 | # NOTES: 23 | # - Uses dscl to query admin group membership 24 | # - No external dependencies required 25 | # - Designed for Intune custom attributes (single line output) 26 | # - Includes count of admin users for quick assessment 27 | # - For more scripts and guides, visit: IntuneMacAdmins.com 28 | 29 | # ============================================================================ 30 | # VARIABLES AND INITIALIZATION 31 | # ============================================================================ 32 | 33 | # ============================================================================ 34 | # FUNCTIONS 35 | # ============================================================================ 36 | 37 | # Function to output result (for Intune custom attributes) 38 | output_result() { 39 | # For Intune custom attributes, output should be a single line 40 | echo "$1" 41 | exit 0 42 | } 43 | 44 | # Function to check prerequisites 45 | check_prerequisites() { 46 | if ! command -v dscl >/dev/null 2>&1; then 47 | output_result "Error: dscl command not found" 48 | fi 49 | } 50 | 51 | # Function to get admin users 52 | get_admin_users() { 53 | # Query the admin group membership 54 | local admin_output 55 | if ! admin_output=$(dscl . -read /Groups/admin GroupMembership 2>&1); then 56 | # Check if it's a permission issue 57 | if echo "$admin_output" | grep -q "eDSPermissionError"; then 58 | output_result "Error: Permission denied" 59 | else 60 | output_result "Error: Unable to query admin group" 61 | fi 62 | fi 63 | 64 | # Extract just the user list (remove "GroupMembership:" prefix) 65 | local admin_list 66 | admin_list=${admin_output#GroupMembership: } 67 | 68 | # Check if we got valid output 69 | if [[ -z "$admin_list" ]] || [[ "$admin_list" == "$admin_output" ]]; then 70 | output_result "Error: No admin users found or invalid format" 71 | fi 72 | 73 | echo "$admin_list" 74 | } 75 | 76 | # ============================================================================ 77 | # MAIN SCRIPT LOGIC 78 | # ============================================================================ 79 | 80 | main() { 81 | # Check prerequisites 82 | check_prerequisites 83 | 84 | # Get the list of admin users 85 | local admin_users 86 | admin_users=$(get_admin_users) 87 | 88 | # Count the number of admin users 89 | local admin_count 90 | admin_count=$(echo "$admin_users" | wc -w | tr -d ' ') 91 | 92 | # Format output for Intune custom attributes 93 | if [[ $admin_count -eq 0 ]]; then 94 | output_result "Admin Users: None found" 95 | elif [[ $admin_count -eq 1 ]]; then 96 | output_result "Admin Users (1): $admin_users" 97 | else 98 | # For multiple users, show count and list 99 | output_result "Admin Users ($admin_count): $admin_users" 100 | fi 101 | } 102 | 103 | # ============================================================================ 104 | # ERROR HANDLING 105 | # ============================================================================ 106 | 107 | # For Intune custom attributes - handle errors gracefully 108 | trap 'output_result "Error: Script failed"' ERR 109 | 110 | # ============================================================================ 111 | # SCRIPT EXECUTION 112 | # ============================================================================ 113 | 114 | # Only run main if script is executed directly (not sourced) 115 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 116 | main "$@" 117 | fi 118 | 119 | # Exit successfully (if not using output_result) 120 | exit 0 121 | -------------------------------------------------------------------------------- /scripts/security/rotate-bitlocker-keys.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .TITLE 3 | Rotate BitLocker Keys 4 | 5 | .SYNOPSIS 6 | Rotates BitLocker keys for all Windows devices in Intune using Graph API. 7 | 8 | .DESCRIPTION 9 | This script connects to Intune via Graph API and rotates the BitLocker keys for all managed Windows devices. 10 | The script retrieves all Windows devices from Intune and triggers BitLocker key rotation for each device. 11 | It provides real-time feedback on the rotation process and handles errors gracefully. 12 | 13 | .TAGS 14 | Security,Operational 15 | 16 | .MINROLE 17 | Intune Administrator 18 | 19 | .PERMISSIONS 20 | DeviceManagementManagedDevices.ReadWrite.All 21 | 22 | .AUTHOR 23 | Ugur Koc 24 | 25 | .VERSION 26 | 1.0 27 | 28 | .CHANGELOG 29 | 1.0 - Initial release 30 | 31 | .LASTUPDATE 32 | 2025-05-29 33 | 34 | .EXAMPLE 35 | .\rotate-bitlocker-keys.ps1 36 | Rotates BitLocker keys for all Windows devices in Intune 37 | 38 | .EXAMPLE 39 | .\rotate-bitlocker-keys.ps1 -DelaySeconds 5 40 | Rotates BitLocker keys with a 5-second delay between operations 41 | 42 | .NOTES 43 | - Requires Microsoft.Graph.Authentication module: Install-Module Microsoft.Graph.Authentication 44 | - Requires appropriate permissions in Azure AD 45 | - BitLocker key rotation is triggered immediately but may take time to complete on the device 46 | - The script will show real-time progress and results 47 | - Only Windows devices with BitLocker enabled will be processed 48 | - Disclaimer: This script is provided AS IS without warranty of any kind. Use it at your own risk. 49 | #> 50 | 51 | [CmdletBinding()] 52 | param( 53 | [Parameter(Mandatory = $false, HelpMessage = "Delay in seconds between BitLocker key rotation operations")] 54 | [int]$DelaySeconds = 2, 55 | 56 | [Parameter(Mandatory = $false, HelpMessage = "Force module installation without prompting")] 57 | [switch]$ForceModuleInstall 58 | ) 59 | 60 | # ============================================================================ 61 | # ENVIRONMENT DETECTION AND SETUP 62 | # ============================================================================ 63 | 64 | function Initialize-RequiredModule { 65 | <# 66 | .SYNOPSIS 67 | Ensures required modules are available and loaded 68 | #> 69 | param( 70 | [string[]]$ModuleNames, 71 | [bool]$IsAutomationEnvironment, 72 | [bool]$ForceInstall = $false 73 | ) 74 | 75 | foreach ($ModuleName in $ModuleNames) { 76 | Write-Verbose "Checking module: $ModuleName" 77 | 78 | # Check if module is available 79 | $module = Get-Module -ListAvailable -Name $ModuleName | Select-Object -First 1 80 | 81 | if (-not $module) { 82 | if ($IsAutomationEnvironment) { 83 | $errorMessage = @" 84 | Module '$ModuleName' is not available in this Azure Automation Account. 85 | 86 | To resolve this issue: 87 | 1. Go to Azure Portal 88 | 2. Navigate to your Automation Account 89 | 3. Go to 'Modules' > 'Browse Gallery' 90 | 4. Search for '$ModuleName' 91 | 5. Click 'Import' and wait for installation to complete 92 | 93 | Alternative: Use PowerShell to import the module: 94 | Import-Module Az.Automation 95 | Import-AzAutomationModule -AutomationAccountName "YourAccount" -ResourceGroupName "YourRG" -Name "$ModuleName" 96 | "@ 97 | throw $errorMessage 98 | } 99 | else { 100 | # Local environment - attempt to install 101 | Write-Information "Module '$ModuleName' not found. Attempting to install..." -InformationAction Continue 102 | 103 | if (-not $ForceInstall) { 104 | $response = Read-Host "Install module '$ModuleName'? (Y/N)" 105 | if ($response -notmatch '^[Yy]') { 106 | throw "Module '$ModuleName' is required but installation was declined." 107 | } 108 | } 109 | 110 | try { 111 | # Check if running as administrator for AllUsers scope 112 | $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 113 | $scope = if ($isAdmin) { "AllUsers" } else { "CurrentUser" } 114 | 115 | Write-Information "Installing '$ModuleName' in scope '$scope'..." -InformationAction Continue 116 | Install-Module -Name $ModuleName -Scope $scope -Force -AllowClobber -Repository PSGallery 117 | Write-Information "✓ Successfully installed '$ModuleName'" -InformationAction Continue 118 | } 119 | catch { 120 | throw "Failed to install module '$ModuleName': $($_.Exception.Message)" 121 | } 122 | } 123 | } 124 | 125 | # Import the module 126 | try { 127 | Write-Verbose "Importing module: $ModuleName" 128 | Import-Module -Name $ModuleName -Force -ErrorAction Stop 129 | Write-Verbose "✓ Successfully imported '$ModuleName'" 130 | } 131 | catch { 132 | throw "Failed to import module '$ModuleName': $($_.Exception.Message)" 133 | } 134 | } 135 | } 136 | 137 | # Detect execution environment 138 | if ($PSPrivateMetadata.JobId.Guid) { 139 | Write-Output "Running inside Azure Automation Runbook" 140 | $IsAzureAutomation = $true 141 | } 142 | else { 143 | Write-Information "Running locally in IDE or terminal" -InformationAction Continue 144 | $IsAzureAutomation = $false 145 | } 146 | 147 | # Initialize required modules 148 | $RequiredModules = @( 149 | "Microsoft.Graph.Authentication" 150 | ) 151 | 152 | try { 153 | Initialize-RequiredModule -ModuleNames $RequiredModules -IsAutomationEnvironment $IsAzureAutomation -ForceInstall $ForceModuleInstall 154 | Write-Verbose "✓ All required modules are available" 155 | } 156 | catch { 157 | Write-Error "Module initialization failed: $_" 158 | exit 1 159 | } 160 | 161 | # ============================================================================ 162 | # AUTHENTICATION 163 | # ============================================================================ 164 | 165 | try { 166 | if ($IsAzureAutomation) { 167 | # Azure Automation - Use Managed Identity 168 | Write-Output "Connecting to Microsoft Graph using Managed Identity..." 169 | Connect-MgGraph -Identity -NoWelcome -ErrorAction Stop 170 | Write-Output "✓ Successfully connected to Microsoft Graph using Managed Identity" 171 | } 172 | else { 173 | # Local execution - Use interactive authentication 174 | Write-Information "Connecting to Microsoft Graph with interactive authentication..." -InformationAction Continue 175 | $Scopes = @( 176 | "DeviceManagementManagedDevices.ReadWrite.All" 177 | ) 178 | Connect-MgGraph -Scopes $Scopes -NoWelcome -ErrorAction Stop 179 | Write-Information "✓ Successfully connected to Microsoft Graph" -InformationAction Continue 180 | } 181 | } 182 | catch { 183 | Write-Error "Failed to connect to Microsoft Graph: $($_.Exception.Message)" 184 | exit 1 185 | } 186 | 187 | # ============================================================================ 188 | # HELPER FUNCTIONS 189 | # ============================================================================ 190 | 191 | # Function to get all pages of results from Graph API 192 | function Get-MgGraphAllPage { 193 | param( 194 | [Parameter(Mandatory = $true)] 195 | [string]$Uri, 196 | [int]$DelayMs = 100 197 | ) 198 | 199 | $AllResults = @() 200 | $NextLink = $Uri 201 | $RequestCount = 0 202 | 203 | do { 204 | try { 205 | # Add delay to respect rate limits 206 | if ($RequestCount -gt 0) { 207 | Start-Sleep -Milliseconds $DelayMs 208 | } 209 | 210 | $Response = Invoke-MgGraphRequest -Uri $NextLink -Method GET 211 | $RequestCount++ 212 | 213 | if ($Response.value) { 214 | $AllResults += $Response.value 215 | } 216 | else { 217 | $AllResults += $Response 218 | } 219 | 220 | $NextLink = $Response.'@odata.nextLink' 221 | } 222 | catch { 223 | if ($_.Exception.Message -like "*429*" -or $_.Exception.Message -like "*throttled*") { 224 | Write-Information "`nRate limit hit, waiting 60 seconds..." -InformationAction Continue 225 | Start-Sleep -Seconds 60 226 | continue 227 | } 228 | Write-Warning "Error fetching data from $NextLink : $($_.Exception.Message)" 229 | break 230 | } 231 | } while ($NextLink) 232 | 233 | return $AllResults 234 | } 235 | 236 | # Function to rotate BitLocker keys for a device 237 | function Invoke-BitLockerKeyRotation { 238 | param( 239 | [Parameter(Mandatory = $true)] 240 | [string]$DeviceId, 241 | [Parameter(Mandatory = $true)] 242 | [string]$DeviceName 243 | ) 244 | 245 | try { 246 | $rotateUri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$DeviceId')/rotateBitLockerKeys" 247 | Invoke-MgGraphRequest -Method POST -Uri $rotateUri -ContentType "application/json" 248 | 249 | Write-Information "✓ Successfully rotated BitLocker keys for device: $DeviceName" -InformationAction Continue 250 | return $true 251 | } 252 | catch { 253 | Write-Warning "✗ Failed to rotate BitLocker keys for device $DeviceName : $($_.Exception.Message)" 254 | return $false 255 | } 256 | } 257 | 258 | # ============================================================================ 259 | # MAIN SCRIPT LOGIC 260 | # ============================================================================ 261 | 262 | try { 263 | Write-Information "Starting BitLocker key rotation process..." -InformationAction Continue 264 | 265 | # Get all managed Windows devices from Intune 266 | Write-Information "Retrieving all Windows devices from Intune..." -InformationAction Continue 267 | $devicesUri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$select=id,deviceName,operatingSystem&`$filter=operatingSystem eq 'Windows'" 268 | $managedDevices = Get-MgGraphAllPage -Uri $devicesUri 269 | 270 | if ($managedDevices.Count -eq 0) { 271 | Write-Warning "No Windows devices found in Intune." 272 | exit 0 273 | } 274 | 275 | Write-Information "✓ Found $($managedDevices.Count) Windows devices" -InformationAction Continue 276 | 277 | # Initialize counters 278 | $successCount = 0 279 | $failureCount = 0 280 | $totalDevices = $managedDevices.Count 281 | $currentDevice = 0 282 | 283 | # Process each device 284 | foreach ($device in $managedDevices) { 285 | $currentDevice++ 286 | $deviceId = $device.id 287 | $deviceName = $device.deviceName 288 | 289 | Write-Information "[$currentDevice/$totalDevices] Processing device: $deviceName" -InformationAction Continue 290 | 291 | # Rotate BitLocker keys 292 | $success = Invoke-BitLockerKeyRotation -DeviceId $deviceId -DeviceName $deviceName 293 | 294 | if ($success) { 295 | $successCount++ 296 | } 297 | else { 298 | $failureCount++ 299 | } 300 | 301 | # Add delay between operations if specified 302 | if ($DelaySeconds -gt 0 -and $currentDevice -lt $totalDevices) { 303 | Start-Sleep -Seconds $DelaySeconds 304 | } 305 | } 306 | 307 | # Display summary 308 | Write-Information "`n" -InformationAction Continue 309 | Write-Information "============================================" -InformationAction Continue 310 | Write-Information "BitLocker Key Rotation Summary" -InformationAction Continue 311 | Write-Information "============================================" -InformationAction Continue 312 | Write-Information "Total devices processed: $totalDevices" -InformationAction Continue 313 | Write-Information "Successful rotations: $successCount" -InformationAction Continue 314 | Write-Information "Failed rotations: $failureCount" -InformationAction Continue 315 | Write-Information "Success rate: $([math]::Round(($successCount / $totalDevices) * 100, 2))%" -InformationAction Continue 316 | Write-Information "============================================" -InformationAction Continue 317 | 318 | Write-Information "✓ BitLocker key rotation process completed" -InformationAction Continue 319 | } 320 | catch { 321 | Write-Error "Script execution failed: $($_.Exception.Message)" 322 | exit 1 323 | } 324 | finally { 325 | # Disconnect from Microsoft Graph 326 | try { 327 | Disconnect-MgGraph | Out-Null 328 | Write-Information "✓ Disconnected from Microsoft Graph" -InformationAction Continue 329 | } 330 | catch { 331 | # Ignore disconnection errors - this is expected behavior when already disconnected 332 | Write-Verbose "Graph disconnection completed (may have already been disconnected)" 333 | } 334 | } -------------------------------------------------------------------------------- /templates/NOTIFICATION_TEMPLATE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Notification Script Template Guide 2 | 3 | This template provides a comprehensive foundation for creating your own notification scripts that monitor specific aspects of your Microsoft Intune environment and send email alerts when conditions are met. 4 | 5 | ## 🎯 What This Template Provides 6 | 7 | - **Complete notification script structure** following project standards 8 | - **Azure Automation optimized** with Managed Identity support 9 | - **Professional HTML email templates** with responsive design 10 | - **Comprehensive error handling** and logging 11 | - **Rate limiting and pagination** for Microsoft Graph API calls 12 | - **Configurable thresholds and filtering** options 13 | - **Detailed documentation** and examples 14 | 15 | ## 📋 Prerequisites 16 | 17 | Before using this template, ensure you have: 18 | 19 | 1. **Azure Automation Account** with Managed Identity enabled 20 | 2. **Required PowerShell modules** installed in your Automation Account: 21 | - `Microsoft.Graph.Authentication` 22 | - `Microsoft.Graph.Mail` 23 | - Additional modules based on what you're monitoring 24 | 3. **Microsoft Graph permissions** assigned to your Managed Identity 25 | 4. **Basic PowerShell knowledge** for customization 26 | 27 | ## 🚀 Quick Start Guide 28 | 29 | ### Step 1: Copy and Rename the Template 30 | 31 | 1. Copy `notification-script-template.ps1` to a new file 32 | 2. Rename it to describe your monitoring purpose (e.g., `certificate-expiration-alert.ps1`) 33 | 3. Update the script header with your specific information 34 | 35 | ### Step 2: Customize the Header 36 | 37 | Update these sections in the comment header: 38 | 39 | ```powershell 40 | .TITLE 41 | Your Custom Notification Script Title 42 | 43 | .SYNOPSIS 44 | Brief description of what your script monitors 45 | 46 | .DESCRIPTION 47 | Detailed description of your monitoring logic 48 | 49 | .TAGS 50 | Notification,YourCategory,RunbookOnly,Email,Monitoring,SpecificTags 51 | 52 | .PERMISSIONS 53 | Your.Required.Graph.Permissions,Mail.Send 54 | ``` 55 | 56 | ### Step 3: Define Your Parameters 57 | 58 | Customize the script parameters based on what you need to monitor: 59 | 60 | ```powershell 61 | param( 62 | # Your main threshold parameter 63 | [Parameter(Mandatory = $true)] 64 | [int]$ThresholdParameter, 65 | 66 | # Always keep email recipients 67 | [Parameter(Mandatory = $true)] 68 | [string]$EmailRecipients, 69 | 70 | # Add additional parameters as needed 71 | [Parameter(Mandatory = $false)] 72 | [string]$AdditionalFilter = "DefaultValue" 73 | ) 74 | ``` 75 | 76 | ### Step 4: Implement Your Monitoring Logic 77 | 78 | The main areas to customize are: 79 | 80 | #### A. Update Required Modules and Permissions 81 | 82 | ```powershell 83 | $RequiredModules = @( 84 | "Microsoft.Graph.Authentication", 85 | "Microsoft.Graph.Mail", 86 | "Microsoft.Graph.DeviceManagement" # Add modules you need 87 | ) 88 | 89 | # Update scopes for local testing 90 | $Scopes = @( 91 | "DeviceManagementManagedDevices.Read.All", # Your required permissions 92 | "Mail.Send" 93 | ) 94 | ``` 95 | 96 | #### B. Customize the `Get-MonitoringData` Function 97 | 98 | Replace the sample monitoring logic with your specific API calls: 99 | 100 | ```powershell 101 | function Get-MonitoringData { 102 | param([hashtable]$Config) 103 | 104 | # Example: Monitor device compliance 105 | $Devices = Get-MgGraphAllPages -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=complianceState ne 'compliant'" 106 | 107 | # Example: Monitor certificate expiration 108 | $Certificates = Get-MgGraphAllPages -Uri "https://graph.microsoft.com/v1.0/deviceManagement/deviceConfigurations" 109 | 110 | # Process and return your monitoring data 111 | return $ProcessedData 112 | } 113 | ``` 114 | 115 | #### C. Customize the `Get-AlertAnalysis` Function 116 | 117 | Define your specific alert criteria: 118 | 119 | ```powershell 120 | function Get-AlertAnalysis { 121 | param([array]$MonitoringData, [hashtable]$Config) 122 | 123 | foreach ($Item in $MonitoringData) { 124 | # Your specific alert logic 125 | if ($Item.SomeProperty -gt $Config.ThresholdValue) { 126 | # Create alert 127 | $AlertData += [PSCustomObject]@{ 128 | Title = "Alert: $($Item.Name)" 129 | Details = "Your specific alert details" 130 | Level = "Critical" # or "Warning" 131 | } 132 | } 133 | } 134 | 135 | return @{ AlertData = $AlertData; Summary = $Summary } 136 | } 137 | ``` 138 | 139 | #### D. Customize the Email Template 140 | 141 | Update the `New-EmailBody` function to include relevant information for your monitoring type: 142 | 143 | ```powershell 144 | function New-EmailBody { 145 | # Customize the HTML template 146 | # Add specific sections for your monitoring data 147 | # Include relevant charts, tables, or metrics 148 | # Provide specific recommendations 149 | } 150 | ``` 151 | 152 | ### Step 5: Test Your Script 153 | 154 | 1. **Test locally first** with your own credentials: 155 | ```powershell 156 | .\your-notification-script.ps1 -ThresholdParameter 10 -EmailRecipients "yourtestemail@domain.com" 157 | ``` 158 | 159 | 2. **Deploy to Azure Automation** and test with Managed Identity 160 | 161 | 3. **Create a schedule** for automated execution 162 | 163 | ## 🎨 Customization Examples 164 | 165 | ### Example 1: Certificate Expiration Monitoring 166 | 167 | ```powershell 168 | # Monitor certificates expiring within threshold days 169 | function Get-MonitoringData { 170 | $Certificates = Get-MgGraphAllPages -Uri "https://graph.microsoft.com/v1.0/deviceManagement/deviceConfigurations" 171 | $ExpiringCerts = @() 172 | 173 | foreach ($Cert in $Certificates) { 174 | if ($Cert.expiryDate) { 175 | $DaysUntilExpiry = (([DateTime]$Cert.expiryDate) - (Get-Date)).Days 176 | if ($DaysUntilExpiry -le $Config.ThresholdValue) { 177 | $ExpiringCerts += [PSCustomObject]@{ 178 | Name = $Cert.displayName 179 | ExpiryDate = $Cert.expiryDate 180 | DaysRemaining = $DaysUntilExpiry 181 | Status = if ($DaysUntilExpiry -le 0) { "Critical" } else { "Warning" } 182 | } 183 | } 184 | } 185 | } 186 | 187 | return $ExpiringCerts 188 | } 189 | ``` 190 | 191 | ### Example 2: Application Deployment Failure Monitoring 192 | 193 | ```powershell 194 | function Get-MonitoringData { 195 | $Apps = Get-MgGraphAllPages -Uri "https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps" 196 | $FailedDeployments = @() 197 | 198 | foreach ($App in $Apps) { 199 | $InstallSummary = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps/$($App.id)/installSummary" 200 | 201 | $FailureRate = if ($InstallSummary.installedDeviceCount -gt 0) { 202 | ($InstallSummary.failedDeviceCount / ($InstallSummary.installedDeviceCount + $InstallSummary.failedDeviceCount)) * 100 203 | } else { 0 } 204 | 205 | if ($FailureRate -gt $Config.ThresholdValue) { 206 | $FailedDeployments += [PSCustomObject]@{ 207 | Name = $App.displayName 208 | FailureRate = [math]::Round($FailureRate, 2) 209 | FailedDevices = $InstallSummary.failedDeviceCount 210 | TotalDevices = $InstallSummary.installedDeviceCount + $InstallSummary.failedDeviceCount 211 | Status = if ($FailureRate -gt 50) { "Critical" } else { "Warning" } 212 | } 213 | } 214 | } 215 | 216 | return $FailedDeployments 217 | } 218 | ``` 219 | 220 | ## 📊 Email Template Customization 221 | 222 | The template includes a professional HTML email template. Customize these sections: 223 | 224 | ### 1. Header and Branding 225 | ```html 226 |
227 |

🔔 Your Custom Alert Title

228 |
Your custom subtitle
229 |
230 | ``` 231 | 232 | ### 2. Summary Cards 233 | Add relevant metrics for your monitoring type: 234 | ```html 235 |
236 |

Your Metric

237 |
$($Summary.YourValue)
238 |
Your Description
239 |
240 | ``` 241 | 242 | ### 3. Alert Sections 243 | Customize how alerts are displayed: 244 | ```html 245 |
246 |
$($Item.YourTitle)
247 |
$($Item.YourDetails)
248 |
249 | ``` 250 | 251 | ### 4. Recommendations 252 | Provide specific guidance for your alert type: 253 | ```html 254 |
255 |

📋 Recommended Actions

256 | 260 |
261 | ``` 262 | 263 | ## 🔧 Configuration Options 264 | 265 | The template supports various configuration options: 266 | 267 | ### Email Configuration 268 | ```powershell 269 | $EmailConfig = @{ 270 | Subject = "Your Custom Subject Line" 271 | FromAddress = "noreply@yourdomain.com" 272 | Priority = "High" # Low, Normal, High 273 | } 274 | ``` 275 | 276 | ### Monitoring Configuration 277 | ```powershell 278 | $MonitoringConfig = @{ 279 | ThresholdValue = $ThresholdParameter 280 | PlatformFilter = $PlatformFilter 281 | # Add custom configuration options 282 | WarningThreshold = $ThresholdParameter + 10 283 | CriticalThreshold = $ThresholdParameter 284 | } 285 | ``` 286 | 287 | ## 🚀 Deployment Guide 288 | 289 | ### 1. Azure Automation Setup 290 | 291 | 1. **Create/Upload Script**: Upload your customized script to Azure Automation 292 | 2. **Install Modules**: Ensure required Graph modules are installed 293 | 3. **Configure Managed Identity**: Enable and configure with required permissions 294 | 4. **Create Schedule**: Set up automated execution schedule 295 | 5. **Test Execution**: Run manually first to verify functionality 296 | 297 | ### 2. Required Permissions 298 | 299 | Grant these permissions to your Managed Identity (customize based on your needs): 300 | 301 | - `Mail.Send` (always required for notifications) 302 | - `DeviceManagementManagedDevices.Read.All` (for device monitoring) 303 | - `DeviceManagementConfiguration.Read.All` (for configuration monitoring) 304 | - `DeviceManagementApps.Read.All` (for application monitoring) 305 | - Additional permissions based on your specific monitoring needs 306 | 307 | ### 3. Scheduling Recommendations 308 | 309 | - **Daily**: For critical monitoring (compliance, security) 310 | - **Weekly**: For trending analysis (stale devices, certificate expiration) 311 | - **Monthly**: For long-term planning (license optimization) 312 | 313 | ## 🔍 Troubleshooting 314 | 315 | ### Common Issues 316 | 317 | 1. **Module Not Found** 318 | - Ensure required modules are installed in Azure Automation 319 | - Check module versions and compatibility 320 | 321 | 2. **Permission Denied** 322 | - Verify Managed Identity has required Graph permissions 323 | - Check permission scope and application vs delegated permissions 324 | 325 | 3. **Email Not Sending** 326 | - Verify `Mail.Send` permission is granted 327 | - Check email addresses are valid 328 | - Review error logs in Azure Automation 329 | 330 | 4. **No Data Retrieved** 331 | - Verify Graph API endpoints are correct 332 | - Check filtering logic and parameters 333 | - Ensure data exists to monitor 334 | 335 | ### Debugging Tips 336 | 337 | 1. **Enable Verbose Logging**: 338 | ```powershell 339 | Write-Information "Debug info: $($Variable | ConvertTo-Json)" -InformationAction Continue 340 | ``` 341 | 342 | 2. **Test API Calls Separately**: 343 | ```powershell 344 | $TestData = Invoke-MgGraphRequest -Uri "your-endpoint" -Method GET 345 | ``` 346 | 347 | 3. **Validate Email Template**: 348 | Save email body to file and open in browser for visual verification 349 | 350 | ## 📚 Additional Resources 351 | 352 | - [Microsoft Graph PowerShell SDK Documentation](https://docs.microsoft.com/en-us/powershell/microsoftgraph/) 353 | - [Azure Automation Runbooks](https://docs.microsoft.com/en-us/azure/automation/automation-runbook-execution) 354 | - [Microsoft Graph API Reference](https://docs.microsoft.com/en-us/graph/api/overview) 355 | - [Intune Graph API Examples](https://docs.microsoft.com/en-us/graph/api/resources/intune-graph-overview) 356 | 357 | ## 🤝 Contributing 358 | 359 | When you create a notification script using this template: 360 | 361 | 1. **Test thoroughly** in your environment 362 | 2. **Document your customizations** clearly 363 | 3. **Consider contributing back** your script as an example 364 | 4. **Share feedback** on template improvements 365 | 366 | ## 📝 Template Checklist 367 | 368 | Before deploying your notification script: 369 | 370 | - [ ] Updated script header with your information 371 | - [ ] Customized monitoring logic for your use case 372 | - [ ] Updated required modules and permissions 373 | - [ ] Customized email template and styling 374 | - [ ] Added specific alert criteria and thresholds 375 | - [ ] Tested locally with your credentials 376 | - [ ] Deployed and tested in Azure Automation 377 | - [ ] Configured appropriate schedule 378 | - [ ] Documented your customizations 379 | - [ ] Set up monitoring for the script itself 380 | 381 | ## 🎯 Success Criteria 382 | 383 | Your notification script should: 384 | 385 | - ✅ **Monitor specific conditions** relevant to your environment 386 | - ✅ **Send actionable alerts** with clear next steps 387 | - ✅ **Run reliably** on schedule without manual intervention 388 | - ✅ **Handle errors gracefully** with appropriate logging 389 | - ✅ **Provide value** by preventing issues or reducing response time 390 | - ✅ **Follow security best practices** with minimal required permissions 391 | 392 | Happy monitoring! 🎉 -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Script Templates 2 | 3 | This directory contains templates to help contributors create consistent, high-quality scripts for the IntuneAutomation project. 4 | 5 | ## 📄 Available Templates 6 | 7 | ### `script-template.ps1` 8 | A comprehensive PowerShell script template that includes all the required elements for IntuneAutomation scripts. 9 | 10 | ### `notification-script-template.ps1` 🆕 11 | A specialized template for creating notification scripts that monitor Intune environments and send email alerts. These scripts are designed specifically for Azure Automation runbooks with email notification capabilities. 12 | 13 | **Key Features:** 14 | - Azure Automation optimized with Managed Identity support 15 | - Professional HTML email templates with responsive design 16 | - Comprehensive error handling and Microsoft Graph integration 17 | - Rate limiting and pagination for API calls 18 | - Configurable thresholds and filtering options 19 | 20 | **Use Cases:** 21 | - Monitor certificate expiration dates 22 | - Track device compliance drift 23 | - Alert on application deployment failures 24 | - Monitor token and license expiration 25 | - Custom threshold-based monitoring 26 | 27 | See `NOTIFICATION_TEMPLATE_GUIDE.md` for detailed usage instructions. 28 | 29 | ## 🚀 How to Use the Templates 30 | 31 | ### For Standard Scripts (script-template.ps1) 32 | 33 | 1. **Copy the template** to the appropriate category directory: 34 | ```bash 35 | cp templates/script-template.ps1 scripts/[category]/your-script-name.ps1 36 | ``` 37 | 38 | 2. **Rename the file** to describe your script's function (use kebab-case): 39 | - `get-device-inventory.ps1` 40 | - `sync-all-devices.ps1` 41 | - `export-compliance-report.ps1` 42 | 43 | 3. **Update the header sections** with your script's information 44 | 45 | 4. **Replace placeholder code** with your implementation 46 | 47 | 5. **Test thoroughly** before submitting 48 | 49 | ### For Notification Scripts (notification-script-template.ps1) 🆕 50 | 51 | 1. **Copy the notification template** to the notification directory: 52 | ```bash 53 | cp templates/notification-script-template.ps1 scripts/notification/your-notification-alert.ps1 54 | ``` 55 | 56 | 2. **Rename the file** to describe your monitoring purpose: 57 | - `certificate-expiration-alert.ps1` 58 | - `license-usage-alert.ps1` 59 | - `policy-drift-alert.ps1` 60 | 61 | 3. **Follow the detailed guide** in `NOTIFICATION_TEMPLATE_GUIDE.md` 62 | 63 | 4. **Customize monitoring logic** for your specific use case 64 | 65 | 5. **Test in Azure Automation** with Managed Identity 66 | 67 | 6. **Set up automated scheduling** for continuous monitoring 68 | 69 | ## 📋 Template Sections Explained 70 | 71 | ### Header Documentation 72 | 73 | #### `.TITLE` 74 | - Brief, descriptive name for your script 75 | - Should clearly indicate what the script does 76 | - Example: "Bulk Device Synchronization" 77 | 78 | #### `.SYNOPSIS` 79 | - One-line summary of functionality 80 | - Keep it concise and clear 81 | - Example: "Triggers synchronization on multiple Intune managed devices" 82 | 83 | #### `.DESCRIPTION` 84 | - Detailed explanation of what the script does 85 | - Include use cases and prerequisites 86 | - Mention any important considerations 87 | - Explain the business value 88 | 89 | #### `.TAGS` 90 | - Use the format: `[Primary Category],[Secondary Tag]` 91 | - Primary categories: `Operational`, `Apps`, `Compliance`, `Security`, `Devices` 92 | - Secondary tags: `Reporting`, `Bulk`, `Remediation`, `Automation`, `Monitoring` 93 | 94 | #### `.MINROLE` 95 | - Specify the minimum Azure AD/Intune role required 96 | - Examples: `Intune Administrator`, `Global Administrator`, `Security Administrator` 97 | 98 | #### `.PERMISSIONS` 99 | - List all required Microsoft Graph API permissions 100 | - Use exact permission names from Microsoft documentation 101 | - Separate multiple permissions with commas 102 | 103 | #### `.AUTHOR` 104 | - Your name or GitHub username 105 | - Will be used for attribution 106 | 107 | #### `.VERSION` 108 | - Start with `1.0` for new scripts 109 | - Follow semantic versioning (major.minor.patch) 110 | 111 | #### `.CHANGELOG` 112 | - Document all changes by version 113 | - Format: `[Version] - [Description]` 114 | 115 | #### `.EXAMPLE` 116 | - Provide at least 2 realistic usage examples 117 | - Include parameter values and descriptions 118 | - Show different use cases 119 | 120 | #### `.NOTES` 121 | - List requirements, limitations, or important information 122 | - Include links to relevant documentation 123 | - Mention performance considerations 124 | 125 | ### Code Structure 126 | 127 | #### Parameters 128 | ```powershell 129 | [CmdletBinding()] 130 | param( 131 | [Parameter(Mandatory = $true, HelpMessage = "Description")] 132 | [ValidateNotNullOrEmpty()] 133 | [string]$RequiredParameter 134 | ) 135 | ``` 136 | 137 | #### Module Checks 138 | - Always verify required modules are installed 139 | - We only use Microsoft.Graph.Authentication module for Connect-MgGraph and Invoke-MgGraphRequest 140 | - Provide clear installation instructions 141 | - Import modules explicitly 142 | 143 | #### Authentication 144 | - Use only Microsoft.Graph.Authentication module for authentication and API calls 145 | - Connect with Connect-MgGraph and make API calls with Invoke-MgGraphRequest 146 | ```powershell 147 | Connect-MgGraph -Scopes $Scopes -NoWelcome 148 | ``` 149 | 150 | #### Helper Functions 151 | - Include the `Get-MgGraphAllPages` function for pagination 152 | - Add custom helper functions as needed 153 | - Use proper error handling 154 | 155 | #### Main Logic 156 | - Implement your core functionality 157 | - Use try-catch blocks for error handling 158 | - Provide progress feedback 159 | - Validate inputs 160 | 161 | #### Cleanup 162 | - Always disconnect from Graph API 163 | - Handle disconnection errors gracefully 164 | 165 | ## 🔧 Customization Guidelines 166 | 167 | ### Required Sections (Don't Remove) 168 | - Header documentation blocks 169 | - Parameter validation 170 | - Module checks and imports 171 | - Graph authentication 172 | - `Get-MgGraphAllPages` helper function 173 | - Error handling blocks 174 | - Cleanup/disconnection 175 | 176 | ### Customizable Sections 177 | - Parameter definitions 178 | - Required modules list 179 | - Graph permissions scope 180 | - Helper functions 181 | - Main script logic 182 | - Output formatting 183 | 184 | ### Best Practices 185 | 1. **Module Usage**: Use only Microsoft.Graph.Authentication module with Connect-MgGraph and Invoke-MgGraphRequest 186 | 2. **Validation**: Validate all user inputs 187 | 3. **Error Handling**: Use comprehensive try-catch blocks 188 | 4. **Progress**: Show real-time progress to users 189 | 5. **Permissions**: Request only minimum required permissions 190 | 6. **Documentation**: Comment complex logic 191 | 7. **Testing**: Test all scenarios thoroughly 192 | 193 | ## 📝 Header Examples 194 | 195 | ### Operational Script Example 196 | ```powershell 197 | <# 198 | .TITLE 199 | Bulk Device Restart 200 | 201 | .SYNOPSIS 202 | Remotely restarts multiple Intune managed devices 203 | 204 | .DESCRIPTION 205 | This script connects to Microsoft Graph and sends restart commands to specified devices. 206 | Devices can be targeted by name, ID, or group membership. The script provides real-time 207 | feedback on restart operations and handles errors gracefully. 208 | 209 | .TAGS 210 | Operational,Bulk 211 | 212 | .MINROLE 213 | Intune Administrator 214 | 215 | .PERMISSIONS 216 | DeviceManagementManagedDevices.ReadWrite.All,DeviceManagementManagedDevices.Read.All 217 | 218 | .AUTHOR 219 | Your Name 220 | 221 | .VERSION 222 | 1.0 223 | 224 | .CHANGELOG 225 | 1.0 - Initial release 226 | 227 | .EXAMPLE 228 | .\restart-devices.ps1 -DeviceNames "LAPTOP001","DESKTOP002" 229 | Restarts specific devices by name 230 | 231 | .NOTES 232 | - Requires only Microsoft.Graph.Authentication module 233 | - Uses Connect-MgGraph and Invoke-MgGraphRequest for all Graph operations 234 | - Devices must be online to receive restart command 235 | - Use with caution in production environments 236 | #> 237 | ``` 238 | 239 | ### Reporting Script Example 240 | ```powershell 241 | <# 242 | .TITLE 243 | Device Compliance Dashboard Report 244 | 245 | .SYNOPSIS 246 | Generates comprehensive device compliance reporting dashboard 247 | 248 | .DESCRIPTION 249 | Creates detailed compliance reports showing device compliance status, policy violations, 250 | and remediation recommendations. Exports data to CSV and generates HTML dashboard 251 | for executive reporting. 252 | 253 | .TAGS 254 | Compliance,Reporting 255 | 256 | .MINROLE 257 | Intune Administrator 258 | 259 | .PERMISSIONS 260 | DeviceManagementManagedDevices.Read.All,DeviceManagementConfiguration.Read.All 261 | 262 | .AUTHOR 263 | Your Name 264 | 265 | .VERSION 266 | 1.0 267 | 268 | .CHANGELOG 269 | 1.0 - Initial release 270 | 271 | .EXAMPLE 272 | .\compliance-dashboard.ps1 -OutputPath "C:\Reports" 273 | Generates reports and saves to specified directory 274 | 275 | .NOTES 276 | - Generates CSV and HTML output files 277 | - Includes charts and visualizations 278 | - Can be scheduled for automated reporting 279 | #> 280 | ``` 281 | 282 | ## 🤝 Getting Help 283 | 284 | - Review existing scripts for examples 285 | - Check the [CONTRIBUTING.md](../CONTRIBUTING.md) guide 286 | - Create an issue if you need assistance 287 | - Ask questions in GitHub Discussions 288 | 289 | ## 📚 Additional Resources 290 | 291 | - [PowerShell Best Practices](https://docs.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations) 292 | - [Microsoft Graph PowerShell SDK](https://docs.microsoft.com/en-us/powershell/microsoftgraph/) 293 | - [Intune PowerShell Samples](https://github.com/microsoftgraph/powershell-intune-samples) -------------------------------------------------------------------------------- /templates/macos-script-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TITLE: [Script Title - Brief descriptive name] 4 | # SYNOPSIS: [One-line description of what the script does] 5 | # DESCRIPTION: [Detailed description of the script's functionality, purpose, and use cases] 6 | # TAGS: [Category],[Subcategory] (e.g., Monitoring,Device or Security,Compliance) 7 | # PLATFORM: macOS 8 | # MIN_OS_VERSION: [Minimum macOS version required - e.g., 10.15] 9 | # AUTHOR: [Your Name] 10 | # VERSION: [Version number - start with 1.0] 11 | # LASTUPDATE: [Date of last update] 12 | # CHANGELOG: 13 | # [Version] - [Description of changes] 14 | # 1.0 - Initial release 15 | # 16 | # EXAMPLE: 17 | # [Script filename] 18 | # [Description of what this example does] 19 | # 20 | # NOTES: 21 | # [Additional notes, requirements, or important information] 22 | # - [Any special requirements or dependencies] 23 | # - [Performance considerations] 24 | # - [Known limitations] 25 | # - For more scripts and guides, visit: IntuneMacAdmins.com 26 | 27 | # ============================================================================ 28 | # VARIABLES AND INITIALIZATION 29 | # ============================================================================ 30 | 31 | # Script version 32 | SCRIPT_VERSION="1.0" 33 | 34 | # Get the current console user (if needed) 35 | # Note: Returns "root" or "_windowserver" when no user is logged in 36 | loggedInUser=$(stat -f "%Su" /dev/console 2>/dev/null) 37 | 38 | # Common paths (initialize only if user is valid) 39 | if [ -n "$loggedInUser" ] && [ "$loggedInUser" != "root" ] && [ "$loggedInUser" != "_windowserver" ]; then 40 | USER_HOME=$(dscl . -read /Users/"$loggedInUser" NFSHomeDirectory 2>/dev/null | awk '{print $2}') 41 | LIBRARY_PATH="$USER_HOME/Library" 42 | fi 43 | 44 | # Optional: Logging setup (comment out for Intune custom attributes) 45 | # LOG_DIR="/var/log" 46 | # LOG_FILE="$LOG_DIR/intune-script-$(date +%Y%m%d).log" 47 | 48 | # ============================================================================ 49 | # FUNCTIONS 50 | # ============================================================================ 51 | 52 | # Function to log messages (optional - remove for Intune custom attributes) 53 | # log_message() { 54 | # local level=$1 55 | # local message=$2 56 | # local timestamp=$(date "+%Y-%m-%d %H:%M:%S") 57 | # echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" 58 | # } 59 | 60 | # Function to output result (for Intune custom attributes) 61 | output_result() { 62 | # For Intune custom attributes, output should be a single line 63 | echo "$1" 64 | exit 0 65 | } 66 | 67 | # Function to check minimum OS version 68 | check_os_version() { 69 | local required_version=$1 70 | local current_version=$(sw_vers -productVersion) 71 | 72 | if [[ $(echo -e "$current_version\n$required_version" | sort -V | head -n1) != "$required_version" ]]; then 73 | # For logging scripts: 74 | # log_message "ERROR" "macOS $required_version or higher is required. Current version: $current_version" 75 | # For Intune custom attributes: 76 | output_result "Unsupported OS: $current_version (requires $required_version+)" 77 | fi 78 | } 79 | 80 | # Function to check if running as root (if needed) 81 | check_root() { 82 | if [[ $EUID -ne 0 ]]; then 83 | # For logging scripts: 84 | # log_message "ERROR" "This script must be run as root" 85 | # For Intune custom attributes: 86 | output_result "Error: Root access required" 87 | fi 88 | } 89 | 90 | # Function to validate prerequisites 91 | validate_prerequisites() { 92 | # Add your prerequisite checks here 93 | # For logging scripts: 94 | # log_message "INFO" "Validating prerequisites..." 95 | 96 | # Example: Check if a specific directory exists 97 | # if [[ ! -d "/path/to/required/directory" ]]; then 98 | # # For logging scripts: 99 | # # log_message "ERROR" "Required directory not found" 100 | # # For Intune custom attributes: 101 | # # output_result "Error: Required directory not found" 102 | # fi 103 | 104 | return 0 105 | } 106 | 107 | # ============================================================================ 108 | # MAIN SCRIPT LOGIC 109 | # ============================================================================ 110 | 111 | main() { 112 | # For logging scripts: 113 | # log_message "INFO" "Starting script execution (v$SCRIPT_VERSION)" 114 | 115 | # Check OS version if required 116 | # check_os_version "10.15" 117 | 118 | # Check if root access is needed 119 | # check_root 120 | 121 | # Validate prerequisites 122 | validate_prerequisites 123 | 124 | # Add your main script logic here 125 | # For logging scripts: 126 | # log_message "INFO" "Executing main logic..." 127 | 128 | # Example: Process some data 129 | # if [[ -f "/path/to/file" ]]; then 130 | # process_file "/path/to/file" 131 | # else 132 | # # For logging scripts: 133 | # # log_message "WARN" "File not found, skipping processing" 134 | # # For Intune custom attributes: 135 | # # output_result "File not found" 136 | # fi 137 | 138 | # Example: Collect information 139 | # device_info=$(system_profiler SPHardwareDataType) 140 | 141 | # Example: Check multiple user directories 142 | # for userHome in /Users/*; do 143 | # if [[ "$userHome" == "/Users/Shared" ]] || [[ "$userHome" == "/Users/Guest" ]]; then 144 | # continue 145 | # fi 146 | # # Process user directory 147 | # done 148 | 149 | # For Intune custom attributes - ensure single line output 150 | # output_result "Status: OK" 151 | 152 | # For logging scripts: 153 | # log_message "INFO" "Script completed successfully" 154 | } 155 | 156 | # ============================================================================ 157 | # ERROR HANDLING 158 | # ============================================================================ 159 | 160 | # For logging scripts - trap errors and log them 161 | # trap 'log_message "ERROR" "Script failed at line $LINENO with exit code $?"' ERR 162 | 163 | # For Intune custom attributes - handle errors gracefully 164 | # trap 'output_result "Error: Script failed"' ERR 165 | 166 | # ============================================================================ 167 | # SCRIPT EXECUTION 168 | # ============================================================================ 169 | 170 | # Only run main if script is executed directly (not sourced) 171 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 172 | main "$@" 173 | fi 174 | 175 | # ============================================================================ 176 | # CLEANUP 177 | # ============================================================================ 178 | 179 | # Perform any cleanup operations if needed 180 | # cleanup() { 181 | # log_message "INFO" "Performing cleanup..." 182 | # # Add cleanup logic here 183 | # } 184 | 185 | # trap cleanup EXIT 186 | 187 | # Exit successfully (if not using output_result) 188 | exit 0 -------------------------------------------------------------------------------- /templates/script-template.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .TITLE 3 | [Script Title - Brief descriptive name] 4 | 5 | .SYNOPSIS 6 | [One-line description of what the script does] 7 | 8 | .DESCRIPTION 9 | [Detailed description of the script's functionality, purpose, and use cases. 10 | Explain what the script accomplishes and any important considerations. 11 | Include information about prerequisites, dependencies, or special requirements.] 12 | 13 | .TAGS 14 | [Category],[Subcategory] (e.g., Operational,Devices or Security,Compliance) 15 | 16 | .PLATFORM 17 | Windows 18 | 19 | .MINROLE 20 | [Minimum Intune/Azure AD role required - e.g., Intune Administrator, Global Administrator] 21 | 22 | .PERMISSIONS 23 | [Required Microsoft Graph permissions - comma separated list] 24 | 25 | .AUTHOR 26 | [Your Name] 27 | 28 | .VERSION 29 | [Version number - start with 1.0] 30 | 31 | .CHANGELOG 32 | [Version] - [Description of changes] 33 | 1.0 - Initial release 34 | 35 | .EXAMPLE 36 | [Script filename] [parameters] 37 | [Description of what this example does] 38 | 39 | .EXAMPLE 40 | [Script filename] [different parameters] 41 | [Description of what this example does] 42 | 43 | .NOTES 44 | [Additional notes, requirements, or important information] 45 | - [Any special requirements or dependencies] 46 | - [Performance considerations] 47 | - [Known limitations] 48 | - [Links to relevant documentation] 49 | #> 50 | 51 | [CmdletBinding()] 52 | param( 53 | # Define your parameters here 54 | # Use proper parameter attributes and validation 55 | [Parameter(Mandatory = $true, HelpMessage = "Description of this parameter")] 56 | [ValidateNotNullOrEmpty()] 57 | [string]$RequiredParameter, 58 | 59 | [Parameter(Mandatory = $false, HelpMessage = "Description of this optional parameter")] 60 | [string]$OptionalParameter = "DefaultValue", 61 | 62 | [Parameter(Mandatory = $false)] 63 | [switch]$SwitchParameter 64 | ) 65 | 66 | # ============================================================================ 67 | # MODULES AND AUTHENTICATION 68 | # ============================================================================ 69 | 70 | # Check if required modules are installed 71 | $RequiredModules = @( 72 | "Microsoft.Graph.Authentication" 73 | # Add other required modules here 74 | ) 75 | 76 | foreach ($Module in $RequiredModules) { 77 | if (-not (Get-Module -ListAvailable -Name $Module)) { 78 | Write-Error "$Module module is required. Install it using: Install-Module $Module -Scope CurrentUser" 79 | exit 1 80 | } 81 | } 82 | 83 | # Import required modules 84 | foreach ($Module in $RequiredModules) { 85 | Import-Module $Module 86 | } 87 | 88 | # Connect to Microsoft Graph 89 | try { 90 | Write-Information "Connecting to Microsoft Graph..." -InformationAction Continue 91 | $Scopes = @( 92 | # Add your required permissions here 93 | "Permission.ReadWrite.All" 94 | ) 95 | Connect-MgGraph -Scopes $Scopes -NoWelcome 96 | Write-Information "✓ Successfully connected to Microsoft Graph" -InformationAction Continue 97 | } 98 | catch { 99 | Write-Error "Failed to connect to Microsoft Graph: $($_.Exception.Message)" 100 | exit 1 101 | } 102 | 103 | # ============================================================================ 104 | # HELPER FUNCTIONS 105 | # ============================================================================ 106 | 107 | # Function to get all pages of results from Graph API 108 | function Get-MgGraphAllPages { 109 | param( 110 | [Parameter(Mandatory = $true)] 111 | [string]$Uri, 112 | [int]$DelayMs = 100 113 | ) 114 | 115 | $AllResults = @() 116 | $NextLink = $Uri 117 | $RequestCount = 0 118 | 119 | do { 120 | try { 121 | # Add delay to respect rate limits 122 | if ($RequestCount -gt 0) { 123 | Start-Sleep -Milliseconds $DelayMs 124 | } 125 | 126 | $Response = Invoke-MgGraphRequest -Uri $NextLink -Method GET 127 | $RequestCount++ 128 | 129 | if ($Response.value) { 130 | $AllResults += $Response.value 131 | } 132 | else { 133 | $AllResults += $Response 134 | } 135 | 136 | $NextLink = $Response.'@odata.nextLink' 137 | } 138 | catch { 139 | if ($_.Exception.Message -like "*429*" -or $_.Exception.Message -like "*throttled*") { 140 | Write-Information "`nRate limit hit, waiting 60 seconds..." -InformationAction Continue 141 | Start-Sleep -Seconds 60 142 | continue 143 | } 144 | Write-Warning "Error fetching data from $NextLink : $($_.Exception.Message)" 145 | break 146 | } 147 | } while ($NextLink) 148 | 149 | return $AllResults 150 | } 151 | 152 | # Add your custom helper functions here 153 | function Invoke-CustomFunction { 154 | param( 155 | [Parameter(Mandatory = $true)] 156 | [string]$Parameter 157 | ) 158 | 159 | try { 160 | # Your function logic here 161 | Write-Information "Processing: $Parameter" -InformationAction Continue 162 | 163 | # Return result 164 | return $true 165 | } 166 | catch { 167 | Write-Error "Error in custom function: $($_.Exception.Message)" 168 | return $false 169 | } 170 | } 171 | 172 | # ============================================================================ 173 | # MAIN SCRIPT LOGIC 174 | # ============================================================================ 175 | 176 | try { 177 | Write-Information "Starting script execution..." -InformationAction Continue 178 | 179 | # Validate parameters 180 | if ([string]::IsNullOrWhiteSpace($RequiredParameter)) { 181 | throw "Required parameter cannot be empty" 182 | } 183 | 184 | # Main script logic goes here 185 | Write-Information "Processing with parameter: $RequiredParameter" -InformationAction Continue 186 | 187 | # Example API call 188 | # $Results = Get-MgGraphAllPages -Uri "https://graph.microsoft.com/v1.0/your-endpoint" 189 | 190 | # Process results 191 | # foreach ($Item in $Results) { 192 | # $Success = Invoke-CustomFunction -Parameter $Item.property 193 | # if ($Success) { 194 | # Write-Information "✓ Successfully processed: $($Item.displayName)" -InformationAction Continue 195 | # } 196 | # else { 197 | # Write-Warning "✗ Failed to process: $($Item.displayName)" 198 | # } 199 | # } 200 | 201 | Write-Information "✓ Script completed successfully" -InformationAction Continue 202 | } 203 | catch { 204 | Write-Error "Script failed: $($_.Exception.Message)" 205 | exit 1 206 | } 207 | finally { 208 | # Cleanup operations 209 | try { 210 | Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null 211 | Write-Information "Disconnected from Microsoft Graph" -InformationAction Continue 212 | } 213 | catch { 214 | # Ignore disconnect errors 215 | } 216 | } 217 | 218 | # ============================================================================ 219 | # SCRIPT SUMMARY 220 | # ============================================================================ 221 | 222 | Write-Information " 223 | ======================================== 224 | Script Execution Summary 225 | ======================================== 226 | Script: [Script Name] 227 | Parameters: $RequiredParameter 228 | Status: Completed 229 | ======================================== 230 | " -InformationAction Continue -------------------------------------------------------------------------------- /testresults.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "filename": "check-apple-token-validity.ps1", 4 | "result": "pass", 5 | "timestamp": "06-05-2025", 6 | "type": "PowerShell" 7 | }, 8 | { 9 | "filename": "check-bitlocker-keys.ps1", 10 | "result": "pass", 11 | "timestamp": "06-05-2025", 12 | "type": "PowerShell" 13 | }, 14 | { 15 | "filename": "check-policy-changes.ps1", 16 | "result": "pass", 17 | "timestamp": "06-05-2025", 18 | "type": "PowerShell" 19 | }, 20 | { 21 | "filename": "check-unassigned-policies.ps1", 22 | "result": "pass", 23 | "timestamp": "06-05-2025", 24 | "type": "PowerShell" 25 | }, 26 | { 27 | "filename": "app-deployment-failure-alert.ps1", 28 | "result": "pass", 29 | "timestamp": "06-05-2025", 30 | "type": "PowerShell" 31 | }, 32 | { 33 | "filename": "apple-token-expiration-alert.ps1", 34 | "result": "pass", 35 | "timestamp": "06-05-2025", 36 | "type": "PowerShell" 37 | }, 38 | { 39 | "filename": "device-compliance-drift-alert.ps1", 40 | "result": "pass", 41 | "timestamp": "06-05-2025", 42 | "type": "PowerShell" 43 | }, 44 | { 45 | "filename": "stale-device-cleanup-alert.ps1", 46 | "result": "pass", 47 | "timestamp": "06-05-2025", 48 | "type": "PowerShell" 49 | }, 50 | { 51 | "filename": "cleanup-autopilot-devices.ps1", 52 | "result": "pass", 53 | "timestamp": "06-05-2025", 54 | "type": "PowerShell" 55 | }, 56 | { 57 | "filename": "get-devices-by-scopetag.ps1", 58 | "result": "pass", 59 | "timestamp": "06-05-2025", 60 | "type": "PowerShell" 61 | }, 62 | { 63 | "filename": "get-stale-devices.ps1", 64 | "result": "pass", 65 | "timestamp": "06-05-2025", 66 | "type": "PowerShell" 67 | }, 68 | { 69 | "filename": "get-device-compliance-report.ps1", 70 | "result": "pass", 71 | "timestamp": "06-05-2025", 72 | "type": "PowerShell" 73 | }, 74 | { 75 | "filename": "get-application-inventory-report.ps1", 76 | "result": "pass", 77 | "timestamp": "06-05-2025", 78 | "type": "PowerShell" 79 | }, 80 | { 81 | "filename": "get-duplicate-applications.ps1", 82 | "result": "pass", 83 | "timestamp": "06-05-2025", 84 | "type": "PowerShell" 85 | }, 86 | { 87 | "filename": "sync-devices.ps1", 88 | "result": "pass", 89 | "timestamp": "06-05-2025", 90 | "type": "PowerShell" 91 | }, 92 | { 93 | "filename": "wipe-devices.ps1", 94 | "result": "pass", 95 | "timestamp": "06-05-2025", 96 | "type": "PowerShell" 97 | }, 98 | { 99 | "filename": "rotate-bitlocker-keys.ps1", 100 | "result": "pass", 101 | "timestamp": "06-05-2025", 102 | "type": "PowerShell" 103 | }, 104 | { 105 | "filename": "check-applecare-warranty-status.sh", 106 | "result": "pass", 107 | "timestamp": "06-05-2025", 108 | "type": "Shell" 109 | }, 110 | { 111 | "filename": "check-available-msupdate-updates.sh", 112 | "result": "pass", 113 | "timestamp": "06-05-2025", 114 | "type": "Shell" 115 | }, 116 | { 117 | "filename": "check-msupdate-status.sh", 118 | "result": "pass", 119 | "timestamp": "06-05-2025", 120 | "type": "Shell" 121 | }, 122 | { 123 | "filename": "check-network-requirements.sh", 124 | "result": "pass", 125 | "timestamp": "06-05-2025", 126 | "type": "Shell" 127 | }, 128 | { 129 | "filename": "check-xprotect-status.sh", 130 | "result": "pass", 131 | "timestamp": "06-05-2025", 132 | "type": "Shell" 133 | }, 134 | { 135 | "filename": "last-reboot.sh", 136 | "result": "pass", 137 | "timestamp": "06-05-2025", 138 | "type": "Shell" 139 | }, 140 | { 141 | "filename": "local-admins.sh", 142 | "result": "pass", 143 | "timestamp": "06-05-2025", 144 | "type": "Shell" 145 | } 146 | ] 147 | --------------------------------------------------------------------------------