-
Notifications
You must be signed in to change notification settings - Fork 1
Add Azure Automation management and secure config functions #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
WimvandenHeijkant
wants to merge
162
commits into
main
Choose a base branch
from
claude/test-new-fgconfig-ZKVhF
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…ioning This commit adds comprehensive Azure SQL Server integration to the FortigiGraph module, enabling users to store Microsoft Graph data in SQL with automatic version history tracking. New Base Functions: - New-FGAzureSQLServer: Provisions Azure SQL Server and Database with firewall configuration - Connect-FGSQLServer: Establishes SQL Server connection with credential management - Connect-FGSQLServerFromAzure: Smart wrapper with automatic firewall updates and Azure integration - Initialize-FGSQLTable: Creates temporal tables with automatic history tracking - Test-FGSQLConnection: Verifies SQL connection and displays server information - Invoke-FGSQLQuery: Execute SQL queries from PowerShell command line New Generic Functions: - Sync-FGUser: Syncs Microsoft Graph users to SQL with automatic schema detection Key Features: - Temporal tables with ValidFrom/ValidTo columns for automatic version history - Azure context confirmation before operations - Automatic server name normalization and validation - Firewall rule management with current IP detection - Credential caching for session persistence - Connection validation before claiming "already connected" - Automatic schema evolution when adding new attributes - Type-aware NULL handling for change detection - Transaction-based sync for optimal performance (40-50 users/sec) - Progress tracking with timestamps and rate display - Deletion handling for removed users - Support for 16 default user attributes plus custom/additional attributes - Manager and signInActivity expansion handling Documentation: - Comprehensive README.md with function documentation - Quick start guide - SQL query examples for temporal data - PowerShell query examples using Invoke-FGSQLQuery - Best practices and troubleshooting guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This refactoring improves the architecture of the SQL integration by: 1. Created Invoke-FGSQLCommand helper function: - Centralizes SQL connection lifecycle management (open/close/dispose) - Eliminates duplicate connection handling code across all SQL functions - Provides consistent error handling and cleanup - Uses ScriptBlock pattern for maximum flexibility 2. Refactored existing functions to use the helper: - Connect-FGSQLServer: Now uses helper for connection testing - Connect-FGSQLServerFromAzure: Calls Test-FGSQLConnection instead of duplicating test logic - Test-FGSQLConnection: Uses helper for connection and query execution - Initialize-FGSQLTable: Uses helper for all SQL operations - Sync-FGUser: Uses helper for both table/schema checks and sync operations Key improvements: - Functions now focus on their business logic, not connection management - Connect-FGSQLServerFromAzure properly delegates to Test-FGSQLConnection - No more duplicate connection open/close/dispose patterns - Better separation of concerns following the "wrapper" pattern - Maintains all existing functionality while reducing code duplication This follows the DRY (Don't Repeat Yourself) principle and makes the codebase more maintainable.
Added comprehensive documentation about the refactored architecture: 1. New "Architecture & Design" section: - Explains the Invoke-FGSQLCommand helper function - Documents the benefits of centralized connection management - Lists clear responsibilities for each SQL function - Shows how functions delegate to each other (wrapper pattern) - Emphasizes DRY principle and separation of concerns 2. Added Invoke-FGSQLQuery documentation: - Function description and parameters - Multiple usage examples (queries, exports, grid view) - Shows how to query current data and temporal history This documentation helps developers understand: - Why the code is organized this way - How the functions work together - The benefits of the architectural approach - How to use the SQL query function effectively
…naming Major changes: 1. Created dedicated SQL/ folder for all SQL-related functions 2. Renamed Connect-FGSQLServerFromAzure -> Connect-FGSQLServer (main function) 3. Renamed Connect-FGSQLServer -> New-FGSQLConnection (low-level helper) 4. Removed Invoke-FGSQLQuery (redundant, use Invoke-FGSQLCommand instead) 5. Fixed AutoConnect bug in New-FGAzureSQLServer to use correct parameters Structure improvements: - SQL functions now separated from Graph API functions - Clearer naming: users call Connect-FGSQLServer for best experience - Better separation of concerns: one command execution function - Updated all documentation and references in README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This documentation provides a complete guide for AI assistants working with the FortigiGraph codebase, including: Architecture & Features: - Microsoft Graph API integration with automatic pagination and token refresh - NEW: Azure SQL integration with temporal versioning for automatic change tracking - NEW: Sync-FGUser function for syncing Graph users to SQL with schema detection - Comprehensive testing infrastructure with secure credential storage Code Organization: - Base/ - Authentication and HTTP operations (17 functions) - Generic/ - Graph API operations including Sync-FGUser (47 functions) - SQL/ - Azure SQL operations with helper pattern (10 functions) - Specific/ - High-level idempotent helpers (10 functions) - _Test/ - Integration tests and secure credentials (4 scripts) Key Design Patterns: - Invoke-FGSQLCommand helper for consistent SQL connection management - Temporal tables for automatic change tracking with zero code overhead - Automatic schema evolution (add columns without recreating tables) - Transaction-based syncing with progress tracking Development Guidelines: - Function templates and examples for Graph API and SQL operations - Best practices for temporal table handling - Testing infrastructure documentation - DO/DON'T lists for common pitfalls Total: 88 functions, 4,753 lines of code
This commit significantly improves code organization and reduces duplication by introducing reusable helper functions and a new group sync capability. New Features: - Sync-FGGroup: Syncs Microsoft Graph groups to SQL with temporal versioning * 20+ default group attributes (id, displayName, mail, type, security, etc.) * Support for on-premises sync attributes * Owner expansion (ownerId from first owner) * Does NOT sync members (that's for a separate function) * Same pattern as Sync-FGUser for consistency SQL Helper Functions (improves code reuse): 1. Test-FGSQLTableExists: Check if table exists in database 2. Get-FGSQLTableSchema: Get list of columns from existing table 3. Add-FGSQLTableColumn: Add columns to temporal table with proper versioning 4. New-FGSQLMergeStatement: Build optimized MERGE statements with change detection 5. ConvertTo-FGSQLParameter: Convert Graph values to SQL parameters with type handling Code Improvements: - Refactored Sync-FGUser to use new helper functions - Reduced code duplication by ~200 lines - Consistent pattern between Sync-FGUser and Sync-FGGroup - Better separation of concerns (schema mgmt, type conversion, SQL building) - More maintainable and testable code Group Attributes Synced by Default: - Identity: id, displayName, description, mail, mailNickname - Type: mailEnabled, securityEnabled, groupTypes, visibility - Metadata: createdDateTime, renewedDateTime, expirationDateTime - Advanced: isAssignableToRole, membershipRule, membershipRuleProcessingState - On-Premises: onPremisesSamAccountName, onPremisesSyncEnabled, etc. - Owner: ownerId (from expanded owners collection) Total New Files: 6 (5 SQL helpers + 1 sync function) Lines of Code: ~800 lines added, ~200 lines removed through refactoring
This commit adds the ability to sync group membership relationships to SQL
with temporal versioning and composite primary keys.
New Features:
- Sync-FGGroupMember: Syncs many-to-many group membership relationships
* Composite primary key (groupId, memberId) for unique constraint
* Temporal table for tracking membership changes over time
* Iterates through each group to fetch all members (required pattern)
* Handles all member types: users, groups, devices, service principals
* Optional transitive member support (nested groups)
* Deletion handling (removes memberships that no longer exist)
* Progress tracking with rate display
* Transaction-based for performance
Attributes Synced:
- groupId (UNIQUEIDENTIFIER) - Group identifier
- memberId (UNIQUEIDENTIFIER) - Member identifier
- memberType (NVARCHAR) - Type from @odata.type (e.g., #microsoft.graph.user)
Enhanced Helper Functions:
- New-FGSQLMergeStatement: Updated to support composite primary keys
* Accepts array of primary key columns
* Builds proper ON clause with multiple key conditions
* Excludes all PK columns from UPDATE SET statement
* Works with both single and composite keys
Use Cases:
- Track who is in which groups over time
- Audit membership changes with temporal queries
- Join with GraphUsers and GraphGroups tables
- Query membership at any point in time
Example Usage:
# Sync all group memberships
Sync-FGGroupMember
# Sync only security groups
Sync-FGGroupMember -Filter "securityEnabled eq true"
# Sync specific groups
Sync-FGGroupMember -GroupIds @('group-id-1', 'group-id-2')
# Include nested group members
Sync-FGGroupMember -IncludeTransitiveMembers
Performance Notes:
- Must iterate through each group individually (Graph API limitation)
- Shows progress every 10 groups processed
- Progress tracking every 1000 memberships synced
- Transaction-based for optimal performance
- Typical rate: ~5-10 groups/sec depending on member counts
Total Files: 2 (1 new, 1 modified)
Lines of Code: ~380 new lines in Sync-FGGroupMember
This commit adds dedicated support for syncing transitive (nested) group
memberships separately from direct memberships.
New Features:
- Sync-FGGroupTransitiveMember: Syncs ALL group memberships including nested
* Same structure as Sync-FGGroupMember (groupId, memberId, memberType)
* Uses /transitiveMembers endpoint instead of /members
* Default table: GraphGroupTransitiveMembers (separate from direct members)
* Composite primary key (groupId, memberId)
* Temporal versioning for tracking changes over time
* Iterates through each group to fetch all transitive members
* Progress tracking and transaction-based syncing
Use Cases:
- Track ALL users with access through nested groups
- Separate tracking of direct vs transitive memberships
- Audit complete access paths through group hierarchies
- Compare direct vs effective memberships over time
Difference from Sync-FGGroupMember:
- Sync-FGGroupMember: Direct members only
- Sync-FGGroupTransitiveMember: All members including nested (transitive)
Example Usage:
# Sync all transitive memberships
Sync-FGGroupTransitiveMember
# Sync only security groups (transitive)
Sync-FGGroupTransitiveMember -Filter "securityEnabled eq true"
# Sync specific groups (transitive)
Sync-FGGroupTransitiveMember -GroupIds @('group-id-1', 'group-id-2')
Query Examples:
-- Find all effective members of a group (including nested)
SELECT * FROM GraphGroupTransitiveMembers
WHERE groupId = 'group-guid'
-- Compare direct vs transitive memberships
SELECT
t.groupId,
t.memberId,
CASE WHEN d.memberId IS NULL THEN 'Nested' ELSE 'Direct' END AS MembershipType
FROM GraphGroupTransitiveMembers t
LEFT JOIN GraphGroupMembers d
ON t.groupId = d.groupId AND t.memberId = d.memberId
WHERE t.groupId = 'group-guid'
-- Find users who have access through nested groups only
SELECT t.*
FROM GraphGroupTransitiveMembers t
LEFT JOIN GraphGroupMembers d
ON t.groupId = d.groupId AND t.memberId = d.memberId
WHERE d.memberId IS NULL
Benefits:
- Maintain separate tables for direct and transitive memberships
- Track changes to nested group structures over time
- Understand full access paths and effective permissions
- Audit nested group membership changes
Total Files: 1 new file
Lines of Code: ~380 lines
This commit adds a helper function to create SQL views that simplify
analyzing direct vs indirect/nested group memberships.
New Features:
- Initialize-FGGroupMembershipViews: Creates two analytical SQL views
* Automatically creates views based on membership tables
* Supports custom table names
* Drop and recreate capability
Views Created:
1. vw_GraphGroupNestedMembers
- Shows ONLY members who have indirect/nested access
- Excludes direct members
- Useful for: Finding hidden access paths through nested groups
- Columns: groupId, memberId, memberType, ValidFrom, ValidTo
2. vw_GraphGroupMembershipType
- Shows ALL members (both direct and indirect)
- Includes membershipType column: "Direct" or "Indirect"
- Useful for: Complete membership analysis with type indicator
- Columns: groupId, memberId, memberType, membershipType, ValidFrom, ValidTo
Usage:
# Create the views (run once after syncing membership data)
Initialize-FGGroupMembershipViews
# Recreate views
Initialize-FGGroupMembershipViews -DropIfExists
# Use custom table names
Initialize-FGGroupMembershipViews -DirectMembersTable "MyDirectMembers" -TransitiveMembersTable "MyTransitiveMembers"
Query Examples:
-- Find all nested members of a specific group
SELECT * FROM vw_GraphGroupNestedMembers
WHERE groupId = 'group-guid'
-- Count direct vs indirect members per group
SELECT
groupId,
membershipType,
COUNT(*) AS MemberCount
FROM vw_GraphGroupMembershipType
GROUP BY groupId, membershipType
-- Find users with only indirect access
SELECT DISTINCT
v.memberId,
u.userPrincipalName,
u.displayName
FROM vw_GraphGroupNestedMembers v
INNER JOIN GraphUsers u ON v.memberId = u.id
WHERE v.memberType = '#microsoft.graph.user'
-- Compare membership types across all groups
SELECT
g.displayName AS GroupName,
v.membershipType,
COUNT(*) AS Count
FROM vw_GraphGroupMembershipType v
INNER JOIN GraphGroups g ON v.groupId = g.id
GROUP BY g.displayName, v.membershipType
ORDER BY g.displayName, v.membershipType
Benefits:
- No need to write complex LEFT JOIN queries
- Consistent logic across all queries
- Easy to understand membership hierarchy
- Temporal data (ValidFrom/ValidTo) preserved for point-in-time queries
- Works seamlessly with GraphUsers and GraphGroups tables
Requirements:
- GraphGroupMembers table (from Sync-FGGroupMember)
- GraphGroupTransitiveMembers table (from Sync-FGGroupTransitiveMember)
Total Files: 1 new file
Lines of Code: ~160 lines
- Add Sync-FGGroupEligibleMember function for PIM eligible group memberships - Syncs eligible members from PIM-enabled groups (isAssignableToRole = true) - Uses /identityGovernance/privilegedAccess/group/eligibilitySchedules endpoint - Creates GraphGroupEligibleMembers table with composite key (groupId, memberId) - Includes memberType for filtering - Update Initialize-FGGroupMembershipViews with eligible member support - Add vw_GraphGroupEligibleMembers view (shows only PIM eligible members) - Enhance vw_GraphGroupMembershipType to include Direct/Indirect/Eligible indicator - Use UNION to include eligible-but-not-active members - Support both PIM and non-PIM environments gracefully
- Add group sync features to Features section - Add Quick Start examples for group and membership syncing - Document Sync-FGGroup with 20+ default attributes - Document Sync-FGGroupMember for direct memberships - Document Sync-FGGroupTransitiveMember for nested memberships - Document Sync-FGGroupEligibleMember for PIM eligible memberships - Document Initialize-FGGroupMembershipViews with all 3 views - Add query examples for membership analysis - Explain composite primary keys and many-to-many relationships - Provide use cases and examples for each function
Add Tests 16-22 to integration test suite: - Test 16: Sync-FGGroup with default properties - Test 17: Sync-FGGroupMember (direct memberships) - Verifies composite primary key (groupId, memberId) - Validates table structure - Test 18: Sync-FGGroupTransitiveMember (nested memberships) - Compares direct vs transitive membership counts - Shows additional nested members - Test 19: Sync-FGGroupEligibleMember (PIM memberships) - Checks for PIM-enabled groups first - Gracefully skips if no PIM groups or PIM not available - Test 20: Initialize-FGGroupMembershipViews - Creates all 3 membership analysis views - Verifies view creation - Test 21: Query group membership views - Tests nested members view - Shows membership type breakdown (Direct/Indirect/Eligible) - Displays sample memberships - Test 22: Group sync summary - Comprehensive summary of groups, direct, and transitive memberships - Shows sync timestamps Features: - All tests include proper data verification - Graceful handling of optional PIM functionality - Detailed output with colored formatting - Resource registration for cleanup - Incremental test numbering (16-23 for cleanup)
Changes: - Remove ownerId from Sync-FGGroup function - Removed from default attributes list - Removed from type mapping - Removed expand logic for owners collection - Removed special processing for first owner - Add new Sync-FGGroupOwner function - Dedicated many-to-many table for group ownership - Composite primary key (groupId, ownerId) - Syncs all owners (not just first owner) - Temporal versioning for ownership changes - Follows same pattern as Sync-FGGroupMember - Update Initialize-FGGroupMembershipViews to include owners - Add OwnersTable parameter (default: "GraphGroupOwners") - Update vw_GraphGroupMembershipType view to include "Owner" type - Owner takes priority in membership type (Owner > Direct > Eligible > Indirect) - Add UNION for owners not in transitive members - Update view descriptions and output to reflect owner support - Gracefully handles missing owners table (optional like eligible) Benefits: - More accurate representation (groups can have multiple owners) - Consistent pattern with other many-to-many relationships - Temporal tracking of ownership changes over time - Views now show complete picture: Owner/Direct/Indirect/Eligible
Bug: The vw_GraphGroupMembershipType view was referencing o.ownerId in the CASE statement even when the owners table didn't exist, causing: "The multi-part identifier 'o.ownerId' could not be bound." Fix: Conditionally build the CASE statement to only reference o.ownerId when $ownersExists is true. Before: CASE always checked o.ownerId first (fails if table missing) After: CASE only checks o.ownerId when owners table exists This allows the view to be created successfully in environments without the GraphGroupOwners table, showing only Direct/Indirect (and Eligible if that table exists).
Create Test-Integration-Fast.ps1 for rapid iteration during development: Features: - Validates existing SQL Server instead of creating new one - Clears all existing tables and views - Runs all same tests as full integration test - Keeps SQL Server by default for next run - Optional -RemoveServer flag to clean up Benefits: - Saves 3-5 minutes per test run (no server/DB creation) - Perfect for development workflow - Reuses Azure resources efficiently Usage: 1. First run: Test-Integration.ps1 -SkipCleanup (creates server, ~10 minutes) 2. Subsequent runs: Test-Integration-Fast.ps1 -ConfigFile config.json (reuses server, ~5 minutes) 3. Final cleanup: Test-Integration-Fast.ps1 -ConfigFile config.json -RemoveServer (removes all resources) The fast test: - Validates SQL Server exists (fails fast if not found) - Clears all tables (with proper temporal table handling) - Drops all views - Runs Tests 7-22 (all sync and query tests) - Preserves server for next iteration
Problem: - Test 6 cleared table data but dropped all views - Tests 7-9 tried to create tables that already existed - Test 14 failed because helper views were missing Solution: - Remove view dropping logic from Test 6 (preserve views) - Skip Tests 7-9 entirely (tables already exist) - Jump directly to sync tests which use existing infrastructure This aligns with the fast test's purpose: reuse existing SQL Server infrastructure to speed up testing.
Test 14 queries temporal history views (vw_*_AllHistory) which are only created by Initialize-FGSQLTable during table creation. Since the fast test: - Skips table creation (tables already exist) - Only syncs data to existing tables - Never calls Initialize-FGSQLTable The helper views don't exist in the fast test scenario. This test validates SQL Server temporal features which are already covered in the full integration test. The fast test focuses on sync speed.
Problem: - All sync functions used Invoke-RestMethod directly - No token validation before API calls - In large environments, sync operations take a long time - Access tokens expire during long-running operations - Subsequent API calls fail with 401 Unauthorized Solution: - Replace all Invoke-RestMethod calls with Invoke-FGGetRequest - Invoke-FGGetRequest automatically checks token validity before each call - Auto-refreshes expired tokens using stored credentials - Simplifies pagination handling (now automatic) Changes: - Sync-FGUser: Replace manual pagination with Invoke-FGGetRequest - Sync-FGGroup: Replace manual pagination with Invoke-FGGetRequest - Sync-FGGroupMember: Fix 3 API call locations (groups + members loop) - Sync-FGGroupTransitiveMember: Fix 3 API call locations - Sync-FGGroupEligibleMember: Fix 4 API call locations (groups + nested loops) - Sync-FGGroupOwner: Fix 3 API call locations (groups + owners loop) Benefits: - Token automatically refreshed every time it expires - Works reliably in large environments with thousands of groups - Cleaner code with automatic pagination - Consistent error handling across all sync functions
Critical Bug Fix in Invoke-FGGetRequest: ======================================== Problem: - Token was captured at function start (line 13: $AccessToken = $Global:AccessToken) - When token expired, it was refreshed and stored in $Global:AccessToken - BUT the function continued using the OLD local $AccessToken variable - Result: Token refresh happened but API calls still used expired token! Solution: - Removed early token capture (line 13) - Added token capture AFTER refresh check (line 44) - Added token validation in pagination loop (tokens can expire during long pagination) - Now uses refreshed token for all API calls Impact: - Fixes "token is expired" errors in large environments - Especially critical for transitive member sync (9318 groups in test) - Token is now checked and refreshed before EVERY page request Test Summary Enhancement: ======================== Problem: - Summary only showed Groups, Direct, and Transitive memberships - Missing Eligible Memberships (PIM) and Group Ownerships Solution: - Dynamic query that checks if tables exist - Adds Eligible Memberships (PIM) if table exists - Adds Group Ownerships if table exists - Applied to both Test-Integration.ps1 and Test-Integration-Fast.ps1 New Summary Output: Groups 9318 Direct Memberships 17343 Transitive Memberships XXXXX Eligible Memberships (PIM) XXXXX ← NEW Group Ownerships XXXXX ← NEW
Problem:
- Some environments have Azure resources in one Entra ID tenant
- And Microsoft Graph API in a different Entra ID tenant
- Tests always used Graph.TenantId for Azure operations
- This caused Azure connection failures in multi-tenant scenarios
Solution:
- Add optional Azure.TenantId to config file
- If Azure.TenantId is specified, use it for Azure operations
- If not specified, fall back to Graph.TenantId (backward compatible)
- Updated both Test-Integration.ps1 and Test-Integration-Fast.ps1
Config File Structure:
{
"Azure": {
"TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", ← NEW (optional)
"SubscriptionId": "...",
"ResourceGroupName": "..."
},
"Graph": {
"TenantId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"ClientId": "..."
}
}
If Azure.TenantId is omitted, Graph.TenantId is used for both (backward compatible).
Documentation Updates: - README-Integration-Tests.md: Added Azure.TenantId to config example - Added explanation of when to use separate Azure tenant ID - Clarified backward compatibility (optional field) Template Updates: - config.test.json.template: Added Azure.TenantId field - Included inline comment explaining when it's needed - Made it clear the field is optional Users can now: - Copy template and understand multi-tenant configuration - See clear examples in the README - Understand backward compatibility (single-tenant still works)
…etup Graph.ClientSecret and Azure.AdminUserPassword are now read using Get-FGSecureConfigValue which supports: - DPAPI-encrypted values (ClientSecret_Encrypted, AdminUserPassword_Encrypted) - Automatic decryption - Prompting if not found - Auto-migration from plaintext to encrypted Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added -RuntimeVersion "7.2" to Import-AzAutomationRunbook to ensure runbooks use PowerShell 7.2 instead of defaulting to 5.1. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add -RuntimeVersion parameter with ValidateSet("5.1", "7.2") and default "7.2"
- Use variable instead of hardcoded value in Import-AzAutomationRunbook
- Runbooks now default to PowerShell 7.2 runtime without requiring explicit parameter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Runbooks are now created by default (no need to specify -CreateRunbooks) - Add -SkipRunbooks switch to skip runbook creation if needed - Updated documentation and examples Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The -RuntimeVersion parameter doesn't exist in Import-AzAutomationRunbook. Use -Type parameter with "PowerShell72" for PS 7.2 or "PowerShell" for PS 5.1. - Rename parameter from RuntimeVersion to RunbookType - Default to "PowerShell72" for PowerShell 7.2 runtime - Update documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Check if SQL Server allows Azure services (0.0.0.0 firewall rule) - If not configured, prompt user for approval before adding the rule - Automatically detect SQL Server's resource group (may differ from Automation RG) - Show clear warning if user declines or if configuration fails This is required for Azure Automation runbooks to connect to SQL Server. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use case-insensitive comparison (-ieq) when finding SQL Server - Use actual server name from Azure ($sqlServer.ServerName) instead of config value - Azure normalizes server names to lowercase, but config may have mixed case Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…lter) - Extract sync configuration from config file (Users.AdditionalAttributes, Users.Filter, Groups.Filter) - Store sync config as Automation Variables (SyncUsersAdditionalAttributes, SyncUsersFilter, SyncGroupsFilter) - Update Sync-FGUsers runbook to read and apply additional attributes and filter - Update Sync-FGGroups runbook to read and apply filter - Variables stored as comma-separated strings, parsed in runbook This allows runbooks to sync the same attributes configured in the config file. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Feature/azure automation setup
New command that walks users through creating a FortigiGraph config file step by step with sensible defaults. Passwords and secrets are automatically encrypted with DPAPI. Supports -Quick switch for essentials-only setup. The generated config works with Get-FGAccessToken, Connect-FGSQLServer, and Start-FGSync. https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
Instead of asking users to type GUIDs manually, the config wizard now: - Uses Connect-AzAccount (or reuses existing session) - Lists subscriptions to pick from - Lists resource groups to pick from - Lists existing SQL servers and databases to pick from - Auto-discovers tenant ID from the Azure context Only the Graph API Client ID/Secret still need manual entry since those come from the App Registration. https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
- Tenant ID now taken directly from Connect-AzAccount context (no separate tenant prompt or Graph TenantId question) - SQL admin password auto-generated as a 24-char cryptographically random complex password by default, with option to enter your own - Uses RandomNumberGenerator for secure password generation - Cleaner login flow: shows account + tenant, asks to confirm https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
When no existing SQL servers are found, suggests a name like sql-fortigraph-x7k2m instead of requiring manual input. User can accept with Enter or type their own. Azure SQL names must be globally unique since they become DNS names. https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
Users can now choose to create a new App Registration directly from the config wizard. The wizard will: - Create the App Registration (New-AzADApplication) - Create the Service Principal (New-AzADServicePrincipal) - Generate a 2-year client secret (New-AzADAppCredential) - Add all required Graph API permissions: User.Read.All, Group.Read.All, GroupMember.Read.All, Directory.Read.All, EntitlementManagement.Read.All, AccessReview.Read.All - Provide a direct Azure Portal URL for admin consent Falls back gracefully to manual entry if the user lacks permissions to create apps. Existing app option still available. https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
The recursive view (vw_GraphGroupMembersRecursive) replaces the need for syncing transitive members separately, saving ~75% sync time. Removed from New-FGConfig prompts, config template, and example config. The sync function itself remains for backwards compatibility. https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
- Create resource group, SQL Server + database, and Automation Account during setup - Add AdditionalAttributes empty array for Groups sync config - Add AutomationAccountName with default "aa-fortigraph" to config output - Update module version to 2.1.20260209.1200 https://claude.ai/code/session_01BSwCZCjSPByVYtMwUBjc2o
Microsoft Graph returns lowercase GUIDs for scopeOriginId in AccessPackageResourceRoleScopes, but uppercase GUIDs for group IDs. This caused JOIN failures in vw_UserPermissionAssignmentViaAccessPackage. Fix applied in two places: - Sync: normalize scopeOriginId to uppercase at sync time - Views: use UPPER() in JOINs as safety net for existing data https://claude.ai/code/session_01R9WuuiEFixJj7HCzP6dyse
Same Microsoft Graph lowercase GUID issue affects scopeId and roleId fields in AccessPackageResourceRoleScopes. Normalize all three GUID fields at sync time for consistency. https://claude.ai/code/session_01R9WuuiEFixJj7HCzP6dyse
- Added resourceProvisioningOptions to default group attributes - Added computed groupTypeCalculated field that classifies groups as: Unified Group with Team, Unified Group without Team, Distribution Group, Security Group, or Mail Enabled Security Group - Ensures required attributes are always fetched from Graph even with custom attribute sets https://claude.ai/code/session_01R9WuuiEFixJj7HCzP6dyse
…-uyh0M Claude/add sql data visualization uyh0 m
…s, add automation step - Add AuditLog.Read.All Graph API permission to fix user sync failure - Change PIM (Group Eligible Members) sync default from No to Yes - Add New-FGAzureAutomationAccount as step 4 in Next steps output https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
Same pattern as SQL Server name (e.g., aa-fortigraph-x7k2m). https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
When selecting an existing SQL Server, prompt for the existing password instead of generating a new one. Moves server selection before credential prompts so the context is correct. https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
Add -ErrorAction Stop and try/catch around New-AzAutomationAccount. Previously a creation error was non-terminating, causing the function to continue and fail on every subsequent operation (variables, modules) with cascading "resource not found" errors. https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
When an existing token belongs to a different app registration than the one specified in the config file, force re-authentication instead of reusing the stale token. This prevents permission errors when switching between app registrations. https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
Replace the stale token detection logic with a simple fresh token request on every sync. Takes < 1 second and eliminates all possible stale/wrong token issues. https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds comprehensive Azure Automation runbook management capabilities and secure credential handling to FortigiGraph. It includes new functions for starting, monitoring, and listing automation runbooks, plus utilities for securely storing and retrieving encrypted credentials from JSON configuration files.
Key Changes
New Automation Management Functions
Secure Credential Management
Configuration & Security
.gitignoreto exclude sensitive config files and Claude workspace directoriesAPI Request Improvements
Implementation Details
https://claude.ai/code/session_01UT5CbdJGD9kxhyfVziTpTD