├── .github └── workflows │ └── workloader-release.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd ├── adgroupexport │ └── cmd.go ├── adgroupimport │ └── cmd.go ├── appgroupflowsummary │ └── cmd.go ├── awslabel │ ├── aws.go │ └── cmd.go ├── azurelabel │ ├── azure.go │ └── cmd.go ├── azurenetwork │ ├── azure.go │ └── cmd.go ├── ccupdate │ └── cmd.go ├── checkversion │ └── checkversion.go ├── compatibility │ └── compatibility.go ├── containmentswitch │ └── cmd.go ├── cwpexport │ ├── cmd.go │ └── headers.go ├── cwpimport │ └── cmd.go ├── dagsync │ └── cmd.go ├── deletehrefs │ └── delete.go ├── deleteunusedlabels │ └── deleteunusedlabels.go ├── denyruleexport │ ├── cmd.go │ └── headers.go ├── denyruleimport │ └── cmd.go ├── dupecheck │ └── dupecheck.go ├── extract │ ├── extract.go │ └── zip.go ├── findfqdn │ ├── cmd.go │ └── headers.go ├── flowimport │ └── flowimport.go ├── gcplabel │ ├── cmd.go │ └── gcp.go ├── getpairingkey │ └── getpairingkey.go ├── hostparse │ ├── hostparse.go │ └── parser-table.csv ├── increasevenupdaterate │ └── cmd.go ├── iplexport │ └── iplexport.go ├── iplimport │ ├── headers.go │ ├── iplimport.go │ └── validate.go ├── iplreplace │ └── cmd.go ├── labeldimension │ ├── export.go │ └── import.go ├── labelexport │ └── cmd.go ├── labelgroupexport │ ├── headers.go │ └── labelgroupexport.go ├── labelgroupimport │ └── labelgroupimport.go ├── labelimport │ └── cmd.go ├── legacy-explorer │ └── explorer.go ├── mislabel │ └── mislabel.go ├── nen │ ├── headers.go │ ├── nen-acl.go │ └── nen-switch.go ├── netscalersync │ ├── cmd.go │ └── nssync.go ├── nicexport │ └── nicexport.go ├── nicmanage │ ├── headers.go │ └── nicmanage.go ├── pairingprofileexport │ └── cmd.go ├── pcemgmt │ ├── addpce.go │ ├── allpce.go │ ├── defaultpce.go │ ├── proxy.go │ └── removepce.go ├── permissionsexport │ └── cmd.go ├── permissionsimport │ └── cmd.go ├── portusage │ ├── cmd.go │ └── results.go ├── processexport │ └── cmd.go ├── root.go ├── ruleexport │ ├── cmd.go │ ├── headers.go │ ├── ruleusage.go │ └── trafficcount.go ├── ruleimport │ ├── headers.go │ ├── iplist.go │ ├── label.go │ ├── labelgroup.go │ ├── ruleimport.go │ ├── service.go │ ├── usergroup.go │ ├── virtualserver.go │ ├── virutalservice.go │ └── workload.go ├── rulesetexport │ └── rulesetexport.go ├── rulesetimport │ └── import.go ├── secprincipalexport │ └── cmd.go ├── secprincipalimport │ └── cmd.go ├── servicefinder │ └── servicefinder.go ├── subnet │ └── subnet.go ├── svcexport │ ├── headers.go │ └── svcexport.go ├── svcimport │ ├── cmd.go │ ├── headers.go │ ├── import.go │ └── services.go ├── templatecreate │ └── cmd.go ├── templateimport │ └── templateimport.go ├── templatelist │ └── cmd.go ├── traffic │ └── traffic.go ├── umwlcleanup │ └── umwlcleanup.go ├── unpair │ └── unpair.go ├── unusedumwl │ ├── cmd.go │ └── unusedUmwl.go ├── upgrade │ └── upgrade.go ├── venexport │ ├── cmd.go │ └── headers.go ├── venhealth │ └── cmd.go ├── venimport │ └── cmd.go ├── virtualserviceexport │ └── cmd.go ├── vmsync │ ├── cmd.go │ ├── headers.go │ ├── utils.go │ └── vcenter.go ├── wkldcleanup │ └── cmd.go ├── wkldexport │ ├── cmd.go │ ├── headers.go │ └── wkldexport.go ├── wkldimport │ ├── cmd.go │ ├── headers.go │ ├── hostname.go │ ├── import.go │ ├── interface.go │ ├── labels.go │ ├── mode.go │ ├── name.go │ └── publicip.go ├── wkldiplmapping │ └── cmd.go ├── wkldlabel │ └── cmd.go └── wkldreplicate │ └── wkkdreplicate.go ├── go.mod ├── go.sum ├── go.work ├── go.work.sum ├── main.go ├── utils ├── bom.go ├── compare.go ├── go.mod ├── go.sum ├── log.go ├── logout.go ├── logv2.go ├── newlinereplace.go ├── output.go ├── parsecsv.go ├── pce.go ├── pcev2.go ├── ptr.go ├── rfc1918.go ├── serviceports.go ├── templates.go └── version.go ├── version └── version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.code-workspace 2 | *.DS_store 3 | *.log 4 | *.csv 5 | !illumio-templates/*.csv 6 | !workloader-identifier-default.csv 7 | !*parser-table.csv* 8 | .vscode/* 9 | *.yaml 10 | *.zip 11 | version_notes 12 | cmd/virtualserviceimport/* 13 | templates/* 14 | jds* 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Workloader 2 | 3 | ## Description 4 | Workloader is an open-source CLI tool that leverages the Illumio API to manage resources and automate common tasks. Workloader's functionality includes 5 | - Import and export commands that allow management of policy objects via CSV 6 | - Automated labeling commands to assign labels to workloads based on cloud or VM tags, workload subnet, and hostname patterns 7 | - Workload management commands to review comaptibility reports in bulk, manage workload enforcement states via CSV, unpair stale/orphan workloads, and more. 8 | - Reporting commands to view rule usage, traffic exports, and more. 9 | 10 | ## Getting started 11 | Run `workloader pce-add` to initiate the prompts for the necessary information to connect to the PCE. Subsequent commands use the `pce.yaml` file generated from `pce-add` to authenticate. To see options for authenticating, see the `pce-add` help menu by running `workloader pce-add -h` or `workloader pce-add --help` 12 | 13 | ## Requirements 14 | The only requirements for workloader are the ability to run the executable and connect to the PCE over HTTPS. The server or workstation running workloader must be able to connect to the PCE like a user would with the browser. 15 | 16 | ## Permissions 17 | Workloader will be limited to functionality based on the roles assigned to the authenticated user or service account. 18 | 19 | ## Logging 20 | Logs of all commands and output are stored in a `workloader.log` file. 21 | 22 | ## Leveraging Workloader in Automation 23 | When a command modifies resources in the PCE, workloader does not trigger the action unless the `--update-pce` flag is included. Without this flag, workloader only simulates the command and logs what would happen. The `--update-pce` flag triggers a prompt for user input to run the command and make the updates. To auto-accept this prompt, as would be needed in automation (i.e., commands running on a cron job), use the `--no-prompt` flag. 24 | 25 | ## Documentation 26 | Each command is documented within the help menu. The documentation for each command includes instructions, optional flags, and examples (when relevant). To see the list of commands run `workloader -h`. To see the documentation for a command run the command name with `-h` or `--help` such as `workloader wkld-import -h`. 27 | 28 | ## Installation 29 | No installation required. Download the binary for your operating system from the [releases](https://github.com/brian1917/workloader/releases) section of this repository. 30 | 31 | ## Feedback or bugs 32 | File issues or feedback requests in the [issues](https://github.com/brian1917/workloader/issues) section of this repository. 33 | -------------------------------------------------------------------------------- /cmd/adgroupexport/cmd.go: -------------------------------------------------------------------------------- 1 | package adgroupexport 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | 9 | "github.com/brian1917/workloader/cmd/adgroupimport" 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // Declare local global variables 15 | var pce illumioapi.PCE 16 | var err error 17 | var outputFileName string 18 | var noHref bool 19 | 20 | func init() { 21 | ADGroupExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 22 | ADGroupExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 23 | ADGroupExportCmd.Flags().SortFlags = false 24 | } 25 | 26 | // LabelDimensionExportCmd runs the label-dimension-export command 27 | var ADGroupExportCmd = &cobra.Command{ 28 | Use: "adgroup-export", 29 | Short: "Create a CSV export of all AD groups in the PCE.", 30 | Long: ` 31 | Create a CSV export of all AD groups in the PCE. The update-pce and --no-prompt flags are ignored for this command.`, 32 | Run: func(cmd *cobra.Command, args []string) { 33 | 34 | // Get the PCE 35 | pce, err = utils.GetTargetPCEV2(false) 36 | if err != nil { 37 | utils.LogError(err.Error()) 38 | } 39 | 40 | exportADGroups() 41 | }, 42 | } 43 | 44 | func exportADGroups() { 45 | 46 | // Start the data slice with headers 47 | csvData := [][]string{{adgroupimport.HeaderName, adgroupimport.HeaderSid, adgroupimport.HeaderDescription}} 48 | if !noHref { 49 | csvData[0] = append(csvData[0], "href") 50 | } 51 | 52 | // Get label dimensions 53 | api, err := pce.GetADUserGroups(nil) 54 | utils.LogAPIRespV2("GetADUserGroups", api) 55 | if err != nil { 56 | utils.LogError(err.Error()) 57 | } 58 | 59 | for _, userGroup := range pce.ConsumingSecurityPrincipalsSlice { 60 | // Create the csv row entry 61 | csvRow := make(map[string]string) 62 | 63 | // Populate the entry 64 | csvRow[adgroupimport.HeaderName] = userGroup.Name 65 | csvRow[adgroupimport.HeaderSid] = userGroup.SID 66 | csvRow[adgroupimport.HeaderDescription] = userGroup.Description 67 | csvRow["href"] = userGroup.Href 68 | 69 | // Append 70 | newRow := []string{} 71 | for _, header := range csvData[0] { 72 | newRow = append(newRow, csvRow[header]) 73 | } 74 | csvData = append(csvData, newRow) 75 | 76 | } 77 | 78 | if len(csvData) > 1 { 79 | if outputFileName == "" { 80 | outputFileName = fmt.Sprintf("workloader-ad-group-export-%s.csv", time.Now().Format("20060102_150405")) 81 | } 82 | utils.WriteOutput(csvData, csvData, outputFileName) 83 | utils.LogInfo(fmt.Sprintf("%d ad groups exported.", len(csvData)-1), true) 84 | } else { 85 | utils.LogInfo("no ad groups in PCE.", true) 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /cmd/awslabel/aws.go: -------------------------------------------------------------------------------- 1 | package awslabel 2 | 3 | import "github.com/aws/aws-sdk-go/service/ec2" 4 | 5 | type AwsCLIResponse struct { 6 | Reservations []struct { 7 | ReservationId string `json:"ReservationId"` 8 | OwnerId string `json:"OwnerId"` 9 | Groups []interface{} `json:"Groups"` 10 | Instance []ec2.Instance `json:"Instances"` 11 | } 12 | } 13 | 14 | type Tags map[string]string 15 | -------------------------------------------------------------------------------- /cmd/azurelabel/azure.go: -------------------------------------------------------------------------------- 1 | package azurelabel 2 | 3 | type AzureVirtualMachine struct { 4 | VirtualMachine AzureVM `json:"virtualMachine"` 5 | } 6 | 7 | type AzureVM struct { 8 | OsProfile *AzureOsProfile `json:"osProfile"` 9 | Tags AzureTags `json:"tags"` 10 | Network *AzureNetwork `json:"network"` 11 | Name string `json:"name"` 12 | InterfaceList string 13 | } 14 | 15 | type AzureOsProfile struct { 16 | ComputerName string `json:"computerName"` 17 | } 18 | 19 | type AzureTags map[string]string 20 | 21 | type AzureNetwork struct { 22 | PrivateIPAddresses []string `json:"privateIpAddresses"` 23 | PublicIPAddresses []AzurePublicIPAddresses `json:"publicIpAddresses"` 24 | } 25 | 26 | type AzurePublicIPAddresses struct { 27 | ID string `json:"id"` 28 | IPAddress string `json:"ipAddress"` 29 | IPAllocationMethod string `json:"ipAllocationMethod"` 30 | Name string `json:"name"` 31 | ResourceGroup string `json:"resourceGroup"` 32 | Zone string `json:"zone"` 33 | } 34 | -------------------------------------------------------------------------------- /cmd/azurenetwork/azure.go: -------------------------------------------------------------------------------- 1 | package azurenetwork 2 | 3 | type AzureNetwork struct { 4 | AddressSpace *AddressSpace `json:"addressSpace"` 5 | EnableDdosProtection bool `json:"enableDdosProtection"` 6 | Etag string `json:"etag"` 7 | ID string `json:"id"` 8 | Location string `json:"location"` 9 | Name string `json:"name"` 10 | ProvisioningState string `json:"provisioningState"` 11 | ResourceGroup string `json:"resourceGroup"` 12 | ResourceGUID string `json:"resourceGuid"` 13 | Subnets *[]Subnets `json:"subnets"` 14 | Type string `json:"type"` 15 | } 16 | 17 | type AddressSpace struct { 18 | AddressPrefixes []string `json:"addressPrefixes"` 19 | } 20 | 21 | type Subnets struct { 22 | AddressPrefix string `json:"addressPrefix"` 23 | Etag string `json:"etag"` 24 | ID string `json:"id"` 25 | IPConfigurations *[]IPConfigurations `json:"ipConfigurations"` 26 | Name string `json:"name"` 27 | PrivateEndpointNetworkPolicies string `json:"privateEndpointNetworkPolicies"` 28 | PrivateLinkServiceNetworkPolicies string `json:"privateLinkServiceNetworkPolicies"` 29 | ProvisioningState string `json:"provisioningState"` 30 | ResourceGroup string `json:"resourceGroup"` 31 | Type string `json:"type"` 32 | } 33 | 34 | type IPConfigurations struct { 35 | ID string `json:"id"` 36 | ResourceGroup string `json:"resourceGroup"` 37 | } 38 | -------------------------------------------------------------------------------- /cmd/azurenetwork/cmd.go: -------------------------------------------------------------------------------- 1 | package azurenetwork 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os/exec" 8 | "strings" 9 | "time" 10 | 11 | "github.com/brian1917/illumioapi/v2" 12 | "github.com/brian1917/workloader/cmd/iplimport" 13 | "github.com/brian1917/workloader/utils" 14 | "github.com/spf13/cobra" 15 | "github.com/spf13/viper" 16 | ) 17 | 18 | var outputFileName, azureOptions string 19 | var exclVNets, exclSubnets, prefixSubnet, provision bool 20 | 21 | func init() { 22 | AzureNetworkCmd.Flags().StringVarP(&azureOptions, "options", "o", "", "AWS CLI can be extended using this option. Anything added after -o inside quotes will be passed as is(e.g \"--region us-west-1\"") 23 | AzureNetworkCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 24 | AzureNetworkCmd.Flags().BoolVarP(&provision, "provision", "p", false, "provision ip lists.") 25 | AzureNetworkCmd.Flags().BoolVar(&exclSubnets, "exclude-subnets", false, "do not include subnets.") 26 | AzureNetworkCmd.Flags().BoolVar(&exclVNets, "exclude-vnets", false, "do not include vnets.") 27 | AzureNetworkCmd.Flags().BoolVar(&prefixSubnet, "prefix-subnet", false, "include the vnet name as a prefix to the subnet.") 28 | AzureNetworkCmd.Flags().MarkHidden("debug-file") 29 | AzureNetworkCmd.Flags().SortFlags = false 30 | } 31 | 32 | // TrafficCmd runs the workload identifier 33 | var AzureNetworkCmd = &cobra.Command{ 34 | Use: "azure-network", 35 | Short: "Import Azure Virtual Networks and subnets as iplists.", 36 | Long: ` 37 | Import Azure Virtual Networks and subnets as iplists . 38 | 39 | The command relies on the Azure CLI being installed and authenticated. See here for installing the Azure CLI: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli. 40 | 41 | To test the Azure CLI is authenticated, run "az network vnet list" and ensure JSON output is displayed. 42 | 43 | A file will be produced that is passed into the ipl-import command. 44 | 45 | It is recommend to run without --update-pce first to the csv produced and what impacts of the ipl-import command. 46 | `, 47 | Run: func(cmd *cobra.Command, args []string) { 48 | 49 | // Get the PCE 50 | pce, err := utils.GetTargetPCEV2(false) 51 | if err != nil { 52 | utils.LogError(fmt.Sprintf("error getting pce - %s", err.Error())) 53 | } 54 | 55 | updatePCE := viper.Get("update_pce").(bool) 56 | noPrompt := viper.Get("no_prompt").(bool) 57 | 58 | AzureNetworks(&pce, provision, updatePCE, noPrompt) 59 | }, 60 | } 61 | 62 | func AzureNetworks(pce *illumioapi.PCE, provision, updatePCE, noPrompt bool) { 63 | 64 | // Set up the csv headers 65 | csvData := [][]string{{iplimport.HeaderName, iplimport.HeaderInclude, iplimport.HeaderExternalDataSet, iplimport.HeaderExternalDataRef}} 66 | 67 | // Get the bytes from either the CLI or the debug json file 68 | var bytes []byte 69 | 70 | // Get the VNets 71 | cmd := exec.Command("az", "network", "vnet", "list") 72 | if azureOptions != "" { 73 | cmd.Args = append(cmd.Args, strings.Split(azureOptions, " ")...) 74 | } 75 | pipe, err := cmd.StdoutPipe() 76 | if err != nil { 77 | utils.LogError(fmt.Sprintf("pipe error - %s", err.Error())) 78 | } 79 | 80 | // Run the command 81 | utils.LogInfof(true, "running command: %s", cmd.String()) 82 | if err := cmd.Start(); err != nil { 83 | utils.LogError(fmt.Sprintf("run error - %s", err.Error())) 84 | } 85 | 86 | // Read the stout 87 | bytes, err = io.ReadAll(pipe) 88 | if err != nil { 89 | utils.LogError(err.Error()) 90 | } 91 | 92 | // Unmarshall the JSON 93 | var azureVNets []AzureNetwork 94 | if err := json.Unmarshal(bytes, &azureVNets); err != nil { 95 | utils.LogErrorf("unmarshaling azure vnets - %s", err) 96 | } 97 | 98 | // Iterate through the azure VMs 99 | for _, vnet := range azureVNets { 100 | if vnet.AddressSpace == nil { 101 | utils.LogWarningf(true, "vnet: %s - nil address space. skipping", vnet.Name) 102 | continue 103 | } 104 | if !exclVNets { 105 | csvData = append(csvData, []string{vnet.Name, strings.Join(vnet.AddressSpace.AddressPrefixes, ";"), "workloader-azure-network", vnet.Name}) 106 | } 107 | if !exclSubnets { 108 | for _, subnet := range illumioapi.PtrToVal(vnet.Subnets) { 109 | subnetName := subnet.Name 110 | if prefixSubnet { 111 | subnetName = fmt.Sprintf("%s-%s", vnet.Name, subnet.Name) 112 | } 113 | csvData = append(csvData, []string{subnetName, subnet.AddressPrefix, "workloader-azure-network", fmt.Sprintf("%s-%s", vnet.Name, subnet.Name)}) 114 | } 115 | } 116 | } 117 | 118 | // Create the output file and call wkld-import 119 | if len(azureVNets) > 0 { 120 | if outputFileName == "" { 121 | outputFileName = fmt.Sprintf("workloader-azure-network-%s.csv", time.Now().Format("20060102_150405")) 122 | } 123 | utils.WriteOutput(csvData, nil, outputFileName) 124 | utils.LogInfo(fmt.Sprintf("%d networks exported", len(csvData)-1), true) 125 | 126 | utils.LogInfo("passing output into ipl-import...", true) 127 | 128 | iplimport.ImportIPLists(*pce, outputFileName, updatePCE, noPrompt, false, provision) 129 | 130 | } else { 131 | utils.LogInfo("no azure networks found", true) 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /cmd/checkversion/checkversion.go: -------------------------------------------------------------------------------- 1 | package checkversion 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // CheckVersionCmd checks if running latest workloader version 15 | var CheckVersionCmd = &cobra.Command{ 16 | Use: "check-version", 17 | Short: "Check if running latest workloader version.", 18 | Run: func(cmd *cobra.Command, args []string) { 19 | getLatestVersion() 20 | }, 21 | } 22 | 23 | // GitHubAPIResp is the response from the GitHub API 24 | type GitHubAPIResp struct { 25 | URL string `json:"url"` 26 | AssetsURL string `json:"assets_url"` 27 | UploadURL string `json:"upload_url"` 28 | HTMLURL string `json:"html_url"` 29 | ID int `json:"id"` 30 | NodeID string `json:"node_id"` 31 | TagName string `json:"tag_name"` 32 | TargetCommitish string `json:"target_commitish"` 33 | Name string `json:"name"` 34 | Draft bool `json:"draft"` 35 | Prerelease bool `json:"prerelease"` 36 | CreatedAt time.Time `json:"created_at"` 37 | PublishedAt time.Time `json:"published_at"` 38 | TarballURL string `json:"tarball_url"` 39 | ZipballURL string `json:"zipball_url"` 40 | Body string `json:"body"` 41 | } 42 | 43 | func getLatestVersion() { 44 | // Create HTTP client and request 45 | client := &http.Client{} 46 | req, err := http.NewRequest("GET", "https://api.github.com/repos/brian1917/workloader/releases/latest", nil) 47 | if err != nil { 48 | utils.LogError(err.Error()) 49 | } 50 | // Make HTTP Request 51 | resp, err := client.Do(req) 52 | if err != nil { 53 | utils.LogError(err.Error()) 54 | } 55 | defer resp.Body.Close() 56 | 57 | // Marshal the response 58 | body, err := io.ReadAll(resp.Body) 59 | if err != nil { 60 | utils.LogError(err.Error()) 61 | } 62 | var ghr GitHubAPIResp 63 | json.Unmarshal(body, &ghr) 64 | 65 | // Print output 66 | current := fmt.Sprintf("v%s", utils.GetVersion()) 67 | fmt.Printf("Your version: %s\r\n", current) 68 | fmt.Printf("Latest version on GitHub Releases: %s\r\n", ghr.TagName) 69 | if current == ghr.TagName { 70 | fmt.Println("You are on the latest version of workloader.") 71 | } else { 72 | fmt.Println("You are not on the latest version of workloader. Go to https://github.com/brian1917/workloader/releases for the latest.") 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /cmd/cwpexport/cmd.go: -------------------------------------------------------------------------------- 1 | package cwpexport 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | "github.com/brian1917/workloader/utils" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var outputFileName string 14 | 15 | func init() { 16 | ContainerProfileExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 17 | 18 | } 19 | 20 | // WkldExportCmd runs the workload identifier 21 | var ContainerProfileExportCmd = &cobra.Command{ 22 | Use: "cwp-export", 23 | Short: "Create a CSV export of all container workload profiles in the PCE.", 24 | Long: ` 25 | Create a CSV export of all container workload profiles in the PCE. 26 | 27 | Only label assignments are supported. Label restrictions will not be exported. 28 | 29 | The update-pce and --no-prompt flags are ignored for this command.`, 30 | Run: func(cmd *cobra.Command, args []string) { 31 | 32 | // Get the PCE 33 | pce, err := utils.GetTargetPCEV2(true) 34 | if err != nil { 35 | utils.LogError(err.Error()) 36 | } 37 | 38 | ExportContainerProfiles(pce) 39 | }, 40 | } 41 | 42 | func ExportContainerProfiles(pce illumioapi.PCE) { 43 | // Get all container clusters 44 | a, err := pce.GetContainerClusters(nil) 45 | utils.LogAPIRespV2("GetContainerClusters", a) 46 | if err != nil { 47 | utils.LogError(err.Error()) 48 | } 49 | 50 | // Iterate each container cluster and get the container profiles 51 | containerWkldProfiles := []illumioapi.ContainerWorkloadProfile{} 52 | for _, cc := range pce.ContainerClustersSlice { 53 | a, err := pce.GetContainerWkldProfiles(nil, cc.ID()) 54 | utils.LogAPIRespV2("GetContainerWkldProfiles", a) 55 | if err != nil { 56 | utils.LogError(err.Error()) 57 | } 58 | for _, p := range pce.ContainerWorkloadProfilesSlice { 59 | if illumioapi.PtrToVal(p.Name) == "Default Profile" { 60 | continue 61 | } 62 | p.ClusterName = cc.Name 63 | containerWkldProfiles = append(containerWkldProfiles, p) 64 | 65 | } 66 | } 67 | 68 | // Start the export with headers 69 | // Get the label keys 70 | labelKeys := []string{"role", "app", "env", "loc"} 71 | api, err := pce.GetLabelDimensions(nil) 72 | utils.LogAPIRespV2("GetLabelDimensions", api) 73 | if err != nil { 74 | utils.LogWarningf(true, "getting labels - %s - will use 4 default keys", err) 75 | } else { 76 | labelKeys = nil 77 | for _, ld := range pce.LabelDimensionsSlice { 78 | labelKeys = append(labelKeys, ld.Key) 79 | } 80 | } 81 | 82 | data := [][]string{{ContainerCluster, Name, Description, Namespace, Enforcement, Visibility, Managed}} 83 | data[0] = append(data[0], labelKeys...) 84 | data[0] = append(data[0], Href) 85 | 86 | for _, cp := range containerWkldProfiles { 87 | if err != nil { 88 | utils.LogError(err.Error()) 89 | } 90 | // Switch visibility levels 91 | visLevel := "" 92 | switch illumioapi.PtrToVal(cp.VisibilityLevel) { 93 | case "flow_summary": 94 | visLevel = "blocked_allowed" 95 | case "flow_drops": 96 | visLevel = "blocked" 97 | case "flow_off": 98 | visLevel = "off" 99 | case "enhanced_data_collection": 100 | visLevel = "enhanced_data_collection" 101 | } 102 | 103 | // Ensure we don't try to print nil pointers 104 | var name, desc string 105 | if cp.Name != nil { 106 | name = *cp.Name 107 | } 108 | if cp.Description != nil { 109 | desc = *cp.Description 110 | } 111 | 112 | // Write output 113 | row := []string{cp.ClusterName, name, desc, cp.Namespace, illumioapi.PtrToVal(cp.EnforcementMode), visLevel, strconv.FormatBool(*cp.Managed)} 114 | for _, lk := range labelKeys { 115 | row = append(row, cp.GetLabelByKey(lk)) 116 | } 117 | row = append(row, cp.Href) 118 | data = append(data, row) 119 | } 120 | 121 | // Write the csv 122 | if len(data) > 1 { 123 | if outputFileName == "" { 124 | outputFileName = fmt.Sprintf("workloader-container-wkld-profile-export-%s.csv", time.Now().Format("20060102_150405")) 125 | } 126 | utils.WriteOutput(data, data, outputFileName) 127 | utils.LogInfo(fmt.Sprintf("%d container workload profiles exported", len(data)-1), true) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /cmd/cwpexport/headers.go: -------------------------------------------------------------------------------- 1 | package cwpexport 2 | 3 | const ( 4 | ContainerCluster = "container_cluster" 5 | Name = "name" 6 | Description = "description" 7 | Namespace = "namespace" 8 | Enforcement = "enforcement" 9 | Visibility = "visibility" 10 | Managed = "managed" 11 | Role = "role" 12 | App = "app" 13 | Env = "env" 14 | Loc = "loc" 15 | Href = "href" 16 | ) 17 | -------------------------------------------------------------------------------- /cmd/deleteunusedlabels/deleteunusedlabels.go: -------------------------------------------------------------------------------- 1 | package deleteunusedlabels 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi" 7 | "github.com/brian1917/workloader/utils" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | // Set global variables for flags 12 | var pce illumioapi.PCE 13 | var err error 14 | 15 | // LabelsDeleteUnusedCmd runs the unpair 16 | var LabelsDeleteUnusedCmd = &cobra.Command{ 17 | Use: "labels-delete-unused", 18 | Short: "Delete labels that are not used.", 19 | Long: ` 20 | Delete labels that are not used. 21 | 22 | The update-pce and --no-prompt flags are ignored for this command.`, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | pce, err = utils.GetTargetPCE(true) 25 | if err != nil { 26 | utils.LogError(err.Error()) 27 | } 28 | 29 | labelsDeleteUnused() 30 | }, 31 | } 32 | 33 | func labelsDeleteUnused() { 34 | 35 | // Get all labels 36 | labels, a, err := pce.GetLabels(nil) 37 | utils.LogAPIResp("GetAllLabels", a) 38 | if err != nil { 39 | utils.LogError(err.Error()) 40 | } 41 | 42 | // For each label, try to delete it 43 | for _, l := range labels { 44 | a, err := pce.DeleteHref(l.Href) 45 | utils.LogAPIResp("DeleteHref", a) 46 | if err != nil { 47 | message := "" 48 | if a.StatusCode == 406 { 49 | message = " which often means label is currently in use." 50 | } 51 | if a.StatusCode == 401 { 52 | message = " which often means account does not have permission to delete labels." 53 | } 54 | utils.LogInfo(fmt.Sprintf("%s(%s) could not be deleted. Status code %d%s", l.Value, l.Key, a.StatusCode, message), false) 55 | } else { 56 | utils.LogInfo(fmt.Sprintf("%s(%s) deleted - Status code %d.", l.Value, l.Key, a.StatusCode), false) 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /cmd/denyruleexport/headers.go: -------------------------------------------------------------------------------- 1 | package denyruleexport 2 | 3 | const ( 4 | HeaderName = "name" 5 | HeaderHref = "href" 6 | HeaderEnabled = "enabled" 7 | HeaderDstAllWorkloads = "dst_all_workloads" 8 | HeaderDstLabels = "dst_labels" 9 | HeaderDstLabelGroups = "dst_label_groups" 10 | HeaderDstIPLists = "dst_iplists" 11 | HeaderSrcAllWorkloads = "src_all_workloads" 12 | HeaderSrcLabels = "src_labels" 13 | HeaderSrcLabelGroups = "src_label_groups" 14 | HeaderSrcIPLists = "src_iplists" 15 | HeaderServices = "services" 16 | HeaderNetworkType = "network_type" 17 | HeaderCreatedAt = "created_at" 18 | HeaderUpdatedAt = "updated_at" 19 | HeaderUpdateType = "update_type" 20 | ) 21 | 22 | func AllHeaders(noHref bool) []string { 23 | headers := []string{HeaderName} 24 | if !noHref { 25 | headers = append(headers, HeaderHref) 26 | } 27 | headers = append(headers, 28 | HeaderEnabled, 29 | HeaderSrcIPLists, 30 | HeaderSrcAllWorkloads, 31 | HeaderSrcLabels, 32 | HeaderSrcLabelGroups, 33 | HeaderDstIPLists, 34 | HeaderDstAllWorkloads, 35 | HeaderDstLabels, 36 | HeaderDstLabelGroups, 37 | HeaderServices, 38 | HeaderNetworkType, 39 | HeaderCreatedAt, 40 | HeaderUpdatedAt, 41 | HeaderUpdateType) 42 | return headers 43 | } 44 | -------------------------------------------------------------------------------- /cmd/dupecheck/dupecheck.go: -------------------------------------------------------------------------------- 1 | package dupecheck 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | ia "github.com/brian1917/illumioapi/v2" 9 | "github.com/brian1917/workloader/cmd/wkldexport" 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var pce ia.PCE 15 | var caseSensitive, oneInterfaceMatch bool 16 | var outputFileName string 17 | var err error 18 | 19 | func init() { 20 | DupeCheckCmd.Flags().BoolVarP(&caseSensitive, "case-sensitive", "c", false, "Require hostname/name matches to be case-sensitve.") 21 | DupeCheckCmd.Flags().BoolVar(&oneInterfaceMatch, "one-interface-match", false, "consider a match if at least one interface matches. default requires all interfaces to match.") 22 | DupeCheckCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 23 | 24 | DupeCheckCmd.Flags().SortFlags = false 25 | } 26 | 27 | // DupeCheckCmd summarizes flows 28 | var DupeCheckCmd = &cobra.Command{ 29 | Use: "dupecheck", 30 | Short: "Identifies duplicate hostnames and IP addresses in the PCE.", 31 | Long: ` 32 | Identifies unmanaged workloads with hostnames, names, or IP addresses also assigned to managed workloads. 33 | 34 | Interfaces with a default gateway are used on managed workloads. 35 | 36 | The --update-pce and --no-prompt flags are ignored for this command.`, 37 | Run: func(cmd *cobra.Command, args []string) { 38 | 39 | pce, err = utils.GetTargetPCEV2(false) 40 | if err != nil { 41 | utils.LogError(err.Error()) 42 | } 43 | 44 | dupeCheck() 45 | }, 46 | } 47 | 48 | func dupeCheck() { 49 | 50 | // Get all workloads 51 | apiResps, err := pce.Load(ia.LoadInput{Workloads: true, Labels: true, LabelDimensions: true}, utils.UseMulti()) 52 | utils.LogMultiAPIRespV2(apiResps) 53 | if err != nil { 54 | utils.LogError(err.Error()) 55 | } 56 | 57 | ldSlice := []string{} 58 | for _, ld := range pce.LabelDimensionsSlice { 59 | ldSlice = append(ldSlice, ld.Key) 60 | } 61 | 62 | headers := append([]string{"href", "hostname", "name", "interfaces"}, ldSlice...) 63 | // Get workload export data 64 | wkldExport := wkldexport.WkldExport{ 65 | PCE: &pce, 66 | Headers: headers, 67 | } 68 | csvDataMap := wkldExport.MapData() 69 | 70 | // Get all managed workloads 71 | managedHostNameMap := make(map[string]ia.Workload) 72 | managedIPAddressMap := make(map[string]ia.Workload) 73 | unmanagedWklds := []ia.Workload{} 74 | for _, w := range pce.WorkloadsSlice { 75 | if w.GetMode() == "unmanaged" { 76 | unmanagedWklds = append(unmanagedWklds, w) 77 | } else { 78 | if caseSensitive { 79 | managedHostNameMap[ia.PtrToVal(w.Hostname)] = w 80 | } else { 81 | managedHostNameMap[strings.ToLower(ia.PtrToVal(w.Hostname))] = w 82 | } 83 | for _, ip := range w.GetIsPWithDefaultGW() { 84 | managedIPAddressMap[ip] = w 85 | } 86 | } 87 | } 88 | 89 | // Start the header 90 | outputData := [][]string{append(headers, "reason")} 91 | 92 | // Iterate through unmanaged workloads 93 | for _, umwl := range unmanagedWklds { 94 | // Start our reason list. If this has a length 0 at the end, we don't have a dupe. 95 | reason := []string{} 96 | 97 | // Check managed hostnames 98 | hostname := strings.ToLower(ia.PtrToVal(umwl.Hostname)) 99 | if caseSensitive { 100 | hostname = ia.PtrToVal(umwl.Hostname) 101 | } 102 | if val, ok := managedHostNameMap[hostname]; ok { 103 | reason = append(reason, fmt.Sprintf("unmanaged workload hostname matches with hostname of managed workload %s", val.Href)) 104 | } 105 | 106 | // Check managed names 107 | name := strings.ToLower(ia.PtrToVal(umwl.Name)) 108 | if caseSensitive { 109 | name = ia.PtrToVal(umwl.Name) 110 | } 111 | if val, ok := managedHostNameMap[name]; ok { 112 | reason = append(reason, fmt.Sprintf("unmanaged workload name matches with hostname of managed workload %s", val.Href)) 113 | } 114 | 115 | // Check interfaces - all must exist in managed workloads to count. 116 | ifaceMatchCount := 0 117 | matches := []string{} 118 | for _, iface := range ia.PtrToVal(umwl.Interfaces) { 119 | if val, ok := managedIPAddressMap[iface.Address]; ok { 120 | ifaceMatchCount++ 121 | matches = append(matches, val.Href) 122 | } 123 | } 124 | if ifaceMatchCount == len(ia.PtrToVal(umwl.Interfaces)) { 125 | reason = append(reason, fmt.Sprintf("unmanaged interfaces match to managed IP addresses of %s", strings.Join(matches, "; "))) 126 | } else if ifaceMatchCount == 1 && oneInterfaceMatch { 127 | reason = append(reason, fmt.Sprintf("one-interface-match set to true and unmanaged interfaces matches to one of a managed IP addresses of %s", strings.Join(matches, "; "))) 128 | } 129 | 130 | // If we have reasons, append 131 | if len(reason) > 0 { 132 | outputCsvRow := []string{} 133 | for _, header := range headers { 134 | outputCsvRow = append(outputCsvRow, csvDataMap[umwl.Href][header]) 135 | } 136 | outputCsvRow = append(outputCsvRow, strings.Join(reason, ";")) 137 | outputData = append(outputData, outputCsvRow) 138 | } 139 | 140 | } 141 | // Write the output 142 | if len(outputData) > 1 { 143 | if outputFileName == "" { 144 | outputFileName = fmt.Sprintf("workloader-dupecheck-%s.csv", time.Now().Format("20060102_150405")) 145 | } 146 | utils.WriteOutput(outputData, outputData, outputFileName) 147 | utils.LogInfo(fmt.Sprintf("%d unmanaged workloads found. The output file can be used as input to workloader delete command.", len(outputData)-1), true) 148 | } else { 149 | utils.LogInfo("No duplicates found", true) 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /cmd/extract/zip.go: -------------------------------------------------------------------------------- 1 | package extract 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func zipit(source, target string) error { 12 | zipfile, err := os.Create(target) 13 | if err != nil { 14 | return err 15 | } 16 | defer zipfile.Close() 17 | 18 | archive := zip.NewWriter(zipfile) 19 | defer archive.Close() 20 | 21 | info, err := os.Stat(source) 22 | if err != nil { 23 | return nil 24 | } 25 | 26 | var baseDir string 27 | if info.IsDir() { 28 | baseDir = filepath.Base(source) 29 | } 30 | 31 | filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 32 | if err != nil { 33 | return err 34 | } 35 | 36 | header, err := zip.FileInfoHeader(info) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | if baseDir != "" { 42 | header.Name = strings.Replace(filepath.Join(baseDir, strings.TrimPrefix(path, source)), "\\", "/", -1) 43 | } 44 | 45 | if info.IsDir() { 46 | header.Name += "/" 47 | } else { 48 | header.Method = zip.Deflate 49 | } 50 | 51 | writer, err := archive.CreateHeader(header) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | if info.IsDir() { 57 | return nil 58 | } 59 | 60 | file, err := os.Open(path) 61 | if err != nil { 62 | return err 63 | } 64 | defer file.Close() 65 | _, err = io.Copy(writer, file) 66 | return err 67 | }) 68 | 69 | return err 70 | } 71 | -------------------------------------------------------------------------------- /cmd/findfqdn/cmd.go: -------------------------------------------------------------------------------- 1 | package findfqdn 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strings" 8 | 9 | "github.com/brian1917/illumioapi/v2" 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | var lookupFile, outputFileName, umwlFile string 16 | var anyIP bool 17 | 18 | func init() { 19 | FindFQDNCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 20 | FindFQDNCmd.Flags().BoolVar(&anyIP, "any-ip", false, "look up all ip addresses. default is just rfc1918.") 21 | FindFQDNCmd.Flags().StringVar(&umwlFile, "umwl-file", "", "create a new file in wkld-import format.") 22 | } 23 | 24 | // TrafficCmd runs the workload identifier 25 | var FindFQDNCmd = &cobra.Command{ 26 | Use: "find-fqdn", 27 | Short: "Perform reverse name lookup on list of IPs.", 28 | Long: ` 29 | Perform reverse name lookup on list of IPs. 30 | 31 | Use the export of the "Connection with Unknown IPs" traffic report as input. 32 | 33 | The update-pce and --no-prompt flags are ignored for this command.`, 34 | Run: func(cmd *cobra.Command, args []string) { 35 | 36 | // Get the PCE 37 | pce, err := utils.GetTargetPCEV2(false) 38 | if err != nil { 39 | utils.LogError(fmt.Sprintf("error getting pce - %s", err.Error())) 40 | } 41 | // Set the CSV file 42 | if len(args) != 1 { 43 | fmt.Println("Command requires 1 argument for file with IPs to perform Reverse lookup on. See usage help.") 44 | os.Exit(0) 45 | } 46 | 47 | lookupFile = args[0] 48 | 49 | // Get the workloads 50 | pce.Load(illumioapi.LoadInput{Workloads: true}, utils.UseMulti()) 51 | 52 | updatePCE := viper.Get("update_pce").(bool) 53 | noPrompt := viper.Get("no_prompt").(bool) 54 | 55 | FindFQDN(&pce, updatePCE, noPrompt) 56 | }, 57 | } 58 | 59 | // FindFQDN - Performs looking through CSV and taking all IPs in the IP Address field and performing reverse lookup on that IP. 60 | // Results will place the names found back into the FQDN field in the CSV and export the file either to specified named file or generic time/date file 61 | func FindFQDN(pce *illumioapi.PCE, updatePCE, noPrompt bool) { 62 | 63 | // Start the output CSV data 64 | umwlCSV := [][]string{} 65 | 66 | // Parse the input CSV 67 | csvData, err := utils.ParseCSV(lookupFile) 68 | if err != nil { 69 | utils.LogError(err.Error()) 70 | } 71 | 72 | // Create the headers 73 | headers := make(map[string]*int) 74 | 75 | ipMap := map[string]bool{} 76 | // Iterate through the CSV 77 | for rowIndex, row := range csvData { 78 | 79 | // If it's the first row, process the headers 80 | if rowIndex == 0 { 81 | for i, l := range row { 82 | x := i 83 | headers[l] = &x 84 | } 85 | umwlCSV = append(umwlCSV, []string{"hostname", "ip"}) 86 | continue 87 | } 88 | 89 | // Get the lookup IP address 90 | var lookupIP string 91 | if valIP, ok := headers[HeaderIPAddr]; ok { 92 | lookupIP = row[*valIP] 93 | } else { 94 | utils.LogWarning(fmt.Sprintf("the ip address field is left blank so no lookup was performed line %d", rowIndex), false) 95 | continue 96 | } 97 | 98 | // Do RFC 1918 check 99 | if !utils.IsRFC1918(lookupIP) && !anyIP { 100 | continue 101 | } 102 | 103 | fqdn, err := net.LookupAddr(lookupIP) 104 | if err != nil { 105 | utils.LogWarningf(false, "error performing reverse lookup for IP %s: %v", lookupIP, err) 106 | } 107 | 108 | // Change the fqdn entry in the CSV unless ip address was skipped (RFC1918) 109 | if val, ok := headers[HeaderFQDN]; ok && (row[*val] == "" && len(fqdn) != 0) { 110 | row[*val] = strings.Join(fqdn, ";") 111 | if ok := ipMap[lookupIP]; ok && fqdn[0] != "" { 112 | umwlCSV = append(umwlCSV, []string{fqdn[0], lookupIP}) 113 | } 114 | } else if row[*val] != "" && fqdn != nil { 115 | umwlCSV = append(umwlCSV, []string{row[*val], lookupIP}) 116 | } 117 | ipMap[lookupIP] = true 118 | 119 | } 120 | 121 | //Output the CSV now with any FQDNs found. 122 | if len(csvData) > 1 { 123 | if outputFileName == "" { 124 | outputFileName = utils.FileName("") 125 | } 126 | utils.WriteOutput(csvData, nil, outputFileName) 127 | utils.LogInfo(fmt.Sprintf("%d rows exported.", len(csvData)-1), true) 128 | 129 | //If you use the umwl-option it will cause a new file in the wkld-import format to be created." 130 | if len(umwlCSV) > 1 && umwlFile != "" { 131 | utils.WriteOutput(umwlCSV, umwlCSV, umwlFile) 132 | utils.LogInfo(fmt.Sprintf("%d rows in umwl file exported.", len(umwlCSV)-1), true) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /cmd/findfqdn/headers.go: -------------------------------------------------------------------------------- 1 | package findfqdn 2 | 3 | const ( 4 | HeaderIPAddr = "IP Address" 5 | HeaderFQDN = "FQDN" 6 | HeaderTransmission = "Transmission" 7 | HeaderDestPortProt = "Destination Port/Process" 8 | HeaderDirection = "Direction" 9 | HeaderWklds = "Workloads" 10 | HeaderFlows = "Flows" 11 | ) 12 | -------------------------------------------------------------------------------- /cmd/gcplabel/gcp.go: -------------------------------------------------------------------------------- 1 | package gcplabel 2 | 3 | type GcpCLIResponse struct { 4 | Name string `json:"name"` 5 | Id string `json:"id"` 6 | Labels map[string]string `json:"labels"` 7 | NetworkInterfaces []NetworkInterface `json:"networkInterfaces"` 8 | } 9 | 10 | type NetworkInterface struct { 11 | Name string `json:"name"` 12 | NetworkIP string `json:"networkIP"` 13 | } 14 | 15 | type Tags map[string]string 16 | -------------------------------------------------------------------------------- /cmd/getpairingkey/getpairingkey.go: -------------------------------------------------------------------------------- 1 | package getpairingkey 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // Declare local global variables 15 | var pce illumioapi.PCE 16 | var err error 17 | var create, noStdOut bool 18 | var profile, pkFile, venType string 19 | 20 | // Init handles flags 21 | func init() { 22 | GetPairingKey.Flags().StringVarP(&profile, "profile", "p", "Default (Servers)", "pairing profile name.") 23 | GetPairingKey.Flags().StringVarP(&pkFile, "file", "f", "", "file to store pairing key. use \"0\" for default name of --pk.txt") 24 | GetPairingKey.Flags().BoolVarP(&noStdOut, "no-std-out", "n", false, "do not print the pairing key to the screen.") 25 | GetPairingKey.Flags().BoolVarP(&create, "create", "c", false, "create pairing profile if it does not exist.") 26 | GetPairingKey.Flags().StringVarP(&venType, "ven-type", "v", "", "ven type (endpoint or server) used in conjunction with --create option") 27 | 28 | GetPairingKey.Flags().SortFlags = false 29 | } 30 | 31 | // GetPairingKey gets a pairing key 32 | var GetPairingKey = &cobra.Command{ 33 | Use: "get-pk", 34 | Short: "Get a pairing key.", 35 | Long: ` 36 | Gets a pairing key. The default pairing profile is used unless a profile name is specified with --profile (-p). 37 | 38 | The update-pce and --no-prompt flags are ignored for this command.`, 39 | Run: func(cmd *cobra.Command, args []string) { 40 | 41 | // Get the PCE 42 | pce, err = utils.GetTargetPCEV2(false) 43 | if err != nil { 44 | utils.LogError(err.Error()) 45 | } 46 | 47 | if create && (venType != "server" && venType != "endpoint") { 48 | utils.LogError("ven type must be server or endpoint with create flag.") 49 | } 50 | 51 | getPK() 52 | }, 53 | } 54 | 55 | func getPK() { 56 | 57 | // Get all pairing profiles 58 | pps, a, err := pce.GetPairingProfiles((map[string]string{"name": profile})) 59 | utils.LogAPIRespV2("GetAllPairingProfiles", a) 60 | if err != nil { 61 | utils.LogError(err.Error()) 62 | } 63 | 64 | match := false 65 | var targetPairingProfile illumioapi.PairingProfile 66 | for _, pp := range pps { 67 | if pp.Name == profile { 68 | match = true 69 | targetPairingProfile = pp 70 | break 71 | } 72 | } 73 | 74 | if !match && create { 75 | utils.LogInfof(false, "%s doesn't exist - creating", profile) 76 | createdPP, api, err := pce.CreatePairingProfile(illumioapi.PairingProfile{Name: profile, Enabled: illumioapi.Ptr(true), VenType: venType}) 77 | utils.LogAPIRespV2("CreatePairingProfile", api) 78 | if err != nil { 79 | utils.LogErrorf("creating pairing profile - %s", err) 80 | } 81 | targetPairingProfile = createdPP 82 | } 83 | 84 | if !match && !create { 85 | utils.LogErrorf("%s does not exist. rerun with --create (-c) flag to create it.", profile) 86 | } 87 | 88 | // Get pairing key 89 | pk, a, err := pce.CreatePairingKey(targetPairingProfile) 90 | utils.LogAPIRespV2("CreatePairingKey", a) 91 | if err != nil { 92 | utils.LogError(err.Error()) 93 | } 94 | if !noStdOut { 95 | fmt.Println(pk.ActivationCode) 96 | } 97 | 98 | // Write the pairing key to a file 99 | if pkFile != "" { 100 | if pkFile == "0" { 101 | pkFile = fmt.Sprintf("%s-%s-pk.txt", pce.FriendlyName, profile) 102 | } 103 | file, err := os.Create(pkFile) 104 | if err != nil { 105 | utils.LogError(err.Error()) 106 | } 107 | defer file.Close() 108 | _, err = io.WriteString(file, pk.ActivationCode) 109 | if err != nil { 110 | utils.LogError(err.Error()) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cmd/hostparse/parser-table.csv: -------------------------------------------------------------------------------- 1 | REGEX,ROLE,APP,ENV,LOC, 2 | (dc)--(\w*)(\d+),DC,INFRA,CORE,pod$3,eg. dc-pod2 3 | (dc)-(\w*)(\d+),DC,INFRA,CORE,pod$3,eg. dc-pod2 4 | (h)(1)-(\w*)-([s])(\d+),WEB,${3},SITE${5},Amazon,eg. h1-app-s1 5 | (h)(2)-(\w*)-([s])(\d+),WEB,${3},SITE${5},Amazon,eg. h2-app-s1 6 | (h)(3)-(\w*)-([s])(\d+),APP,${3},SITE${5},Amazon,eg. h2-app-s1 7 | (h)(4)-(\w*)-([s])(\d+),APP,${3},SITE${5},Amazon,eg. h2-app-s1 8 | (h)(5)-(\w*)-([s])(\d+),DB,${3},SITE${5},Amazon,eg. h2-app-s1 9 | (h)(6)-(\w*)-([s])(\d+),DB,${3},SITE${5},Amazon,eg. h2-app-s1 10 | (h)(7)-(\w*)-([s])(\d+),TermServ,${3},SITE${5},Amazon,eg. h2-app-s1 11 | (h)(8)-(\w*)-([s])(\d+),SiteServer,${3},SITE${5},Amazon,eg. h2-app-s1 12 | (h)(1)-(\w*)-([d])(\d+),WEB,${3},${4}${5},Amazon,eg. h1-app-s1 13 | (h)(2)-(\w*)-([d])(\d+),WEB,${3},${4}${5},Amazon,eg. h2-app-s1 14 | (h)(3)-(\w*)-([d])(\d+),APP,${3},${4}${5},Amazon,eg. h2-app-s1 15 | (h)(4)-(\w*)-([d])(\d+),APP,${3},${4}${5},Amazon,eg. h2-app-s1 16 | (h)(5)-(\w*)-([d])(\d+),DB,${3},${4}${5},Amazon,eg. h2-app-s1 17 | (h)(6)-(\w*)-([d])(\d+),DB,${3},${4}${5},Amazon,eg. h2-app-s1 18 | (h)(7)-(\w*)-([d])(\d+),TermServ,${3},${4}${5},Amazon,eg. h2-app-s1 19 | (h)(8)-(\w*)-([d])(\d+),SiteServer,${3},${4}${5},Amazon,eg. h2-app-s1 20 | ([ENen].)([SWsw])([A-Za-z]{2})(\d)([A-Za-z]{2})\d+,${1},${3},${2}${2},${1}, 21 | ([ENen].)([IMim])([A-Za-z]{3})([A-Za-z]{2})\d+,${4},${3},INFRA,${1}, 22 | ([ENen].)([IMim])([A-Za-z]{3})([A-Za-z]{3})\d+,${4},,INFRA,${1}, 23 | ([ENen].)([Mm])([A-Za-z]{3})([A-Za-z]{2})\d+,${4},${3},MGMT,${1}, 24 | (DR)(\d{2})(S)([A-Za-z]{3})([A-Za-z]{2})\d+,${1},${4},${2}${2},${1}${2}-TEST, 25 | (D)(\d{2})(P)([A-Z]{3})([A-Z]{2})\d+,${5},${4},${3},${1}${2}, 26 | (D)(\d{2})([A-Z]{2})(\d{2})([A-Z]{2})\d+,${5},${3},${4},${1}${2}, 27 | (DW)(\d{2})([A-Za-z]{2})(\d{2})([A-Za-z]{2})\d+,${5},${3},${2}_${4},${1},DW00RE01AG01 28 | ([A-Za-z0-9]*)\.([A-Za-z0-9]*)\.([A-Za-z0-9]*)\.\w+,${1},${2},,,Web.App1.Illumio.com 29 | (DV)(\d)(WS)(\d{2})(SSIS),DATABASE,APP-$5,$1-DEV,WAP, 30 | (DV)(\d)(WS)(\d{2})(SSO),APP,ADD_WSUS_MAA,DEV,WAP, 31 | (\w*)\.(\w*),Cool,${1},${2},, -------------------------------------------------------------------------------- /cmd/iplimport/headers.go: -------------------------------------------------------------------------------- 1 | package iplimport 2 | 3 | const ( 4 | HeaderHref = "href" 5 | HeaderName = "name" 6 | HeaderDescription = "description" 7 | HeaderInclude = "include" 8 | HeaderExclude = "exclude" 9 | HeaderFqdns = "fqdns" 10 | HeaderExternalDataSet = "external_data_set" 11 | HeaderExternalDataRef = "external_data_ref" 12 | ) 13 | -------------------------------------------------------------------------------- /cmd/iplimport/validate.go: -------------------------------------------------------------------------------- 1 | package iplimport 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | func ValidateIplistEntry(entry string) bool { 9 | // Process cidr 10 | if strings.Contains(entry, "/") { 11 | _, _, err := net.ParseCIDR(entry) 12 | if err != nil { 13 | return false 14 | } else { 15 | return true 16 | } 17 | } 18 | 19 | // Process non cidr 20 | for _, i := range strings.Split(entry, "-") { 21 | if ipAddress := net.ParseIP(i); ipAddress == nil { 22 | return false 23 | } 24 | } 25 | return true 26 | } 27 | -------------------------------------------------------------------------------- /cmd/labeldimension/export.go: -------------------------------------------------------------------------------- 1 | package labeldimension 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | 9 | "github.com/brian1917/workloader/utils" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | const ( 14 | HeaderHref = "href" 15 | HeaderKey = "key" 16 | HeaderDisplayName = "display_name" 17 | HeaderFGColor = "foreground_color" 18 | HeaderBGColor = "background_color" 19 | HeaderInitial = "initial" 20 | HeaderDisplayPlural = "display_name_plural" 21 | HeaderIcon = "icon" 22 | HeaderExternalDataSet = "external_dataset" 23 | HeaderExternalDataRef = "external_dataref" 24 | ) 25 | 26 | // Declare local global variables 27 | var pce illumioapi.PCE 28 | var err error 29 | var outputFileName string 30 | var noHref bool 31 | 32 | func init() { 33 | LabelDimensionExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 34 | LabelDimensionExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 35 | LabelDimensionExportCmd.Flags().SortFlags = false 36 | } 37 | 38 | // LabelDimensionExportCmd runs the label-dimension-export command 39 | var LabelDimensionExportCmd = &cobra.Command{ 40 | Use: "label-dimension-export", 41 | Short: "Create a CSV export of all labels dimensions in the PCE.", 42 | Long: ` 43 | Create a CSV export of all label dimensions in the PCE. The update-pce and --no-prompt flags are ignored for this command.`, 44 | Run: func(cmd *cobra.Command, args []string) { 45 | 46 | // Get the PCE 47 | pce, err = utils.GetTargetPCEV2(true) 48 | if err != nil { 49 | utils.LogError(err.Error()) 50 | } 51 | 52 | exportLabelDimensions() 53 | }, 54 | } 55 | 56 | func exportLabelDimensions() { 57 | 58 | // Start the data slice with headers 59 | csvData := [][]string{{HeaderKey, HeaderDisplayName, HeaderFGColor, HeaderBGColor, HeaderInitial, HeaderDisplayPlural, HeaderIcon, HeaderExternalDataSet, HeaderExternalDataRef}} 60 | if !noHref { 61 | csvData[0] = append(csvData[0], HeaderHref) 62 | } 63 | 64 | // Get label dimensions 65 | api, err := pce.GetLabelDimensions(nil) 66 | utils.LogAPIRespV2("GetLabelDimensions", api) 67 | if err != nil { 68 | utils.LogError(err.Error()) 69 | } 70 | 71 | for _, ld := range pce.LabelDimensionsSlice { 72 | // Create the csv row entry 73 | csvRow := make(map[string]string) 74 | 75 | // Populate the entry 76 | csvRow[HeaderKey] = ld.Key 77 | csvRow[HeaderDisplayName] = ld.DisplayName 78 | csvRow[HeaderExternalDataSet] = illumioapi.PtrToVal(ld.ExternalDataSet) 79 | csvRow[HeaderExternalDataRef] = illumioapi.PtrToVal(ld.ExternalDataReference) 80 | csvRow[HeaderHref] = ld.Href 81 | if ld.DisplayInfo != nil { 82 | csvRow[HeaderFGColor] = ld.DisplayInfo.ForegroundColor 83 | csvRow[HeaderBGColor] = ld.DisplayInfo.BackgroundColor 84 | csvRow[HeaderInitial] = ld.DisplayInfo.Initial 85 | csvRow[HeaderDisplayPlural] = ld.DisplayInfo.DisplayNamePlural 86 | csvRow[HeaderIcon] = ld.DisplayInfo.Icon 87 | } 88 | 89 | // Append 90 | newRow := []string{} 91 | for _, header := range csvData[0] { 92 | newRow = append(newRow, csvRow[header]) 93 | } 94 | csvData = append(csvData, newRow) 95 | 96 | } 97 | 98 | if len(csvData) > 1 { 99 | if outputFileName == "" { 100 | outputFileName = fmt.Sprintf("workloader-label-dimension-export-%s.csv", time.Now().Format("20060102_150405")) 101 | } 102 | utils.WriteOutput(csvData, csvData, outputFileName) 103 | utils.LogInfo(fmt.Sprintf("%d label dimensions exported.", len(csvData)-1), true) 104 | } else { 105 | // Log command execution for 0 results 106 | utils.LogInfo("no label dimensions in PCE.", true) 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /cmd/labelexport/cmd.go: -------------------------------------------------------------------------------- 1 | package labelexport 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | "time" 8 | 9 | "github.com/brian1917/workloader/cmd/labelimport" 10 | 11 | "github.com/brian1917/illumioapi/v2" 12 | 13 | "github.com/brian1917/workloader/utils" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | // Declare local global variables 18 | var pce illumioapi.PCE 19 | var err error 20 | var search, outputFileName string 21 | var noHref bool 22 | 23 | func init() { 24 | LabelExportCmd.Flags().StringVarP(&search, "search", "s", "", "Only export labels containing a specific string (not case sensitive)") 25 | LabelExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 26 | LabelExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 27 | 28 | LabelExportCmd.Flags().SortFlags = false 29 | 30 | } 31 | 32 | // LabelExportCmd runs the label-export command 33 | var LabelExportCmd = &cobra.Command{ 34 | Use: "label-export", 35 | Short: "Create a CSV export of all labels in the PCE.", 36 | Long: ` 37 | Create a CSV export of all labels in the PCE. The update-pce and --no-prompt flags are ignored for this command.`, 38 | Run: func(cmd *cobra.Command, args []string) { 39 | 40 | // Get the PCE 41 | pce, err = utils.GetTargetPCEV2(true) 42 | if err != nil { 43 | utils.LogError(err.Error()) 44 | } 45 | 46 | exportLabels() 47 | }, 48 | } 49 | 50 | func exportLabels() { 51 | 52 | // Start the data slice with headers 53 | csvData := [][]string{{labelimport.HeaderHref, labelimport.HeaderKey, labelimport.HeaderValue, labelimport.HeaderCreatedBy, labelimport.HeaderCreatedAt, labelimport.HeaderUpdatedBy, labelimport.HeaderUpdatedAt, labelimport.HeaderExtDataSet, labelimport.HeaderExtDataSetRef, "virtual_server_usage", "label_group_usage", "ruleset_usage", "static_policy_scopes_usage", "pairing_profile_usage", "permission_usage", "workload_usage", "container_workload_usage", "firewall_coexistence_scope_usage", "containers_inherit_host_policy_scopes_usage", "container_workload_profile_usage", "blocked_connection_reject_scope_usage", "enforcement_boundary_usage", "loopback_interfaces_in_policy_scopes_usage", "virtual_service_usage"}} 54 | if noHref { 55 | csvData = [][]string{{labelimport.HeaderKey, labelimport.HeaderValue, labelimport.HeaderCreatedBy, labelimport.HeaderCreatedAt, labelimport.HeaderUpdatedBy, labelimport.HeaderUpdatedAt, labelimport.HeaderExtDataSet, labelimport.HeaderExtDataSetRef, "virtual_server_usage", "label_group_usage", "ruleset_usage", "static_policy_scopes_usage", "pairing_profile_usage", "permission_usage", "workload_usage", "container_workload_usage", "firewall_coexistence_scope_usage", "containers_inherit_host_policy_scopes_usage", "container_workload_profile_usage", "blocked_connection_reject_scope_usage", "enforcement_boundary_usage", "loopback_interfaces_in_policy_scopes_usage", "virtual_service_usage"}} 56 | 57 | } 58 | stdOutData := [][]string{{"href", "key", "value"}} 59 | 60 | // Get all labels 61 | a, err := pce.GetLabels(map[string]string{"usage": "true"}) 62 | utils.LogAPIRespV2("GetAllLabels", a) 63 | if err != nil { 64 | utils.LogError(err.Error()) 65 | } 66 | 67 | // Check our search term 68 | newLabels := []illumioapi.Label{} 69 | if search != "" { 70 | for _, l := range pce.LabelsSlice { 71 | if strings.Contains(strings.ToLower(l.Value), strings.ToLower(search)) { 72 | newLabels = append(newLabels, l) 73 | } 74 | } 75 | pce.LabelsSlice = newLabels 76 | } 77 | 78 | for _, l := range pce.LabelsSlice { 79 | 80 | // Skip deleted workloads 81 | if illumioapi.PtrToVal(l.Deleted) { 82 | continue 83 | } 84 | 85 | // Append to data slice 86 | csvRow := []string{} 87 | if !noHref { 88 | csvRow = append(csvRow, l.Href) 89 | } 90 | csvRow = append(csvRow, l.Key, l.Value, illumioapi.PtrToVal(l.CreatedBy).Href, l.CreatedAt, illumioapi.PtrToVal(l.UpdatedBy).Href, l.UpdatedAt, illumioapi.PtrToVal(l.ExternalDataSet), illumioapi.PtrToVal(l.ExternalDataReference)) 91 | labelUsage := illumioapi.PtrToVal(l.LabelUsage) 92 | csvRow = append(csvRow, strconv.FormatBool(labelUsage.VirtualServer), strconv.FormatBool(labelUsage.LabelGroup), strconv.FormatBool(labelUsage.Ruleset), strconv.FormatBool(labelUsage.StaticPolicyScopes), strconv.FormatBool(labelUsage.PairingProfile), strconv.FormatBool(labelUsage.Permission), strconv.FormatBool(labelUsage.Workload), strconv.FormatBool(labelUsage.ContainerWorkload), strconv.FormatBool(labelUsage.FirewallCoexistenceScope), strconv.FormatBool(labelUsage.ContainersInheritHostPolicyScopes), strconv.FormatBool(labelUsage.ContainerWorkloadProfile), strconv.FormatBool(labelUsage.BlockedConnectionRejectScope), strconv.FormatBool(labelUsage.EnforcementBoundary), strconv.FormatBool(labelUsage.LoopbackInterfacesInPolicyScopes), strconv.FormatBool(labelUsage.VirtualService)) 93 | csvData = append(csvData, csvRow) 94 | stdOutData = append(stdOutData, []string{l.Href, l.Key, l.Value}) 95 | } 96 | 97 | if len(csvData) > 1 { 98 | if outputFileName == "" { 99 | outputFileName = fmt.Sprintf("workloader-label-export-%s.csv", time.Now().Format("20060102_150405")) 100 | } 101 | utils.WriteOutput(csvData, stdOutData, outputFileName) 102 | utils.LogInfo(fmt.Sprintf("%d labels exported.", len(csvData)-1), true) 103 | } else { 104 | // Log command execution for 0 results 105 | utils.LogInfo("no labels in PCE.", true) 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /cmd/labelgroupexport/headers.go: -------------------------------------------------------------------------------- 1 | package labelgroupexport 2 | 3 | const ( 4 | HeaderName = "name" 5 | HeaderKey = "key" 6 | HeaderDescription = "description" 7 | HeaderMemberLabels = "member_labels" 8 | HeaderMemberLabelGroups = "member_label_groups" 9 | HeaderFullyExpandedMembers = "fully_expanded_members" 10 | HeaderHref = "href" 11 | ) 12 | -------------------------------------------------------------------------------- /cmd/labelgroupexport/labelgroupexport.go: -------------------------------------------------------------------------------- 1 | package labelgroupexport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/brian1917/illumioapi" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // Declare local global variables 15 | var pce illumioapi.PCE 16 | var err error 17 | var useActive, noHref bool 18 | var outputFileName string 19 | 20 | func init() { 21 | LabelGroupExportCmd.Flags().BoolVar(&useActive, "active", false, "Use active policy versus draft. Draft is default.") 22 | LabelGroupExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 23 | LabelGroupExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 24 | 25 | LabelGroupExportCmd.Flags().SortFlags = false 26 | 27 | } 28 | 29 | // LabelGroupExportCmd runs the label-export command 30 | var LabelGroupExportCmd = &cobra.Command{ 31 | Use: "labelgroup-export", 32 | Short: "Create a CSV export of all label groups in the PCE.", 33 | Long: ` 34 | Create a CSV export of all label groups in the PCE. The update-pce and --no-prompt flags are ignored for this command. 35 | 36 | The update-pce and --no-prompt flags are ignored for this command.`, 37 | Run: func(cmd *cobra.Command, args []string) { 38 | 39 | // Get the PCE 40 | pce, err = utils.GetTargetPCE(true) 41 | if err != nil { 42 | utils.LogError(err.Error()) 43 | } 44 | 45 | exportLabels() 46 | }, 47 | } 48 | 49 | func exportLabels() { 50 | 51 | // Check active/draft 52 | provisionStatus := "draft" 53 | if useActive { 54 | provisionStatus = "active" 55 | } 56 | utils.LogInfo(fmt.Sprintf("provision status: %s", provisionStatus), false) 57 | 58 | // Start the data slice with headers 59 | csvData := [][]string{{HeaderName, HeaderKey, HeaderDescription, HeaderMemberLabels, HeaderMemberLabelGroups, HeaderFullyExpandedMembers, HeaderHref}} 60 | if noHref { 61 | csvData = [][]string{{HeaderName, HeaderKey, HeaderDescription, HeaderMemberLabels, HeaderMemberLabelGroups, HeaderFullyExpandedMembers}} 62 | 63 | } 64 | // GetAllLabelGroups 65 | apiResps, err := pce.Load(illumioapi.LoadInput{LabelGroups: true, ProvisionStatus: provisionStatus}) 66 | utils.LogMultiAPIResp(apiResps) 67 | if err != nil { 68 | utils.LogError(err.Error()) 69 | } 70 | 71 | for _, lg := range pce.LabelGroupsSlice { 72 | // Find members 73 | labels := []string{} 74 | sgs := []string{} 75 | 76 | // Iterate labels 77 | for _, l := range lg.Labels { 78 | labels = append(labels, l.Value) 79 | } 80 | // Iterate sub groups 81 | for _, sg := range lg.SubGroups { 82 | sgs = append(sgs, sg.Name) 83 | } 84 | 85 | // Expand all subgroups 86 | fullLabelHrefs := pce.ExpandLabelGroup(lg.Href) 87 | fullLabels := []string{} 88 | for _, f := range fullLabelHrefs { 89 | fullLabels = append(fullLabels, pce.Labels[f].Value) 90 | } 91 | 92 | // Append to data slice 93 | if noHref { 94 | csvData = append(csvData, []string{lg.Name, lg.Key, lg.Description, strings.Join(labels, "; "), strings.Join(sgs, ";"), strings.Join(fullLabels, "; ")}) 95 | } else { 96 | csvData = append(csvData, []string{lg.Name, lg.Key, lg.Description, strings.Join(labels, "; "), strings.Join(sgs, ";"), strings.Join(fullLabels, "; "), lg.Href}) 97 | } 98 | } 99 | 100 | if len(csvData) > 1 { 101 | if outputFileName == "" { 102 | outputFileName = fmt.Sprintf("workloader-label-group-export-%s.csv", time.Now().Format("20060102_150405")) 103 | } 104 | utils.WriteOutput(csvData, csvData, outputFileName) 105 | utils.LogInfo(fmt.Sprintf("%d label-groups exported.", len(csvData)-1), true) 106 | } else { 107 | // Log command execution for 0 results 108 | utils.LogInfo("no label-groups in PCE.", true) 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /cmd/nen/headers.go: -------------------------------------------------------------------------------- 1 | package nen 2 | 3 | type BaseSwitchData []struct { 4 | Name string `json:"name,omitempty"` 5 | IntfName string `json:"intfname,omitempty"` 6 | Href string `json:"href,omitempty"` 7 | Ips []string `json:"ips"` 8 | SetsRuleCount int //number of rules that PCE sends natively using sets 9 | RuleCount int //number of rules if not supporting sets. 10 | Rules struct { 11 | Outbound []struct { 12 | Action string `json:"action,omitempty"` 13 | Port string `json:"port,omitempty"` 14 | ProtocolNum string `json:"protocol,omitempty"` 15 | ProtocolTxt string 16 | Ips []string `json:"ips,omitempty"` 17 | OutHash uint64 `json:"outhash,omitempty"` 18 | } `json:"Outbound"` 19 | Inbound []struct { 20 | Action string `json:"action,omitempty"` 21 | Port string `json:"port,omitempty"` 22 | ProtocolNum string `json:"protocol,omitempty"` 23 | ProtocolTxt string 24 | Ips []string `json:"ips,omitempty"` 25 | InHash uint64 `json:"inhash,omitempty"` 26 | } `json:"Inbound"` 27 | } `json:"rules,omitempty"` 28 | } 29 | 30 | type ProtoPort struct { 31 | Port string 32 | ProtocolNum string 33 | ProtocolTxt string 34 | } 35 | 36 | type SwitchACLData struct { 37 | BaseSwitch BaseSwitchData 38 | ProtoPort map[string]ProtoPort 39 | HashList map[uint64][]string 40 | } 41 | 42 | type BaseSwitchConfig []struct { 43 | Href string `json:"href"` 44 | Config struct { 45 | EndpointType string `json:"endpoint_type"` 46 | Name string `json:"name"` 47 | WorkloadDiscovery bool `json:"workload_discovery"` 48 | } `json:"config"` 49 | Workloads []struct { 50 | Href string `json:"href"` 51 | } `json:"workloads"` 52 | NetworkDevice struct { 53 | Href string `json:"href"` 54 | } `json:"network_device"` 55 | Status string `json:"status"` 56 | } 57 | 58 | type IntfConfig struct { 59 | EndpointType string `json:"endpoint_type"` 60 | Name string `json:"name"` 61 | WorkloadDiscovery bool `json:"workload_discovery"` 62 | } 63 | -------------------------------------------------------------------------------- /cmd/netscalersync/cmd.go: -------------------------------------------------------------------------------- 1 | package netscalersync 2 | 3 | import ( 4 | "github.com/brian1917/illumioapi" 5 | "github.com/brian1917/ns" 6 | "github.com/brian1917/workloader/utils" 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | // Global variables 12 | var pce illumioapi.PCE 13 | var netscaler ns.NetScaler 14 | var externalDataSet string 15 | var cleanup, updatePCE, noPrompt bool 16 | var err error 17 | 18 | func init() { 19 | 20 | NetScalerSyncCmd.Flags().StringVarP(&netscaler.Server, "netscaler-server", "n", "", "netscaler server in format server.com:8080") 21 | NetScalerSyncCmd.Flags().StringVarP(&netscaler.User, "netscaler-user", "u", "", "netscaler user") 22 | NetScalerSyncCmd.Flags().StringVarP(&netscaler.Password, "netscaler-pwd", "p", "", "netscaler password") 23 | NetScalerSyncCmd.Flags().StringVarP(&externalDataSet, "externalDataSet", "e", "workloader-netscaler-sync", "external data set") 24 | NetScalerSyncCmd.Flags().BoolVarP(&cleanup, "cleanup", "c", true, "clean up virtual services (VIPs) and unmanaged workloads (SNAT IPs) in external data set that are no longer in netscaler.") 25 | NetScalerSyncCmd.Flags().SortFlags = false 26 | 27 | } 28 | 29 | // NetScalerSyncCmd runs the NetScalerSync command 30 | var NetScalerSyncCmd = &cobra.Command{ 31 | Use: "netscaler-sync", 32 | Short: "Create an Illumio Virtual Service for each Citrix virtual server and an unmanaged workload for each SNAT IP.", 33 | Long: ` 34 | Create an Illumio Virtual Service for each Citrix virtual server and an unmanaged workload for each SNAT IP. 35 | 36 | This version only supports single IP VIPs. 37 | 38 | Recommended to run without --update-pce first to log of what will change.`, 39 | 40 | Run: func(cmd *cobra.Command, args []string) { 41 | 42 | pce, err = utils.GetTargetPCE(true) 43 | if err != nil { 44 | utils.LogError(err.Error()) 45 | } 46 | 47 | // Login in to the netscaler 48 | _, err := netscaler.Login() 49 | if err != nil { 50 | utils.LogError(err.Error()) 51 | } 52 | 53 | updatePCE = viper.Get("update_pce").(bool) 54 | noPrompt = viper.Get("no_prompt").(bool) 55 | 56 | utils.LogWarning("this command has been marked for deprecation. please open an issue on GitHub if you use it and want it preserved.", true) 57 | 58 | nsSync(pce, netscaler) 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /cmd/nicexport/nicexport.go: -------------------------------------------------------------------------------- 1 | package nicexport 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/brian1917/illumioapi" 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var consolidate bool 15 | var pce illumioapi.PCE 16 | var err error 17 | var outputFileName string 18 | 19 | func init() { 20 | NICExportCmd.Flags().BoolVarP(&consolidate, "consolidate", "c", false, "instead of one line per IP address, the output is one line per interface name. If an interface has multiple addresses, they are separated by a semi-colon. This is the format if you plan to edit ignored status and feed into workloads's nic-manage command.") 21 | NICExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 22 | } 23 | 24 | // NICExportCmd produces a report of all network interfaces 25 | var NICExportCmd = &cobra.Command{ 26 | Use: "nic-export", 27 | Short: "Export all network interfaces for all managed and unmanaged workloads.", 28 | Long: ` 29 | Export all network interfaces for all managed and unmanaged workloads. 30 | 31 | The update-pce and --no-prompt flags are ignored for this command.`, 32 | Run: func(cmd *cobra.Command, args []string) { 33 | 34 | pce, err = utils.GetTargetPCE(false) 35 | if err != nil { 36 | utils.LogError(err.Error()) 37 | } 38 | 39 | nicExport() 40 | }, 41 | } 42 | 43 | func nicExport() { 44 | 45 | // Build our CSV data output 46 | headerRow := []string{"wkld_hostname", "wkld_href", "wkld_polcy_state", "nic_name", "ignored", "address", "cidr", "ipv4_net_mask", "default_gw"} 47 | data := [][]string{headerRow} 48 | 49 | // Get all workloads 50 | wklds, a, err := pce.GetWklds(nil) 51 | utils.LogAPIResp("GetAllWorkloads", a) 52 | if err != nil { 53 | utils.LogError(err.Error()) 54 | } 55 | 56 | // For each workload, iterate through the network interfaces and add to the data slice 57 | for _, w := range wklds { 58 | for _, i := range w.Interfaces { 59 | // Check if the interface is ignored 60 | ignored := false 61 | for _, ignoredInt := range *w.IgnoredInterfaceNames { 62 | if ignoredInt == i.Name { 63 | ignored = true 64 | } 65 | } 66 | // Convert the CIDR 67 | var cidr string 68 | if i.CidrBlock == nil { 69 | cidr = "" 70 | } else { 71 | cidr = fmt.Sprintf("%s/%d", i.Address, *i.CidrBlock) 72 | } 73 | data = append(data, []string{w.Hostname, w.Href, w.GetMode(), i.Name, strconv.FormatBool(ignored), i.Address, cidr, w.GetNetMask(i.Address), i.DefaultGatewayAddress}) 74 | } 75 | } 76 | 77 | type mapEntry struct { 78 | stringSlice []string 79 | order int 80 | } 81 | 82 | if consolidate { 83 | 84 | interfaceMap := make(map[string]mapEntry) 85 | for i, d := range data { 86 | // Skip the header row 87 | if i == 0 { 88 | continue 89 | } 90 | // If it's already in the map, edit the fields we need to concatenate and put in the consolidateData slice 91 | if val, ok := interfaceMap[d[1]+d[3]]; ok { 92 | interfaceMap[d[1]+d[3]].stringSlice[5] = d[5] + ";" + val.stringSlice[5] 93 | interfaceMap[d[1]+d[3]].stringSlice[6] = d[6] + ";" + val.stringSlice[6] 94 | interfaceMap[d[1]+d[3]].stringSlice[7] = d[7] + ";" + val.stringSlice[7] 95 | // If it's not in the map, add it to the map 96 | } else { 97 | interfaceMap[d[1]+d[3]] = mapEntry{stringSlice: d, order: i} 98 | } 99 | } 100 | 101 | // Add it to the consolidated data. This will be unordered - I want to fix this later. 102 | mapEntrySlice := []mapEntry{} 103 | for _, e := range interfaceMap { 104 | mapEntrySlice = append(mapEntrySlice, e) 105 | } 106 | 107 | // Sory the slice by order 108 | sort.SliceStable(mapEntrySlice, func(i, j int) bool { 109 | return mapEntrySlice[i].order < mapEntrySlice[j].order 110 | }) 111 | 112 | // Replace data 113 | data = nil 114 | data = append(data, headerRow) 115 | for _, i := range mapEntrySlice { 116 | data = append(data, i.stringSlice) 117 | } 118 | } 119 | 120 | // Write the data 121 | if outputFileName == "" { 122 | outputFileName = fmt.Sprintf("workloader-nic-export-%s.csv", time.Now().Format("20060102_150405")) 123 | } 124 | utils.WriteOutput(data, data, outputFileName) 125 | 126 | } 127 | -------------------------------------------------------------------------------- /cmd/nicmanage/headers.go: -------------------------------------------------------------------------------- 1 | package nicmanage 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | type headers struct { 11 | wkldHref int 12 | interfaceName int 13 | ignored int 14 | } 15 | 16 | func findHeaders(headerRow []string) headers { 17 | fieldMaps := fieldMapping() 18 | headers := headers{} 19 | ok := 0 20 | 21 | for i, h := range headerRow { 22 | if fieldMaps[strings.ToLower(h)] == "wkld_href" { 23 | headers.wkldHref = i 24 | ok++ 25 | } 26 | if fieldMaps[strings.ToLower(h)] == "interface_name" { 27 | headers.interfaceName = i 28 | ok++ 29 | } 30 | if fieldMaps[strings.ToLower(h)] == "ignored" { 31 | headers.ignored = i 32 | ok++ 33 | } 34 | } 35 | 36 | if ok != 3 { 37 | utils.LogError("input requires a header row with three values - wkld_href, ignored, and nic_name") 38 | } 39 | 40 | utils.LogInfo(fmt.Sprintf("wkld_href index: %d; ignored index: %d", headers.wkldHref, headers.ignored), false) 41 | 42 | return headers 43 | 44 | } 45 | 46 | func fieldMapping() map[string]string { 47 | // Check for the existing of the headers 48 | fieldMapping := make(map[string]string) 49 | 50 | // Workload HREF 51 | fieldMapping["wkld_href"] = "wkld_href" 52 | fieldMapping["wkld href"] = "wkld_href" 53 | fieldMapping["workloader_href"] = "wkld_href" 54 | fieldMapping["href"] = "wkld_href" 55 | 56 | // Interface name 57 | fieldMapping["interface_name"] = "interface_name" 58 | fieldMapping["interface name"] = "interface_name" 59 | fieldMapping["int_name"] = "interface_name" 60 | fieldMapping["int name"] = "interface_name" 61 | fieldMapping["nic name"] = "interface_name" 62 | fieldMapping["nic_name"] = "interface_name" 63 | 64 | // Ignored 65 | fieldMapping["ignored"] = "ignored" 66 | fieldMapping["ignore"] = "ignored" 67 | 68 | return fieldMapping 69 | } 70 | -------------------------------------------------------------------------------- /cmd/pairingprofileexport/cmd.go: -------------------------------------------------------------------------------- 1 | package pairingprofileexport 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | 9 | "github.com/brian1917/workloader/utils" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // Declare local global variables 14 | var pce illumioapi.PCE 15 | var err error 16 | var outputFileName string 17 | 18 | func init() { 19 | PairingProfileExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 20 | 21 | PairingProfileExportCmd.Flags().SortFlags = false 22 | 23 | } 24 | 25 | // LabelExportCmd runs the label-export command 26 | var PairingProfileExportCmd = &cobra.Command{ 27 | Use: "pairing-profile-export", 28 | Short: "Create a CSV export of all pairing profiles in the PCE.", 29 | Long: ` 30 | Create a CSV export of all pairing profiles in the PCE.`, 31 | Run: func(cmd *cobra.Command, args []string) { 32 | 33 | // Get the PCE 34 | pce, err = utils.GetTargetPCEV2(false) 35 | if err != nil { 36 | utils.LogError(err.Error()) 37 | } 38 | 39 | exportPairingProfiles() 40 | }, 41 | } 42 | 43 | func exportPairingProfiles() { 44 | 45 | // Start the data slice with headers 46 | csvData := [][]string{{"href", "name"}} 47 | 48 | // Get all labels 49 | pairingProfiles, api, err := pce.GetPairingProfiles(nil) 50 | utils.LogAPIRespV2("GetPairingProfiles", api) 51 | if err != nil { 52 | utils.LogError(err.Error()) 53 | } 54 | 55 | for _, pp := range pairingProfiles { 56 | csvData = append(csvData, []string{pp.Href, pp.Name}) 57 | } 58 | 59 | if len(csvData) > 1 { 60 | if outputFileName == "" { 61 | outputFileName = fmt.Sprintf("workloader-pairing-profile-export-%s.csv", time.Now().Format("20060102_150405")) 62 | } 63 | utils.WriteOutput(csvData, nil, outputFileName) 64 | utils.LogInfo(fmt.Sprintf("%d pairing profiles exported.", len(csvData)-1), true) 65 | } else { 66 | // Log command execution for 0 results 67 | utils.LogInfo("no pairing profiles in PCE.", true) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /cmd/pcemgmt/allpce.go: -------------------------------------------------------------------------------- 1 | package pcemgmt 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // AllPceCmd runs a command on all PCEs 8 | var AllPceCmd = &cobra.Command{ 9 | Use: "all-pces", 10 | Short: "Run a workloadaer command on all PCEs in your pce.yaml file.", 11 | Long: ` 12 | Run a workloadaer command on all pces in your pce.yaml file. 13 | 14 | Prepend the all-pces command to any workloader command to run it on all PCEs in the pce.yaml file. 15 | 16 | # Example to run a wkld-import to label and/or create unmanaged workloads in all PCEs: 17 | workloader all-pces wkld-import file.csv --update-pce --no-prompt --umwl 18 | 19 | # Example to import ip lists to all PCEs 20 | workloader all-pces ipl-import iplists.csv --update-pce --no-prompt --provision 21 | `, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | // Just a place holder function for help menu 24 | // Logic is processed in main.go 25 | }, 26 | } 27 | 28 | // AllPceCmd runs a command on all PCEs 29 | var TargetPcesCmd = &cobra.Command{ 30 | Use: "target-pces [pce-file] '[workloader command]'", 31 | Short: "Run a workloadaer command on target PCEs in your pce.yaml file.", 32 | Long: ` 33 | Run a workloadaer command on target pces in your pce.yaml file. 34 | 35 | Prepend the target-pces command and the location of a file listing the PCEs to any workloader command to run it on the target PCEs. 36 | 37 | # Example to run a wkld-import to label and/or create unmanaged workloads in all PCEs: 38 | workloader target-pces pces.csv wkld-import file.csv --update-pce --no-prompt --umwl 39 | 40 | # Example to import ip lists to all PCEs 41 | workloader target-pces pces.csv ipl-import iplists.csv --update-pce --no-prompt --provision 42 | `, 43 | Args: cobra.MinimumNArgs(1), 44 | Run: func(cmd *cobra.Command, args []string) { 45 | // Just a place holder function for help menu 46 | // Logic is processed in main.go 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /cmd/pcemgmt/defaultpce.go: -------------------------------------------------------------------------------- 1 | package pcemgmt 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/brian1917/workloader/utils" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | // PCEListCmd gets all PCEs 14 | var PCEListCmd = &cobra.Command{ 15 | Use: "pce-list", 16 | Short: "List all PCEs in pce.yaml.", 17 | PreRun: func(cmd *cobra.Command, args []string) { 18 | configFilePath, err = filepath.Abs(viper.ConfigFileUsed()) 19 | if err != nil { 20 | utils.LogError(err.Error()) 21 | } 22 | }, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | 25 | allSettings := viper.AllSettings() 26 | 27 | defaultPCEName := "" 28 | if viper.Get("default_pce_name") != nil { 29 | defaultPCEName = viper.Get("default_pce_name").(string) 30 | } 31 | 32 | count := 0 33 | for k := range allSettings { 34 | if viper.Get(k+".fqdn") != nil { 35 | if k == defaultPCEName { 36 | fmt.Printf("* %s (%s)\r\n", k, viper.Get(k+".fqdn").(string)) 37 | count++ 38 | } else { 39 | fmt.Printf(" %s (%s)\r\n", k, viper.Get(k+".fqdn").(string)) 40 | count++ 41 | } 42 | } 43 | } 44 | if count == 0 { 45 | utils.LogInfo("no pce configured. run pce-add to add a pce to pce.yaml file.", true) 46 | } 47 | 48 | }, 49 | } 50 | 51 | // GetAllPCEnames returns PCE names in the pce.yaml file 52 | func GetAllPCENames() (pceNames []string) { 53 | allSettings := viper.AllSettings() 54 | for k := range allSettings { 55 | if viper.Get(k+".fqdn") != nil { 56 | pceNames = append(pceNames, k) 57 | } 58 | } 59 | return pceNames 60 | } 61 | -------------------------------------------------------------------------------- /cmd/pcemgmt/proxy.go: -------------------------------------------------------------------------------- 1 | package pcemgmt 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/brian1917/workloader/utils" 10 | 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | // AddPCECmd generates the pce.yaml file 16 | var SetProxyCmd = &cobra.Command{ 17 | Use: "set-proxy [fqdn:port]", 18 | Short: "Set workloader-specific proxy.", 19 | Long: ` 20 | Set workloader-specific proxy. 21 | 22 | Workloader uses HTTP_PROXY and HTTPS_PROXY environment variables natively. This command is only if the proxy needs to be explicitly set for workloader outside those variables. 23 | 24 | The command requires two arguments: pce name and proxy in format of http://fqdn:port (or http://ip:port). 25 | 26 | For example, the following command sets the proxy for default-pce to http://proxy.com:8080 27 | 28 | workloader set-proxy default-pce http://proxy.com:8080 29 | `, 30 | PreRun: func(cmd *cobra.Command, args []string) { 31 | configFilePath, err = filepath.Abs(viper.ConfigFileUsed()) 32 | if err != nil { 33 | utils.LogError(err.Error()) 34 | } 35 | }, 36 | Run: func(cmd *cobra.Command, args []string) { 37 | if len(args) != 2 { 38 | utils.LogError("command requires 2 arguments for the pce name and the proxy string as fqdn:port. See usage help.") 39 | } 40 | pce, err := utils.GetPCENoAPI(args[0]) 41 | if err != nil { 42 | utils.LogError(err.Error()) 43 | } 44 | // Make sure has "http" 45 | if !strings.Contains(args[1], "http") { 46 | utils.LogError(fmt.Sprintf("%s is not a valid proxy - it must be in format of http://fqdn:port", args[1])) 47 | } 48 | // Make sure valid port 49 | s := strings.Split(args[1], ":") 50 | _, err = strconv.Atoi(s[len(s)-1]) 51 | if err != nil { 52 | utils.LogError(fmt.Sprintf("%s is not a valid proxy - it must be in format of http://fqdn:port", args[1])) 53 | } 54 | viper.Set(pce.FriendlyName+".proxy", args[1]) 55 | if err := viper.WriteConfig(); err != nil { 56 | utils.LogError(err.Error()) 57 | } 58 | 59 | }, 60 | } 61 | 62 | // ClearProxyCmd clears any set proxy 63 | var ClearProxyCmd = &cobra.Command{ 64 | Use: "clear-proxy [pce name]", 65 | Short: "Clear workloader-specific proxy.", 66 | Long: ` 67 | Clear workloader-specific proxy. 68 | `, 69 | PreRun: func(cmd *cobra.Command, args []string) { 70 | configFilePath, err = filepath.Abs(viper.ConfigFileUsed()) 71 | if err != nil { 72 | utils.LogError(err.Error()) 73 | } 74 | }, 75 | Run: func(cmd *cobra.Command, args []string) { 76 | if len(args) != 1 { 77 | utils.LogError("command requires 1 argument for the pce name. See usage help.") 78 | } 79 | pce, err := utils.GetPCEbyName(args[0], false) 80 | if err != nil { 81 | utils.LogError(err.Error()) 82 | } 83 | viper.Set(pce.FriendlyName+".proxy", "") 84 | if err := viper.WriteConfig(); err != nil { 85 | utils.LogError(err.Error()) 86 | } 87 | 88 | }, 89 | } 90 | -------------------------------------------------------------------------------- /cmd/pcemgmt/removepce.go: -------------------------------------------------------------------------------- 1 | package pcemgmt 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | 12 | "github.com/spf13/cobra" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // Set global variables for flags 17 | var clear bool 18 | var pceName string 19 | 20 | func init() { 21 | RemovePCECmd.Flags().BoolVarP(&clear, "clear-keys", "x", false, "Remove PCE from yaml file and clear all Workloader generated API credentials from the PCE.") 22 | } 23 | 24 | // RemovePCECmd removes the pce.yaml file 25 | var RemovePCECmd = &cobra.Command{ 26 | Use: "pce-remove [name of pce]", 27 | Short: "remove a pce from pce.yaml file", 28 | Long: fmt.Sprintf("\r\n%s\r\n\r\nThe --update-pce and --no-prompt flags are ignored for this command.", utils.LogOutDesc()), 29 | PreRun: func(cmd *cobra.Command, args []string) { 30 | configFilePath, err = filepath.Abs(viper.ConfigFileUsed()) 31 | if err != nil { 32 | utils.LogError(err.Error()) 33 | } 34 | }, 35 | Run: func(cmd *cobra.Command, args []string) { 36 | 37 | // Get Name of PCE 38 | if len(args) != 1 { 39 | fmt.Println("Command requires 1 argument for the name of the PCE to logout. See usage help.") 40 | os.Exit(0) 41 | } 42 | pceName = args[0] 43 | 44 | removePce() 45 | }, 46 | } 47 | 48 | func removePce() { 49 | 50 | // Start by clearing API keys 51 | if clear { 52 | 53 | // Log start of command 54 | utils.LogInfo("removing API keys...", true) 55 | 56 | // Get the PCE 57 | pce, err := utils.GetPCEbyName(pceName, false) 58 | if err != nil { 59 | utils.LogError(err.Error()) 60 | } 61 | 62 | // Get all API Keys 63 | apiKeys, _, err := pce.GetAllAPIKeys(viper.Get(pceName + ".userhref").(string)) 64 | if err != nil { 65 | utils.LogError(err.Error()) 66 | } 67 | 68 | // Delete the API keys that are from Workloader 69 | saveHref := "" 70 | for _, a := range apiKeys { 71 | if a.Name == "workloader" && a.Description == "created by workloader" { 72 | if a.AuthUsername != viper.Get(pceName+".user").(string) { 73 | _, err := pce.DeleteHref(a.Href) 74 | if err != nil { 75 | utils.LogError(err.Error()) 76 | } 77 | utils.LogInfo(fmt.Sprintf("deleted api key: %s", a.Href), true) 78 | } else { 79 | saveHref = a.Href 80 | } 81 | } 82 | } 83 | // Delete the active API Key 84 | _, err = pce.DeleteHref(saveHref) 85 | if err != nil { 86 | utils.LogError(err.Error()) 87 | } 88 | utils.LogInfo(fmt.Sprintf("deleted api key: %s", saveHref), true) 89 | } 90 | 91 | // Remove login information from YAML 92 | configMap := viper.AllSettings() 93 | delete(configMap, pceName) 94 | encodedConfig, _ := json.MarshalIndent(configMap, "", " ") 95 | err := viper.ReadConfig(bytes.NewReader(encodedConfig)) 96 | if err != nil { 97 | utils.LogError(err.Error()) 98 | } 99 | viper.WriteConfig() 100 | 101 | utils.LogInfo("Removed pce infomration from pce.yaml.", true) 102 | 103 | } 104 | -------------------------------------------------------------------------------- /cmd/permissionsexport/cmd.go: -------------------------------------------------------------------------------- 1 | package permissionsexport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // Declare local global variables 15 | var outputFileName string 16 | var noHref, groupsOnly bool 17 | 18 | const ( 19 | HeaderHref = "href" 20 | HeaderRole = "role" 21 | HeaderScope = "scope" 22 | HeaderAuthSecPrincipalName = "auth_security_principal_name" 23 | HeaderAuthSecPrincipalHref = "auth_security_principal_href" 24 | HeaderAuthSecPrincipalType = "auth_security_principal_type" 25 | ) 26 | 27 | func init() { 28 | PermissionsExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 29 | PermissionsExportCmd.Flags().BoolVar(&groupsOnly, "groups-only", false, "only export permissions attached to groups.") 30 | PermissionsExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 31 | PermissionsExportCmd.Flags().SortFlags = false 32 | } 33 | 34 | // LabelDimensionExportCmd runs the label-dimension-export command 35 | var PermissionsExportCmd = &cobra.Command{ 36 | Use: "permissions-export", 37 | Short: "Create a csv export of all permissions.", 38 | Long: ` 39 | Create a csv export of all permissions. The update-pce and --no-prompt flags are ignored for this command.`, 40 | Run: func(cmd *cobra.Command, args []string) { 41 | 42 | // Get the PCE 43 | pce, err := utils.GetTargetPCEV2(false) 44 | if err != nil { 45 | utils.LogError(err.Error()) 46 | } 47 | 48 | exportPermissions(pce) 49 | }, 50 | } 51 | 52 | func exportPermissions(pce illumioapi.PCE) { 53 | 54 | // Start the data slice with headers 55 | csvData := [][]string{{HeaderAuthSecPrincipalName, HeaderAuthSecPrincipalType, HeaderAuthSecPrincipalHref, HeaderRole, HeaderScope}} 56 | if !noHref { 57 | csvData[0] = append(csvData[0], HeaderHref) 58 | } 59 | 60 | // Get permissions and auth security principals 61 | apiResps, err := pce.Load(illumioapi.LoadInput{Permissions: true, AuthSecurityPrincipals: true, Labels: true, LabelGroups: true, ProvisionStatus: "active"}, utils.UseMulti()) 62 | utils.LogMultiAPIRespV2(apiResps) 63 | if err != nil { 64 | utils.LogErrorf("loading pce - %s", err) 65 | } 66 | 67 | for _, permission := range pce.PermissionsSlice { 68 | if groupsOnly && pce.AuthSecurityPrincipals[permission.AuthSecurityPrincipal.Href].Type != "group" { 69 | continue 70 | } 71 | csvRow := make(map[string]string) 72 | csvRow[HeaderHref] = permission.Href 73 | csvRow[HeaderAuthSecPrincipalName] = pce.AuthSecurityPrincipals[permission.AuthSecurityPrincipal.Href].DisplayName 74 | csvRow[HeaderAuthSecPrincipalHref] = permission.AuthSecurityPrincipal.Href 75 | csvRow[HeaderAuthSecPrincipalType] = pce.AuthSecurityPrincipals[permission.AuthSecurityPrincipal.Href].Type 76 | 77 | // Process the Role 78 | roleStrSplit := strings.Split(permission.Role.Href, "/") 79 | csvRow[HeaderRole] = roleStrSplit[len(roleStrSplit)-1] 80 | 81 | // Process the scope 82 | scopeStrSlice := []string{} 83 | for _, scope := range illumioapi.PtrToVal(permission.Scope) { 84 | if scope.Label != nil { 85 | label := pce.Labels[scope.Label.Href] 86 | scopeStrSlice = append(scopeStrSlice, fmt.Sprintf("%s:%s", label.Key, label.Value)) 87 | } 88 | if scope.LabelGroup != nil { 89 | labelGroup := pce.LabelGroups[scope.LabelGroup.Href] 90 | scopeStrSlice = append(scopeStrSlice, fmt.Sprintf("%s:%s-lg", labelGroup.Key, labelGroup.Name)) 91 | } 92 | } 93 | if len(scopeStrSlice) != 0 { 94 | csvRow[HeaderScope] = strings.Join(scopeStrSlice, "; ") 95 | } 96 | 97 | // Append 98 | newRow := []string{} 99 | for _, header := range csvData[0] { 100 | newRow = append(newRow, csvRow[header]) 101 | } 102 | csvData = append(csvData, newRow) 103 | 104 | } 105 | 106 | if len(csvData) > 1 { 107 | if outputFileName == "" { 108 | outputFileName = fmt.Sprintf("workloader-permissions-export-%s.csv", time.Now().Format("20060102_150405")) 109 | } 110 | utils.WriteOutput(csvData, csvData, outputFileName) 111 | utils.LogInfo(fmt.Sprintf("%d permissions exported", len(csvData)-1), true) 112 | } else { 113 | utils.LogInfo("no permissions in PCE.", true) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /cmd/portusage/results.go: -------------------------------------------------------------------------------- 1 | package portusage 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | "github.com/brian1917/workloader/utils" 9 | ) 10 | 11 | func getResults(file string) { 12 | 13 | // parse the input csv 14 | csvData, err := utils.ParseCSV(file) 15 | if err != nil { 16 | utils.LogError(err.Error()) 17 | } 18 | 19 | // Find the async_query_href and the status header 20 | var asyncHrefCol, asyncQueryStatusCol, flowsCol int 21 | for i, col := range csvData[0] { 22 | if col == "async_query_href" { 23 | asyncHrefCol = i 24 | } 25 | if col == "async_query_status" { 26 | asyncQueryStatusCol = i 27 | } 28 | if col == "flows" { 29 | flowsCol = i 30 | } 31 | } 32 | 33 | // Get all pending explorer queries 34 | utils.LogInfo("getting all async queries", true) 35 | asyncQueries, api, err := pce.GetAsyncQueries(nil) 36 | utils.LogAPIRespV2("GetAsyncQueries", api) 37 | if err != nil { 38 | utils.LogError(err.Error()) 39 | } 40 | utils.LogInfo(fmt.Sprintf("%d total async queries for this user", len(asyncQueries)), true) 41 | 42 | // Create the asyncQueries map 43 | asyncHrefMap := make(map[string]illumioapi.AsyncTrafficQuery) 44 | for _, aq := range asyncQueries { 45 | asyncHrefMap[aq.Href] = aq 46 | } 47 | 48 | // Iterate through the csv and check for reesults 49 | newCsvData := [][]string{} 50 | var numStillPending, numAlreadyCompleted, numNewlyCompleted, numExpired int 51 | for i, row := range csvData { 52 | // Create thew new CSV data 53 | newCsvData = append(newCsvData, row) 54 | // Skip the first row 55 | if i == 0 { 56 | continue 57 | } 58 | if row[asyncQueryStatusCol] == "completed" { 59 | utils.LogInfo(fmt.Sprintf("csv row %d - %s already completed from previous run", i+1, row[asyncHrefCol]), true) 60 | numAlreadyCompleted++ 61 | continue 62 | } 63 | // Get the async query 64 | var aq illumioapi.AsyncTrafficQuery 65 | var exists bool 66 | if aq, exists = asyncHrefMap[row[asyncHrefCol]]; !exists { 67 | utils.LogWarning(fmt.Sprintf("csv row %d - %s does not exist as an async query. invalid href or it expired.", i+1, row[asyncHrefCol]), true) 68 | numExpired++ 69 | continue 70 | } 71 | if aq.Status != "completed" { 72 | utils.LogInfo(fmt.Sprintf("csv row %d - %s is not completed.", i+1, aq.Href), true) 73 | numStillPending++ 74 | continue 75 | } 76 | 77 | traffic, api, err := pce.GetAsyncQueryResults(aq) 78 | utils.LogAPIRespV2("GetResults", api) 79 | if err != nil { 80 | utils.LogError(err.Error()) 81 | } 82 | // Edit the csv 83 | newCsvData[len(newCsvData)-1][flowsCol] = strconv.Itoa(len(traffic)) 84 | newCsvData[len(newCsvData)-1][asyncQueryStatusCol] = "completed" 85 | utils.LogInfo(fmt.Sprintf("csv row %d - %s completed and downloaded", i+1, aq.Href), true) 86 | numNewlyCompleted++ 87 | 88 | } 89 | 90 | // Summarize 91 | utils.LogInfo(fmt.Sprintf("%d traffic queries in input.", len(csvData)-1), true) 92 | utils.LogInfo(fmt.Sprintf("%d traffic queries completed prior to this run.", numAlreadyCompleted), true) 93 | utils.LogInfo(fmt.Sprintf("%d traffic queries completed on this run.", numNewlyCompleted), true) 94 | utils.LogInfo(fmt.Sprintf("%d traffic queries expired (see warnings).", numExpired), true) 95 | utils.LogInfo(fmt.Sprintf("%d traffic queries still pending.", numStillPending), true) 96 | utils.WriteOutput(newCsvData, [][]string{}, file) 97 | } 98 | -------------------------------------------------------------------------------- /cmd/ruleexport/headers.go: -------------------------------------------------------------------------------- 1 | package ruleexport 2 | 3 | // Constants for header values in ruleexport and ruleimport 4 | const ( 5 | HeaderRulesetName = "ruleset_name" 6 | HeaderRuleSetScope = "ruleset_scope" 7 | HeaderRulesetEnabled = "ruleset_enabled" 8 | HeaderRuleType = "rule_type" 9 | HeaderRuleDescription = "rule_description" 10 | HeaderRuleEnabled = "rule_enabled" 11 | HeaderUnscopedConsumers = "unscoped_consumers" 12 | HeaderSrcAllWorkloads = "src_all_workloads" 13 | HeaderSrcLabels = "src_labels" 14 | HeaderSrcLabelsExclusions = "src_labels_exclusions" 15 | HeaderSrcLabelGroup = "src_label_groups" 16 | HeaderSrcLabelGroupExclusions = "src_label_groups_exclusions" 17 | HeaderSrcIplists = "src_iplists" 18 | HeaderSrcUserGroups = "src_user_groups" 19 | HeaderSrcWorkloads = "src_workloads" 20 | HeaderSrcVirtualServices = "src_virtual_services" 21 | HeaderSrcUseWorkloadSubnets = "src_use_workload_subnets" 22 | HeaderDstAllWorkloads = "dst_all_workloads" 23 | HeaderDstLabels = "dst_labels" 24 | HeaderDstLabelsExclusions = "dst_labels_exclusions" 25 | HeaderDstLabelGroups = "dst_label_groups" 26 | HeaderDstLabelGroupsExclusions = "dst_label_groups_exclusions" 27 | HeaderDstIplists = "dst_iplists" 28 | HeaderDstWorkloads = "dst_workloads" 29 | HeaderDstVirtualServices = "dst_virtual_services" 30 | HeaderDstVirtualServers = "dst_virtual_servers" 31 | HeaderDstUseWorkloadSubnets = "dst_use_workload_subnets" 32 | HeaderServices = "services" 33 | HeaderSrcResolveLabelsAs = "src_resolve_labels_as" 34 | HeaderDstResolveLabelsAs = "dst_resolve_labels_as" 35 | HeaderMachineAuthEnabled = "machine_auth_enabled" 36 | HeaderSecureConnectEnabled = "secure_connect_enabled" 37 | HeaderStateless = "stateless_enabled" 38 | HeaderRulesetDescription = "ruleset_description" 39 | HeaderRulesetContainsCustomIptables = "ruleset_contains_custom_iptables" 40 | HeaderRulesetHref = "ruleset_href" 41 | HeaderRuleHref = "rule_href" 42 | HeaderUpdateType = "update_type" 43 | HeaderNetworkType = "network_type" 44 | HeaderExternalDataSet = "external_data_set" 45 | HeaderExternalDataReference = "external_data_reference" 46 | ) 47 | 48 | func getCSVHeaders(templateFormat bool) []string { 49 | headers := []string{ 50 | HeaderRulesetName, 51 | HeaderRulesetDescription, 52 | HeaderRuleSetScope, 53 | HeaderRulesetEnabled, 54 | HeaderRuleType, 55 | HeaderRuleDescription, 56 | HeaderRuleEnabled, 57 | HeaderUnscopedConsumers, 58 | HeaderSrcAllWorkloads, 59 | HeaderSrcLabels, 60 | HeaderSrcLabelsExclusions, 61 | HeaderSrcLabelGroup, 62 | HeaderSrcLabelGroupExclusions, 63 | HeaderSrcIplists, 64 | HeaderSrcUserGroups, 65 | HeaderSrcWorkloads, 66 | HeaderSrcVirtualServices, 67 | HeaderSrcUseWorkloadSubnets, 68 | HeaderDstAllWorkloads, 69 | HeaderDstLabels, 70 | HeaderDstLabelsExclusions, 71 | HeaderDstLabelGroups, 72 | HeaderDstLabelGroupsExclusions, 73 | HeaderDstIplists, 74 | HeaderDstWorkloads, 75 | HeaderDstVirtualServices, 76 | HeaderDstVirtualServers, 77 | HeaderDstUseWorkloadSubnets, 78 | HeaderServices, 79 | HeaderSrcResolveLabelsAs, 80 | HeaderDstResolveLabelsAs, 81 | HeaderMachineAuthEnabled, 82 | HeaderSecureConnectEnabled, 83 | HeaderStateless, 84 | HeaderNetworkType, 85 | HeaderExternalDataSet, 86 | HeaderExternalDataReference} 87 | 88 | if !templateFormat { 89 | headers = append(headers, HeaderRulesetHref, HeaderRuleHref, HeaderUpdateType) 90 | } 91 | 92 | return headers 93 | } 94 | 95 | func createEntrySlice(csvEntryMap map[string]string, templateFormat bool, useSubnets bool) []string { 96 | entry := []string{} 97 | for _, h := range getCSVHeaders(templateFormat) { 98 | if !useSubnets && (h == HeaderSrcUseWorkloadSubnets || h == HeaderDstUseWorkloadSubnets) { 99 | continue 100 | } 101 | entry = append(entry, csvEntryMap[h]) 102 | } 103 | return entry 104 | } 105 | -------------------------------------------------------------------------------- /cmd/ruleimport/headers.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/brian1917/workloader/cmd/ruleexport" 8 | "github.com/brian1917/workloader/utils" 9 | ) 10 | 11 | func (i *Input) processHeaders(headers []string) { 12 | 13 | i.Headers = make(map[string]int) 14 | 15 | headerWarningGiven := false 16 | for e, h := range headers { 17 | if strings.Contains(h, "consumer_") { 18 | if !headerWarningGiven { 19 | utils.LogWarning("deprecation - headers are using legacy terminology of consumer and provider. switch to src and dst. see help menu for accceptable headers. processing will continue.", true) 20 | headerWarningGiven = true 21 | } 22 | h = strings.Replace(h, "consumer_", "src_", -1) 23 | } 24 | if strings.Contains(h, "provider_") { 25 | if !headerWarningGiven { 26 | utils.LogWarning("deprecation - headers are using legacy terminology of consumer and provider. switch to src and dst. see help menu for accceptable headers. processing will continue.", true) 27 | headerWarningGiven = true 28 | } 29 | h = strings.Replace(h, "provider_", "dst_", -1) 30 | } 31 | i.Headers[h] = e 32 | } 33 | 34 | // Convert the first row into a map 35 | headerMap := make(map[string]int) 36 | for i, header := range headers { 37 | if strings.Contains(header, "consumer_") { 38 | if !headerWarningGiven { 39 | utils.LogWarning("deprecation - headers are using legacy terminology of consumer and provider. switch to src and dst. see help menu for accceptable headers. processing will continue.", true) 40 | headerWarningGiven = true 41 | } 42 | header = strings.Replace(header, "consumer_", "src_", -1) 43 | } 44 | if strings.Contains(header, "provider_") { 45 | if !headerWarningGiven { 46 | utils.LogWarning("deprecation - headers are using legacy terminology of consumer and provider. switch to src and dst. see help menu for accceptable headers. processing will continue.", true) 47 | headerWarningGiven = true 48 | } 49 | header = strings.Replace(header, "provider_", "dst_", -1) 50 | } 51 | headerMap[header] = i 52 | } 53 | 54 | // Check for required headers 55 | requiredHeaders := []string{ 56 | ruleexport.HeaderServices, 57 | ruleexport.HeaderUnscopedConsumers, 58 | ruleexport.HeaderRulesetName, 59 | ruleexport.HeaderRuleEnabled, 60 | ruleexport.HeaderDstResolveLabelsAs, 61 | ruleexport.HeaderSrcResolveLabelsAs} 62 | 63 | for _, rh := range requiredHeaders { 64 | if _, ok := i.Headers[rh]; !ok { 65 | utils.LogError(fmt.Sprintf("No header for found for required field: %s", rh)) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cmd/ruleimport/iplist.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func IplComparison(csvIPLNames []string, rule illumioapi.Rule, pceIPLMap map[string]illumioapi.IPList, csvLine int, provider bool) (bool, []illumioapi.IPList) { 11 | 12 | // Build a map of the existing IP Lists 13 | ruleIPLsNameMap := make(map[string]illumioapi.IPList) 14 | connectionSide := "consumer" 15 | if provider { 16 | connectionSide = "provider" 17 | for _, provider := range illumioapi.PtrToVal(rule.Providers) { 18 | if provider.IPList != nil { 19 | ruleIPLsNameMap[pceIPLMap[provider.IPList.Href].Name] = pceIPLMap[provider.IPList.Href] 20 | } 21 | } 22 | } else { 23 | for _, consumer := range illumioapi.PtrToVal(rule.Consumers) { 24 | if consumer.IPList != nil { 25 | ruleIPLsNameMap[pceIPLMap[consumer.IPList.Href].Name] = pceIPLMap[consumer.IPList.Href] 26 | } 27 | } 28 | } 29 | 30 | // Build a map of the CSV provided IP Lists 31 | csvIPLsNameMap := make(map[string]illumioapi.IPList) 32 | for _, iplName := range csvIPLNames { 33 | if iplName != "" { 34 | if ipl, iplCheck := pceIPLMap[iplName]; iplCheck { 35 | csvIPLsNameMap[ipl.Name] = ipl 36 | } else { 37 | utils.LogError(fmt.Sprintf("CSV line %d - %s %s does not exist as an IP List", csvLine, connectionSide, iplName)) 38 | } 39 | } 40 | } 41 | 42 | // Set our iplChange to false 43 | change := false 44 | if rule.Href != "" { 45 | 46 | // Check for IP Lists in CSV that are not in the PCE 47 | for _, csvIPL := range csvIPLsNameMap { 48 | if _, check := ruleIPLsNameMap[csvIPL.Name]; !check { 49 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s IP List in the CSV but is not in the rule. It will be added.", csvLine, csvIPL.Name, connectionSide), false) 50 | change = true 51 | } 52 | } 53 | 54 | // Check for IP Lists in the PCE that are not in the CSV 55 | for _, existingRuleIPL := range ruleIPLsNameMap { 56 | if _, check := csvIPLsNameMap[existingRuleIPL.Name]; !check { 57 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s IP List in the rule but is not in the CSV. It will be removed.", csvLine, existingRuleIPL.Name, connectionSide), false) 58 | change = true 59 | } 60 | } 61 | } 62 | 63 | returnedIPLs := []illumioapi.IPList{} 64 | if change || rule.Href == "" { 65 | for _, ipl := range csvIPLsNameMap { 66 | returnedIPLs = append(returnedIPLs, illumioapi.IPList{Href: ipl.Href}) 67 | } 68 | } else { 69 | for _, ipl := range ruleIPLsNameMap { 70 | returnedIPLs = append(returnedIPLs, illumioapi.IPList{Href: ipl.Href}) 71 | } 72 | } 73 | 74 | return change, returnedIPLs 75 | } 76 | -------------------------------------------------------------------------------- /cmd/ruleimport/label.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func LabelComparison(csvLabels []illumioapi.Label, exclusion bool, pce illumioapi.PCE, rule illumioapi.Rule, csvLine int, provider bool) (bool, []illumioapi.Label) { 11 | 12 | // Build a map of the existing labels 13 | ruleLabelMap := make(map[string]illumioapi.Label) 14 | connectionSide := "consumer" 15 | if provider { 16 | connectionSide = "provider" 17 | for _, provider := range illumioapi.PtrToVal(rule.Providers) { 18 | if provider.Label != nil && exclusion == illumioapi.PtrToVal(provider.Exclusion) { 19 | ruleLabelMap[pce.Labels[provider.Label.Href].Key+pce.Labels[provider.Label.Href].Value] = pce.Labels[provider.Label.Href] 20 | } 21 | } 22 | } else { 23 | for _, consumer := range illumioapi.PtrToVal(rule.Consumers) { 24 | if consumer.Label != nil && exclusion == illumioapi.PtrToVal(consumer.Exclusion) { 25 | ruleLabelMap[pce.Labels[consumer.Label.Href].Key+pce.Labels[consumer.Label.Href].Value] = pce.Labels[consumer.Label.Href] 26 | } 27 | } 28 | } 29 | 30 | // Build a map of the CSV provided labels 31 | csvLabelMap := make(map[string]illumioapi.Label) 32 | for _, label := range csvLabels { 33 | if pceLabel, labelExists := pce.Labels[label.Key+label.Value]; labelExists { 34 | csvLabelMap[label.Key+label.Value] = pceLabel 35 | } else if globalInput.CreateLabels { 36 | if globalInput.UpdatePCE { 37 | createdLabel, a, err := pce.CreateLabel(illumioapi.Label{Key: label.Key, Value: label.Value}) 38 | utils.LogAPIRespV2("CreateLabel", a) 39 | if err != nil { 40 | utils.LogError(fmt.Sprintf("csv line %d - creating label - %s", csvLine, err.Error())) 41 | } 42 | csvLabelMap[label.Key+label.Value] = createdLabel 43 | pce.Labels[label.Href] = createdLabel 44 | pce.Labels[label.Key+label.Value] = createdLabel 45 | utils.LogInfo(fmt.Sprintf("csv line %d - %s does not exist as a %s label. created %d", csvLine, label.Value, label.Key, a.StatusCode), true) 46 | } else { 47 | utils.LogInfo(fmt.Sprintf("csv line %d - %s does not exist as a %s label. will be created with update-pce", csvLine, label.Value, label.Key), true) 48 | } 49 | } else { 50 | utils.LogError(fmt.Sprintf("csv line %d - %s %s does not exist as a %s label", csvLine, connectionSide, label.Value, label.Key)) 51 | } 52 | } 53 | 54 | // Set change to false 55 | change := false 56 | if rule.Href != "" { 57 | 58 | // Check for label in CSV that are not in the PCE 59 | for _, csvLabel := range csvLabelMap { 60 | if _, check := ruleLabelMap[csvLabel.Key+csvLabel.Value]; !check { 61 | utils.LogInfo(fmt.Sprintf("csv line %d - %s is a %s %s label in the CSV but is not in the rule. It will be added.", csvLine, csvLabel.Value, connectionSide, csvLabel.Key), false) 62 | change = true 63 | } 64 | } 65 | 66 | // Check for labels in the PCE that are not in the CSV 67 | for _, existingRuleLabel := range ruleLabelMap { 68 | if _, check := csvLabelMap[existingRuleLabel.Key+existingRuleLabel.Value]; !check { 69 | utils.LogInfo(fmt.Sprintf("csv line %d - %s is a %s %s label in the rule but is not in the CSV. It will be removed.", csvLine, existingRuleLabel.Value, connectionSide, existingRuleLabel.Key), false) 70 | change = true 71 | } 72 | } 73 | } 74 | 75 | // Build out the returned labels 76 | returnedLabels := []illumioapi.Label{} 77 | if change || rule.Href == "" { 78 | for _, label := range csvLabelMap { 79 | returnedLabels = append(returnedLabels, illumioapi.Label{Href: label.Href}) 80 | } 81 | } else { 82 | for _, label := range ruleLabelMap { 83 | returnedLabels = append(returnedLabels, illumioapi.Label{Href: label.Href}) 84 | } 85 | } 86 | 87 | return change, returnedLabels 88 | } 89 | -------------------------------------------------------------------------------- /cmd/ruleimport/labelgroup.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func LabelGroupComparison(csvLGNames []string, exclusion bool, rule illumioapi.Rule, pceLGMap map[string]illumioapi.LabelGroup, csvLine int, provider bool) (bool, []illumioapi.LabelGroup) { 11 | 12 | // Build a map of the existing Label Groups 13 | ruleLGsNameMap := make(map[string]illumioapi.LabelGroup) 14 | connectionSide := "consumer" 15 | if provider { 16 | connectionSide = "provider" 17 | for _, provider := range illumioapi.PtrToVal(rule.Providers) { 18 | if provider.LabelGroup != nil && exclusion == illumioapi.PtrToVal(provider.Exclusion) { 19 | ruleLGsNameMap[pceLGMap[provider.LabelGroup.Href].Name] = pceLGMap[provider.LabelGroup.Href] 20 | } 21 | } 22 | } else { 23 | for _, consumer := range illumioapi.PtrToVal(rule.Consumers) { 24 | if consumer.LabelGroup != nil && exclusion == illumioapi.PtrToVal(consumer.Exclusion) { 25 | ruleLGsNameMap[pceLGMap[consumer.LabelGroup.Href].Name] = pceLGMap[consumer.LabelGroup.Href] 26 | } 27 | } 28 | } 29 | 30 | // Build a map of the CSV provided Label Groups 31 | csvLGsNameMap := make(map[string]illumioapi.LabelGroup) 32 | for _, lgName := range csvLGNames { 33 | if lgName != "" { 34 | if lg, lgCheck := pceLGMap[lgName]; lgCheck { 35 | csvLGsNameMap[lg.Name] = lg 36 | } else { 37 | utils.LogError(fmt.Sprintf("CSV line %d - %s %s does not exist as an label group", csvLine, connectionSide, lgName)) 38 | } 39 | } 40 | } 41 | 42 | // Set our change to false 43 | change := false 44 | if rule.Href != "" { 45 | 46 | // Check for Label Groups in CSV that are not in the PCE 47 | for _, csvLG := range csvLGsNameMap { 48 | if _, check := ruleLGsNameMap[csvLG.Name]; !check { 49 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s label group in the CSV but is not in the rule. It will be added.", csvLine, csvLG.Name, connectionSide), false) 50 | change = true 51 | } 52 | } 53 | 54 | // Check for Label Groups in the PCE that are not in the CSV 55 | for _, existingRuleLG := range ruleLGsNameMap { 56 | if _, check := csvLGsNameMap[existingRuleLG.Name]; !check { 57 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s label group in the rule but is not in the CSV. It will be removed.", csvLine, existingRuleLG.Name, connectionSide), false) 58 | change = true 59 | } 60 | } 61 | } 62 | 63 | returnedLGs := []illumioapi.LabelGroup{} 64 | if change || rule.Href == "" { 65 | for _, lg := range csvLGsNameMap { 66 | returnedLGs = append(returnedLGs, illumioapi.LabelGroup{Href: lg.Href}) 67 | } 68 | } else { 69 | for _, lg := range ruleLGsNameMap { 70 | returnedLGs = append(returnedLGs, illumioapi.LabelGroup{Href: lg.Href}) 71 | } 72 | } 73 | 74 | return change, returnedLGs 75 | } 76 | -------------------------------------------------------------------------------- /cmd/ruleimport/service.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/brian1917/workloader/utils" 9 | 10 | "github.com/brian1917/illumioapi/v2" 11 | ) 12 | 13 | func ServiceComparison(csvServices []string, rule illumioapi.Rule, pceServiceMap map[string]illumioapi.Service, csvLine int) (bool, []illumioapi.IngressServices) { 14 | 15 | // The key in the maps is name, protocol, from, to all concatenated together 16 | csvServiceEntries := make(map[string]illumioapi.IngressServices) 17 | ruleServiceEntries := make(map[string]illumioapi.IngressServices) 18 | 19 | // Process the CSV provided services 20 | for _, c := range csvServices { 21 | 22 | // Check if last three letters are TCP or UDP 23 | if _, err := strconv.Atoi(string(c[0])); err == nil && (strings.ToLower(c[len(c)-3:]) == "tcp" || strings.ToLower(c[len(c)-3:]) == "udp") && strings.Count(c, " ") == 1 { 24 | protocol, port, toPort, err := parseCSVPortEntry(c) 25 | if err != nil { 26 | utils.LogError(err.Error()) 27 | } 28 | 29 | // Add to our slice 30 | proto := 6 31 | if protocol == "udp" { 32 | proto = 17 33 | } 34 | toPortPtr := &toPort 35 | if toPort == 0 { 36 | toPortPtr = nil 37 | } 38 | csvServiceEntries[fmt.Sprintf("%s-%d-%d", protocol, port, toPort)] = illumioapi.IngressServices{Protocol: &proto, Port: &port, ToPort: toPortPtr} 39 | 40 | // Check if it's a service 41 | } else if service, exists := pceServiceMap[c]; exists { 42 | // Add to our slice 43 | csvServiceEntries[pceServiceMap[service.Href].Name] = illumioapi.IngressServices{Href: service.Href} 44 | } else { 45 | utils.LogError(fmt.Sprintf("CSV line %d - %s does not exist as a service", csvLine, c)) 46 | } 47 | } 48 | 49 | // Process the rule provided ingress services 50 | if rule.IngressServices != nil { 51 | for _, ruleService := range illumioapi.PtrToVal(rule.IngressServices) { 52 | // Port range here 53 | if ruleService.Href == "" { 54 | protocol := "tcp" 55 | if *ruleService.Protocol == 17 { 56 | protocol = "udp" 57 | } 58 | var port, toPort int 59 | if ruleService.Port != nil { 60 | port = *ruleService.Port 61 | } 62 | if ruleService.ToPort != nil { 63 | toPort = *ruleService.ToPort 64 | } 65 | ruleServiceEntries[fmt.Sprintf("%s-%d-%d", protocol, port, toPort)] = ruleService 66 | } else { 67 | ruleServiceEntries[pceServiceMap[ruleService.Href].Name] = ruleService 68 | } 69 | } 70 | } 71 | 72 | // Initalize the change 73 | change := false 74 | 75 | // Check to see if what's in the CSV is in the PCE rule 76 | for s := range csvServiceEntries { 77 | if _, check := ruleServiceEntries[s]; !check && rule.Href != "" { 78 | utils.LogInfo(fmt.Sprintf("csv line %d - %s is a service in the csv rule but is not in the pce rule. it will be added.", csvLine, s), false) 79 | change = true 80 | } 81 | } 82 | 83 | // Check to see if what's in the PCE rule is in the CSV 84 | for s := range ruleServiceEntries { 85 | if _, check := csvServiceEntries[s]; !check && rule.Href != "" { 86 | utils.LogInfo(fmt.Sprintf("csv line %d - %s is a service in the pce rule but is not in the csv rule. it will be removed.", csvLine, s), false) 87 | change = true 88 | } 89 | } 90 | 91 | returnServices := []illumioapi.IngressServices{} 92 | if change || rule.Href == "" { 93 | for _, s := range csvServiceEntries { 94 | returnServices = append(returnServices, illumioapi.IngressServices{Port: s.Port, ToPort: s.ToPort, Href: s.Href, Protocol: s.Protocol}) 95 | } 96 | return true, returnServices 97 | } 98 | return false, *rule.IngressServices 99 | } 100 | 101 | func parseCSVPortEntry(entry string) (protocol string, port int, toPort int, err error) { 102 | 103 | // Get the protocol 104 | protocol = strings.ToLower(entry[len(entry)-3:]) 105 | 106 | // Remove the spaces 107 | entry = strings.Replace(entry, " ", "", -1) 108 | 109 | // Split the ports on the dash 110 | s := strings.Split(entry, "-") 111 | if len(s) == 1 { 112 | port, err := strconv.Atoi(s[0][:len(s[0])-3]) 113 | return protocol, port, toPort, err 114 | } 115 | port, err = strconv.Atoi(s[0]) 116 | if err != nil { 117 | return protocol, port, toPort, err 118 | } 119 | toPort, err = strconv.Atoi(s[1][:len(s[1])-3]) 120 | 121 | return protocol, port, toPort, err 122 | } 123 | -------------------------------------------------------------------------------- /cmd/ruleimport/usergroup.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func userGroupComaprison(csvUserGroupNames []string, rule illumioapi.Rule, userGroupMapName map[string]illumioapi.ConsumingSecurityPrincipals, csvLine int) (bool, []illumioapi.ConsumingSecurityPrincipals) { 11 | 12 | // Build a map of the existing user groups 13 | ruleUserGroupsNameMap := make(map[string]illumioapi.ConsumingSecurityPrincipals) 14 | for _, ug := range illumioapi.PtrToVal(rule.ConsumingSecurityPrincipals) { 15 | ruleUserGroupsNameMap[userGroupMapName[ug.Href].Name] = userGroupMapName[ug.Href] 16 | } 17 | 18 | // Build a map of the CSV provided user groups 19 | csvUserGroupsNameMap := make(map[string]illumioapi.ConsumingSecurityPrincipals) 20 | for _, ugName := range csvUserGroupNames { 21 | if ug, ugCheck := userGroupMapName[ugName]; ugCheck { 22 | csvUserGroupsNameMap[ug.Name] = ug 23 | } else { 24 | utils.LogError(fmt.Sprintf("CSV line %d - %s does not exist as a user group", csvLine, ugName)) 25 | } 26 | } 27 | 28 | // Set change to false 29 | change := false 30 | if rule.Href != "" { 31 | 32 | // Check for user groups in CSV that are not in the PCE 33 | for _, csvUG := range csvUserGroupsNameMap { 34 | if _, check := ruleUserGroupsNameMap[csvUG.Name]; !check { 35 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a user group in the CSV but is not in the rule. It will be added.", csvLine, csvUG.Name), false) 36 | change = true 37 | } 38 | } 39 | 40 | // Check for user groups in the PCE that are not in the rule 41 | for _, ruleUG := range ruleUserGroupsNameMap { 42 | if _, check := csvUserGroupsNameMap[ruleUG.Name]; !check { 43 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a user group in the rule but is not in the CSV. It will be removed.", csvLine, ruleUG.Name), false) 44 | change = true 45 | } 46 | } 47 | } 48 | 49 | // Create the right consumingSecPrincipals 50 | consumingSecPrincipals := []illumioapi.ConsumingSecurityPrincipals{} 51 | if change || rule.Href == "" { 52 | for _, ug := range csvUserGroupsNameMap { 53 | consumingSecPrincipals = append(consumingSecPrincipals, illumioapi.ConsumingSecurityPrincipals{Href: ug.Href}) 54 | } 55 | } else { 56 | for _, cp := range illumioapi.PtrToVal(rule.ConsumingSecurityPrincipals) { 57 | consumingSecPrincipals = append(consumingSecPrincipals, illumioapi.ConsumingSecurityPrincipals{Href: cp.Href}) 58 | } 59 | } 60 | return change, consumingSecPrincipals 61 | } 62 | -------------------------------------------------------------------------------- /cmd/ruleimport/virtualserver.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | // import ( 4 | // "fmt" 5 | 6 | // "github.com/brian1917/illumioapi" 7 | // "github.com/brian1917/workloader/utils" 8 | // ) 9 | 10 | // func virtualServerCompare(csvVirtualServerNames []string, rule illumioapi.Rule, pceVirtualServerMap map[string]illumioapi.VirtualServer, csvLine int, provider bool) (bool, []*illumioapi.VirtualServer) { 11 | 12 | // // Build a map of the existing Virtual Services 13 | // ruleVirtualServerNameMap := make(map[string]illumioapi.VirtualServer) 14 | // connectionSide := "consumer" 15 | // if provider { 16 | // connectionSide = "provider" 17 | // for _, provider := range rule.Providers { 18 | // if provider.VirtualServer != nil { 19 | // ruleVirtualServerNameMap[pceVirtualServerMap[provider.VirtualServer.Href].Name] = pceVirtualServerMap[provider.VirtualServer.Href] 20 | // } 21 | // } 22 | // } 23 | 24 | // // Build a map of the CSV provided Virtual Services 25 | // csvVirtualServerNameMap := make(map[string]illumioapi.VirtualServer) 26 | // for _, vsName := range csvVirtualServerNames { 27 | // if vsName != "" { 28 | // if vs, vsCheck := pceVirtualServerMap[vsName]; vsCheck { 29 | // csvVirtualServerNameMap[vs.Name] = vs 30 | // } else { 31 | // utils.LogError(fmt.Sprintf("CSV line %d - %s %s does not exist as a virtual server", csvLine, connectionSide, vsName)) 32 | // } 33 | // } 34 | // } 35 | 36 | // // Set our change to false 37 | // change := false 38 | // if rule.Href != "" { 39 | 40 | // // Check for Virtual Services in CSV that are not in the PCE 41 | // for _, csvVirtualServer := range csvVirtualServerNameMap { 42 | // if _, check := ruleVirtualServerNameMap[csvVirtualServer.Name]; !check { 43 | // utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s virtual server in the CSV but is not in the rule. It will be added.", csvLine, csvVirtualServer.Name, connectionSide), false) 44 | // change = true 45 | // } 46 | // } 47 | 48 | // // Check for Virtual Services in the PCE that are not in the CSV 49 | // for _, existingRuleVS := range ruleVirtualServerNameMap { 50 | // if _, check := csvVirtualServerNameMap[existingRuleVS.Name]; !check { 51 | // utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s virtual server in the rule but is not in the CSV. It will be removed.", csvLine, existingRuleVS.Name, connectionSide), false) 52 | // change = true 53 | // } 54 | // } 55 | // } 56 | 57 | // returnedVirtualServers := []*illumioapi.VirtualServer{} 58 | // if change || rule.Href == "" { 59 | // for _, vs := range csvVirtualServerNameMap { 60 | // returnedVirtualServers = append(returnedVirtualServers, &illumioapi.VirtualServer{Href: vs.Href}) 61 | // } 62 | // } else { 63 | // for _, vs := range ruleVirtualServerNameMap { 64 | // returnedVirtualServers = append(returnedVirtualServers, &illumioapi.VirtualServer{Href: vs.Href}) 65 | // } 66 | // } 67 | 68 | // return change, returnedVirtualServers 69 | // } 70 | -------------------------------------------------------------------------------- /cmd/ruleimport/virutalservice.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func virtualServiceCompare(csvVSNames []string, rule illumioapi.Rule, pceVSMap map[string]illumioapi.VirtualService, csvLine int, provider bool) (bool, []illumioapi.VirtualService) { 11 | 12 | // Build a map of the existing Virtual Services 13 | ruleVirtualServicesNameMap := make(map[string]illumioapi.VirtualService) 14 | connectionSide := "consumer" 15 | if provider { 16 | connectionSide = "provider" 17 | for _, provider := range illumioapi.PtrToVal(rule.Providers) { 18 | if provider.VirtualService != nil { 19 | ruleVirtualServicesNameMap[pceVSMap[provider.VirtualService.Href].Name] = pceVSMap[provider.VirtualService.Href] 20 | } 21 | } 22 | } else { 23 | for _, consumer := range illumioapi.PtrToVal(rule.Consumers) { 24 | if consumer.VirtualService != nil { 25 | ruleVirtualServicesNameMap[pceVSMap[consumer.VirtualService.Href].Name] = pceVSMap[consumer.VirtualService.Href] 26 | } 27 | } 28 | } 29 | 30 | // Build a map of the CSV provided Virtual Services 31 | csvVirtualServicesNameMap := make(map[string]illumioapi.VirtualService) 32 | for _, vsName := range csvVSNames { 33 | if vsName != "" { 34 | if vs, vsCheck := pceVSMap[vsName]; vsCheck { 35 | csvVirtualServicesNameMap[vs.Name] = vs 36 | } else { 37 | utils.LogError(fmt.Sprintf("CSV line %d - %s %s does not exist as a virtual service", csvLine, connectionSide, vsName)) 38 | } 39 | } 40 | } 41 | 42 | // Set our change to false 43 | change := false 44 | if rule.Href != "" { 45 | 46 | // Check for Virtual Services in CSV that are not in the PCE 47 | for _, csvVirtualService := range csvVirtualServicesNameMap { 48 | if _, check := ruleVirtualServicesNameMap[csvVirtualService.Name]; !check { 49 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s virtual service in the CSV but is not in the rule. It will be added.", csvLine, csvVirtualService.Name, connectionSide), false) 50 | change = true 51 | } 52 | } 53 | 54 | // Check for Virtual Services in the PCE that are not in the CSV 55 | for _, existingRuleVS := range ruleVirtualServicesNameMap { 56 | if _, check := csvVirtualServicesNameMap[existingRuleVS.Name]; !check { 57 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s virtual service in the rule but is not in the CSV. It will be removed.", csvLine, existingRuleVS.Name, connectionSide), false) 58 | change = true 59 | } 60 | } 61 | } 62 | 63 | returnedVirtualServices := []illumioapi.VirtualService{} 64 | if change || rule.Href == "" { 65 | for _, vs := range csvVirtualServicesNameMap { 66 | returnedVirtualServices = append(returnedVirtualServices, illumioapi.VirtualService{Href: vs.Href}) 67 | } 68 | } else { 69 | for _, vs := range ruleVirtualServicesNameMap { 70 | returnedVirtualServices = append(returnedVirtualServices, illumioapi.VirtualService{Href: vs.Href}) 71 | } 72 | } 73 | 74 | return change, returnedVirtualServices 75 | } 76 | -------------------------------------------------------------------------------- /cmd/ruleimport/workload.go: -------------------------------------------------------------------------------- 1 | package ruleimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | ) 9 | 10 | func wkldComparison(csvWkldNames []string, rule illumioapi.Rule, pceWkldMap map[string]illumioapi.Workload, csvLine int, provider bool) (bool, []illumioapi.Workload) { 11 | 12 | // Build a map of the existing Workloads 13 | ruleWkldsNameMap := make(map[string]illumioapi.Workload) 14 | connectionSide := "consumer" 15 | if provider { 16 | connectionSide = "provider" 17 | for _, provider := range illumioapi.PtrToVal(rule.Providers) { 18 | if provider.Workload != nil { 19 | ruleWkldsNameMap[illumioapi.PtrToVal(pceWkldMap[provider.Workload.Href].Name)] = pceWkldMap[provider.Workload.Href] 20 | } 21 | } 22 | } else { 23 | for _, consumer := range illumioapi.PtrToVal(rule.Consumers) { 24 | if consumer.Workload != nil { 25 | ruleWkldsNameMap[illumioapi.PtrToVal(pceWkldMap[consumer.Workload.Href].Name)] = pceWkldMap[consumer.Workload.Href] 26 | } 27 | } 28 | } 29 | 30 | // Build a map of the CSV provided Workloads 31 | csvWkldsNameMap := make(map[string]illumioapi.Workload) 32 | for _, wkldName := range csvWkldNames { 33 | if wkldName != "" { 34 | if wkld, wkldCheck := pceWkldMap[wkldName]; wkldCheck { 35 | csvWkldsNameMap[illumioapi.PtrToVal(wkld.Name)] = wkld 36 | } else { 37 | utils.LogError(fmt.Sprintf("CSV line %d - %s %s does not exist as a workload", csvLine, connectionSide, wkldName)) 38 | } 39 | } 40 | } 41 | 42 | // Set our wkldChange to false 43 | change := false 44 | if rule.Href != "" { 45 | 46 | // Check for Workloads in CSV that are not in the PCE 47 | for _, csvWkld := range csvWkldsNameMap { 48 | if _, check := ruleWkldsNameMap[illumioapi.PtrToVal(csvWkld.Name)]; !check { 49 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s workload in the CSV but is not in the rule. It will be added.", csvLine, illumioapi.PtrToVal(csvWkld.Name), connectionSide), false) 50 | change = true 51 | } 52 | } 53 | 54 | // Check for Workloads in the PCE that are not in the CSV 55 | for _, existingRuleWkld := range ruleWkldsNameMap { 56 | if _, check := csvWkldsNameMap[illumioapi.PtrToVal(existingRuleWkld.Name)]; !check { 57 | utils.LogInfo(fmt.Sprintf("CSV line %d - %s is a %s workload in the rule but is not in the CSV. It will be removed.", csvLine, illumioapi.PtrToVal(existingRuleWkld.Name), connectionSide), false) 58 | change = true 59 | } 60 | } 61 | } 62 | 63 | returnedWklds := []illumioapi.Workload{} 64 | if change || rule.Href == "" { 65 | for _, wkld := range csvWkldsNameMap { 66 | returnedWklds = append(returnedWklds, illumioapi.Workload{Href: wkld.Href}) 67 | } 68 | } else { 69 | for _, wkld := range ruleWkldsNameMap { 70 | returnedWklds = append(returnedWklds, illumioapi.Workload{Href: wkld.Href}) 71 | } 72 | } 73 | 74 | return change, returnedWklds 75 | } 76 | -------------------------------------------------------------------------------- /cmd/secprincipalexport/cmd.go: -------------------------------------------------------------------------------- 1 | package secprincipalexport 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | 9 | "github.com/brian1917/workloader/utils" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // Declare local global variables 14 | var outputFileName string 15 | var noHref, groupsOnly bool 16 | 17 | const ( 18 | HeaderHref = "href" 19 | HeaderName = "name" 20 | HeaderDisplayName = "display_name" 21 | HeaderType = "type" 22 | ) 23 | 24 | func init() { 25 | SecPrincipalExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 26 | SecPrincipalExportCmd.Flags().BoolVar(&groupsOnly, "groups-only", false, "only export groups.") 27 | SecPrincipalExportCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 28 | SecPrincipalExportCmd.Flags().SortFlags = false 29 | } 30 | 31 | // SecPrincipalExportCmd runs the label-dimension-export command 32 | var SecPrincipalExportCmd = &cobra.Command{ 33 | Use: "sec-principal-export", 34 | Short: "Create a csv export of all security principals.", 35 | Long: ` 36 | Create a csv export of all security principals. The update-pce and --no-prompt flags are ignored for this command.`, 37 | Run: func(cmd *cobra.Command, args []string) { 38 | 39 | // Get the PCE 40 | pce, err := utils.GetTargetPCEV2(false) 41 | if err != nil { 42 | utils.LogError(err.Error()) 43 | } 44 | 45 | exportSecPrincipals(pce) 46 | }, 47 | } 48 | 49 | func exportSecPrincipals(pce illumioapi.PCE) { 50 | 51 | // Start the data slice with headers 52 | csvData := [][]string{{HeaderName, HeaderDisplayName, HeaderType}} 53 | if !noHref { 54 | csvData[0] = append(csvData[0], HeaderHref) 55 | } 56 | 57 | // Get permissions and auth security principals 58 | apiResps, err := pce.Load(illumioapi.LoadInput{Permissions: true, AuthSecurityPrincipals: true, Labels: true, LabelGroups: true, ProvisionStatus: "active"}, utils.UseMulti()) 59 | utils.LogMultiAPIRespV2(apiResps) 60 | if err != nil { 61 | utils.LogErrorf("loading pce - %s", err) 62 | } 63 | 64 | for _, asp := range pce.AuthSecurityPrincipalsSlices { 65 | // Skip blank group 66 | if asp.Name == "" && asp.DisplayName == "" && asp.Type == "group" { 67 | continue 68 | } 69 | // Skip if only groups 70 | if groupsOnly && asp.Type != "group" { 71 | continue 72 | } 73 | csvRow := make(map[string]string) 74 | csvRow[HeaderHref] = asp.Href 75 | csvRow[HeaderDisplayName] = asp.DisplayName 76 | csvRow[HeaderType] = asp.Type 77 | csvRow[HeaderName] = asp.Name 78 | 79 | // Append 80 | newRow := []string{} 81 | for _, header := range csvData[0] { 82 | newRow = append(newRow, csvRow[header]) 83 | } 84 | csvData = append(csvData, newRow) 85 | 86 | } 87 | 88 | if len(csvData) > 1 { 89 | if outputFileName == "" { 90 | outputFileName = fmt.Sprintf("workloader-sec-principals-export-%s.csv", time.Now().Format("20060102_150405")) 91 | } 92 | utils.WriteOutput(csvData, csvData, outputFileName) 93 | utils.LogInfo(fmt.Sprintf("%d security principals exported", len(csvData)-1), true) 94 | } else { 95 | utils.LogInfo("no security principals in PCE.", true) 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /cmd/secprincipalimport/cmd.go: -------------------------------------------------------------------------------- 1 | package secprincipalimport 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | 10 | "github.com/brian1917/workloader/cmd/secprincipalexport" 11 | "github.com/brian1917/workloader/utils" 12 | "github.com/spf13/cobra" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | func init() {} 17 | 18 | type newSecAuthPrincipal struct { 19 | secAuthPrincipal illumioapi.AuthSecurityPrincipal 20 | csvLine int 21 | } 22 | 23 | // SecPrincipalExportCmd runs the label-dimension-export command 24 | var SecPrincipalImportCmd = &cobra.Command{ 25 | Use: "sec-principal-import", 26 | Short: "Create external users or groups from a csv file.", 27 | Long: ` 28 | Create external users or groups from a csv file. 29 | 30 | The following headers are required: 31 | - display_name 32 | - name 33 | - type (user or group) 34 | `, 35 | Run: func(cmd *cobra.Command, args []string) { 36 | 37 | // Get the PCE 38 | pce, err := utils.GetTargetPCEV2(false) 39 | if err != nil { 40 | utils.LogError(err.Error()) 41 | } 42 | 43 | // Set the CSV file 44 | if len(args) != 1 { 45 | fmt.Println("Command requires 1 argument for the csv file. See usage help.") 46 | os.Exit(0) 47 | } 48 | 49 | importSecPrincipals(pce, args[0], viper.Get("update_pce").(bool), viper.Get("no_prompt").(bool)) 50 | }, 51 | } 52 | 53 | func importSecPrincipals(pce illumioapi.PCE, csvFile string, updatePCE, noPrompt bool) { 54 | 55 | // Parse the CSV 56 | csvData, err := utils.ParseCSV(csvFile) 57 | if err != nil { 58 | utils.LogErrorf("parsing csv - %s", err) 59 | } 60 | 61 | // Create the slice for new groups 62 | secPrincipals := []newSecAuthPrincipal{} 63 | 64 | // Iterate over the CSV 65 | headersMap := make(map[string]int) 66 | for rowIndex, row := range csvData { 67 | // parse the headers 68 | if rowIndex == 0 { 69 | for colIndex, header := range row { 70 | headersMap[header] = colIndex 71 | } 72 | continue 73 | } 74 | 75 | // Create the new sec principal 76 | secPrincipal := illumioapi.AuthSecurityPrincipal{} 77 | 78 | if colIndex, exists := headersMap[secprincipalexport.HeaderDisplayName]; exists { 79 | secPrincipal.DisplayName = row[colIndex] 80 | } 81 | if colIndex, exists := headersMap[secprincipalexport.HeaderName]; exists { 82 | secPrincipal.Name = row[colIndex] 83 | } 84 | if colIndex, exists := headersMap[secprincipalexport.HeaderType]; exists { 85 | secPrincipal.Type = row[colIndex] 86 | } 87 | 88 | // Check if it exists 89 | if val, exists := pce.AuthSecurityPrincipals[secPrincipal.Name]; exists { 90 | utils.LogWarningf(true, "csv row %d - %s exists (%s). skipping", rowIndex+1, val.Name, val.Href) 91 | continue 92 | } 93 | 94 | // Add to the slice 95 | secPrincipals = append(secPrincipals, newSecAuthPrincipal{secAuthPrincipal: secPrincipal, csvLine: rowIndex + 1}) 96 | } 97 | 98 | // End run of nothing to do 99 | if len(secPrincipals) == 0 { 100 | utils.LogInfo("nothing to be done.", true) 101 | return 102 | } 103 | 104 | if !updatePCE { 105 | utils.LogInfof(true, "workloader identified %d security principals to create. See workloader.log for all identified changes. To do the import, run again using --update-pce flag", len(secPrincipals)) 106 | return 107 | } 108 | 109 | if !noPrompt { 110 | var prompt string 111 | fmt.Printf("[PROMPT] - workloader will create %d security principals in %s (%s). Do you want to run the import (yes/no)? ", len(secPrincipals), pce.FriendlyName, viper.Get(pce.FriendlyName+".fqdn").(string)) 112 | 113 | fmt.Scanln(&prompt) 114 | if strings.ToLower(prompt) != "yes" { 115 | utils.LogInfo("Prompt denied.", true) 116 | return 117 | } 118 | } 119 | 120 | // Create the groups 121 | for _, secPrincipal := range secPrincipals { 122 | _, api, err := pce.CreateAuthSecurityPrincipal(secPrincipal.secAuthPrincipal) 123 | utils.LogAPIRespV2("CreateAuthSecurityPrincipal", api) 124 | if err != nil { 125 | utils.LogErrorf("csv line %d - error - api status code: %d, api resp: %s", secPrincipal.csvLine, api.StatusCode, api.RespBody) 126 | } 127 | utils.LogInfof(true, "csv line %d - created %s - %d", secPrincipal.csvLine, secPrincipal.secAuthPrincipal.DisplayName, api.StatusCode) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /cmd/svcexport/headers.go: -------------------------------------------------------------------------------- 1 | package svcexport 2 | 3 | const ( 4 | HeaderHref = "href" 5 | HeaderName = "name" 6 | HeaderDescription = "description" 7 | HeaderPort = "ports" 8 | HeaderProto = "protocol" 9 | HeaderProcess = "process_name" 10 | HeaderService = "service_name" 11 | HeaderWinService = "is_windows_service" 12 | HeaderICMPCode = "icmp_code" 13 | HeaderICMPType = "icmp_type" 14 | HeaderRansomwareCategory = "ransomware_category" 15 | HeaderRansomwareSeverity = "ransomware_severity" 16 | HeaderRansomWareOs = "ransomware_os_platform" 17 | HeaderExternalDataSet = "external_data_set" 18 | HeaderExternalDataReference = "external_data_reference" 19 | ) 20 | 21 | func ImportHeaders() []string { 22 | return []string{ 23 | HeaderHref, 24 | HeaderName, 25 | HeaderDescription, 26 | HeaderPort, 27 | HeaderProto, 28 | HeaderProcess, 29 | HeaderService, 30 | HeaderWinService, 31 | HeaderICMPCode, 32 | HeaderICMPType, 33 | HeaderRansomwareCategory, 34 | HeaderRansomwareSeverity, 35 | HeaderRansomWareOs, 36 | HeaderExternalDataSet, 37 | HeaderExternalDataReference, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cmd/svcimport/cmd.go: -------------------------------------------------------------------------------- 1 | package svcimport 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | 9 | "github.com/brian1917/workloader/cmd/svcexport" 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | var input Input 16 | var err error 17 | 18 | func init() { 19 | SvcImportCmd.Flags().BoolVarP(&input.Provision, "provision", "p", false, "Provision IP Lists after creating and/or updating.") 20 | SvcImportCmd.Flags().BoolVar(&input.UpdateOnName, "update-on-name", false, "Update based on a match name vs. requiring href.") 21 | SvcImportCmd.Flags().BoolVarP(&input.Meta, "meta", "m", false, "Used for updating descriptions, names, risk information. Leverages the output from svc-export --compressed") 22 | } 23 | 24 | // SvcImportCmd runs the service import command 25 | var SvcImportCmd = &cobra.Command{ 26 | Use: "svc-import [csv file to import]", 27 | Short: "Create and update services from a CSV.", 28 | Long: ` 29 | Create and update services from a CSV file. 30 | 31 | It's recommended to start with a svc-export command and edit/add to it. The input should have a header row as the first row will be skipped. Acceptable values for the headers are below: 32 | ` + "\r\n- " + svcexport.HeaderName + "\r\n" + 33 | "- " + svcexport.HeaderDescription + "\r\n" + 34 | "- " + svcexport.HeaderPort + "\r\n" + 35 | "- " + svcexport.HeaderProto + "\r\n" + 36 | "- " + svcexport.HeaderProcess + "\r\n" + 37 | "- " + svcexport.HeaderService + "\r\n" + 38 | "- " + svcexport.HeaderWinService + "\r\n" + 39 | "- " + svcexport.HeaderICMPCode + "\r\n" + 40 | "- " + svcexport.HeaderICMPType + "\r\n" + ` 41 | 42 | 43 | Notes on input: 44 | - The name field is required. If an HREF field is provided the service will updated. No href means a service will be created. 45 | - Rows that share a common name are the same service. For example, a service that has muliple ports should be separate rows with the same name. 46 | - Ports can be individual values or a range (e.g., 10-20) 47 | 48 | Recommended to run without --update-pce first to log of what will change. If --update-pce is used, svc-import will create the services with a user prompt. To disable the prompt, use --no-prompt.`, 49 | Run: func(cmd *cobra.Command, args []string) { 50 | 51 | // Get the PCE 52 | input.PCE, err = utils.GetTargetPCEV2(true) 53 | if err != nil { 54 | utils.LogError(err.Error()) 55 | } 56 | 57 | // Get the services 58 | input.PCE.Load(illumioapi.LoadInput{Services: true}, utils.UseMulti()) 59 | 60 | // Set the CSV file 61 | if len(args) != 1 { 62 | fmt.Println("Command requires 1 argument for the csv file. See usage help.") 63 | os.Exit(0) 64 | } 65 | 66 | input.Data, err = utils.ParseCSV(args[0]) 67 | if err != nil { 68 | utils.LogError(err.Error()) 69 | } 70 | 71 | // Get the viper values 72 | input.UpdatePCE = viper.Get("update_pce").(bool) 73 | input.NoPrompt = viper.Get("no_prompt").(bool) 74 | 75 | ImportServices(input) 76 | }, 77 | } 78 | -------------------------------------------------------------------------------- /cmd/svcimport/headers.go: -------------------------------------------------------------------------------- 1 | package svcimport 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func (i *Input) processHeaders(headers []string) { 8 | 9 | // Convert the first row into a map 10 | csvHeaderMap := make(map[string]int) 11 | for i, header := range headers { 12 | csvHeaderMap[strings.ToLower(header)] = i 13 | } 14 | 15 | // Initiate the map 16 | i.Headers = make(map[string]int) 17 | 18 | // Update the header map 19 | for header, col := range csvHeaderMap { 20 | i.Headers[header] = col 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /cmd/svcimport/services.go: -------------------------------------------------------------------------------- 1 | package svcimport 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | 10 | "github.com/brian1917/workloader/cmd/svcexport" 11 | "github.com/brian1917/workloader/utils" 12 | ) 13 | 14 | func processServices(input Input, data []string, csvLine int) (winSvc illumioapi.WindowsService, svcPort illumioapi.ServicePort) { 15 | var err error 16 | 17 | // If the port column is there and not blank, process it. 18 | if col, ok := input.Headers[svcexport.HeaderPort]; ok && data[col] != "" { 19 | // The port is the first entry after splitting on the "-" and removing spaces 20 | intValue, err := strconv.Atoi(strings.Split(strings.Replace(data[col], " ", "", -1), "-")[0]) 21 | if err != nil { 22 | utils.LogError(fmt.Sprintf("CSV line %d - invalid %s", csvLine, svcexport.HeaderPort)) 23 | } 24 | winSvc.Port = &intValue 25 | // Make the service port the same as the WinSvc 26 | svcPort.Port = winSvc.Port 27 | 28 | // The to port is the second entry after splitting on the "-" and removing spaces 29 | if strings.Contains(data[input.Headers[svcexport.HeaderPort]], "-") { 30 | winSvc.ToPort, err = strconv.Atoi(strings.Split(strings.Replace(data[col], " ", "", -1), "-")[1]) 31 | if err != nil { 32 | utils.LogError(fmt.Sprintf("CSV line %d - invalid %s", csvLine, svcexport.HeaderPort)) 33 | } 34 | // Make the service port the same as the WinSvc 35 | svcPort.ToPort = winSvc.ToPort 36 | } 37 | } 38 | 39 | // Process the protocol column 40 | if col, ok := input.Headers[svcexport.HeaderProto]; !ok && illumioapi.PtrToVal(winSvc.Port) != 0 { 41 | utils.LogError(fmt.Sprintf("CSV line %d - protocol is required when port is provided", csvLine)) 42 | } else if ok && data[col] != "" { 43 | proto := 0 44 | if strings.ToLower(data[col]) == "tcp" { 45 | proto = 6 46 | } else if strings.ToLower(data[col]) == "udp" { 47 | proto = 17 48 | } else { 49 | proto, err = strconv.Atoi(data[col]) 50 | if err != nil { 51 | utils.LogError(fmt.Sprintf("CSV line %d - invalid %s", csvLine, svcexport.HeaderProto)) 52 | } 53 | } 54 | winSvc.Protocol = proto 55 | svcPort.Protocol = proto 56 | } 57 | 58 | // Process the ICMP Code column 59 | if col, ok := input.Headers[svcexport.HeaderICMPCode]; ok && data[col] != "" { 60 | winSvc.IcmpCode, err = strconv.Atoi(data[col]) 61 | if err != nil { 62 | utils.LogError(fmt.Sprintf("CSV line %d - invalid ICMP code", csvLine)) 63 | } 64 | svcPort.IcmpCode = winSvc.IcmpCode 65 | } 66 | 67 | // Process the ICMP Type column 68 | if col, ok := input.Headers[svcexport.HeaderICMPType]; ok && data[col] != "" { 69 | winSvc.IcmpType, err = strconv.Atoi(data[col]) 70 | if err != nil { 71 | utils.LogError(fmt.Sprintf("CSV line %d - invalid ICMP type", csvLine)) 72 | } 73 | svcPort.IcmpType = winSvc.IcmpType 74 | } 75 | 76 | // Process the Process Name 77 | if col, ok := input.Headers[svcexport.HeaderProcess]; ok { 78 | winSvc.ProcessName = data[col] 79 | } 80 | 81 | // Process the service name 82 | if col, ok := input.Headers[svcexport.HeaderService]; ok { 83 | winSvc.ServiceName = data[col] 84 | } 85 | 86 | return winSvc, svcPort 87 | 88 | } 89 | -------------------------------------------------------------------------------- /cmd/templatecreate/cmd.go: -------------------------------------------------------------------------------- 1 | package templatecreate 2 | 3 | // // Global variables 4 | // var directory string 5 | // var ruleSetNames []string 6 | // var templateName string 7 | // var pce illumioapi.PCE 8 | // var err error 9 | 10 | // // TemplateCreateCmd runs the template export command 11 | // var TemplateCreateCmd = &cobra.Command{ 12 | // Use: "template-create [space separated list of rulesets]", 13 | // Short: "Export an Illumio segmentation template.", 14 | // Long: ` 15 | // Create an Illumio segmentation template. 16 | 17 | // Segmentation templates are a set of CSV files. The template-create command will create a template for the rulesets, rules, and services. These templates can be imported using workloader template-import. All IP lists in the rules are converted to Any with the expectation the user will refine on import. 18 | 19 | // Example commands: 20 | 21 | // Create a template named Active-Directory based on the ruleset named "ACTIVE-DIRECTORY | PROD": 22 | // workloader template-create "ACTIVE-DIRECTORY | PROD" -n Active-Directory 23 | 24 | // Create a template based on mutliple rulesets: 25 | // workloader template-create "RULESET1" "RULESET2" -n template_name`, 26 | 27 | // Run: func(cmd *cobra.Command, args []string) { 28 | 29 | // pce, err = utils.GetTargetPCE(true) 30 | // if err != nil { 31 | // utils.Logger.Fatalf("Error getting PCE for csv command - %s", err) 32 | // } 33 | 34 | // // Set the template file 35 | // if len(args) == 0 { 36 | // fmt.Println("Command requires at least 1 argument for the ruleset name(s) to templatize. See usage help.") 37 | // os.Exit(0) 38 | // } 39 | // ruleSetNames = args 40 | 41 | // importTemplate() 42 | // }, 43 | // } 44 | 45 | // func init() { 46 | 47 | // TemplateCreateCmd.Flags().StringVarP(&directory, "directory", "d", "", "directory to export template files to. by default the files are created in the working directory.") 48 | // TemplateCreateCmd.Flags().StringVarP(&templateName, "name", "n", "", "name for the template") 49 | // TemplateCreateCmd.MarkFlagRequired("name") 50 | // TemplateCreateCmd.Flags().SortFlags = false 51 | 52 | // } 53 | 54 | // // Process template file 55 | // func importTemplate() { 56 | 57 | // // Load the PCE with RuleSets 58 | // apiResps, err := pce.Load(illumioapi.LoadInput{RuleSets: true}) 59 | // utils.LogMultiAPIResp(apiResps) 60 | // if err != nil { 61 | // utils.LogError(err.Error()) 62 | // } 63 | 64 | // // Create the slice of target Hrefs 65 | // var targetRuleSetsHrefs []string 66 | // for _, rsName := range ruleSetNames { 67 | // if val, ok := pce.RuleSets[rsName]; ok { 68 | // targetRuleSetsHrefs = append(targetRuleSetsHrefs, val.Href) 69 | // } else { 70 | // utils.LogError(fmt.Sprintf("%s does not exist as a ruleset in the PCE", rsName)) 71 | // } 72 | // } 73 | 74 | // // Get the services we need. 75 | // services := make(map[string]bool) 76 | // for _, rsHef := range targetRuleSetsHrefs { 77 | // if rs, ok := pce.RuleSets[rsHef]; ok { 78 | // for _, rule := range rs.Rules { 79 | // for _, svc := range *rule.IngressServices { 80 | // if svc.Href != nil && *svc.Href != "" { 81 | // services[*svc.Href] = true 82 | // } 83 | // } 84 | // } 85 | // } 86 | // } 87 | // serviceHrefs := []string{} 88 | // for svc := range services { 89 | // serviceHrefs = append(serviceHrefs, svc) 90 | // } 91 | 92 | // // Export the RuleSets 93 | // fmt.Println("\r\n------------------------------------------ RULE SETS ------------------------------------------") 94 | // rulesetexport.ExportRuleSets(pce, fmt.Sprintf("%s%s.rulesets.csv", directory, templateName), true, targetRuleSetsHrefs) 95 | 96 | // // Export the Rules 97 | // fmt.Println("\r\n------------------------------------------- RULES ---------------------------------------------") 98 | // ruleexport.ExportRules(ruleexport.Input{PCE: pce, SkipWkldDetailCheck: true, OutputFileName: fmt.Sprintf("%s%s.rules.csv", directory, templateName), PolicyVersion: "draft", NoHref: true, RulesetHrefs: targetRuleSetsHrefs}) 99 | 100 | // // Export the services 101 | // fmt.Println("\r\n------------------------------------------ SERVICES -------------------------------------------") 102 | // svcexport.ExportServices(pce, true, fmt.Sprintf("%s%s.services.csv", directory, templateName), serviceHrefs) 103 | 104 | // // Get the directory 105 | // if directory == "" { 106 | // directory = "illumio-templates/" 107 | // } else if directory[len(directory)-1:] != string(os.PathSeparator) { 108 | // directory = fmt.Sprintf("%s%s", directory, string(os.PathSeparator)) 109 | // } 110 | 111 | // 112 | // } 113 | -------------------------------------------------------------------------------- /cmd/templatelist/cmd.go: -------------------------------------------------------------------------------- 1 | package templatelist 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "sort" 8 | "strings" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var directory string 15 | 16 | func init() { 17 | TemplateListCmd.Flags().StringVar(&directory, "directory", "", "Directory with template files. Default is workign directory illumio-templates") 18 | } 19 | 20 | // TemplateListCmd lists all templates in the PCE 21 | var TemplateListCmd = &cobra.Command{ 22 | Use: "template-list", 23 | Short: "List templates available in", 24 | Long: ` 25 | List available Illumio templates. 26 | 27 | Segmentation templates are a set of CSV files. By default, workloader looks for an "illumio-template" directory in the current directory. To use a different directory, use the --directory flag. 28 | 29 | The update-pce and --no-prompt flags are ignored for this command.`, 30 | Run: func(cmd *cobra.Command, args []string) { 31 | 32 | // Get the directory 33 | if directory == "" { 34 | directory = "illumio-templates/" 35 | } else if directory[len(directory)-1:] != string(os.PathSeparator) { 36 | directory = fmt.Sprintf("%s%s", directory, string(os.PathSeparator)) 37 | } 38 | 39 | // Get the files in that directory 40 | files, err := ioutil.ReadDir(directory) 41 | if err != nil { 42 | utils.LogError(err.Error()) 43 | } 44 | 45 | // Create a template map 46 | templates := make(map[string][]string) 47 | 48 | // Iterate through each file 49 | for _, f := range files { 50 | templateType := strings.Split(f.Name(), ".")[1] 51 | templateName := strings.Split(f.Name(), ".")[0] 52 | // If the templateName is already in the map, append. Else, add it to map. 53 | if val, ok := templates[templateName]; ok { 54 | templates[templateName] = append(val, templateType) 55 | } else { 56 | templates[templateName] = []string{templateType} 57 | } 58 | } 59 | 60 | // Create a templateNames slice so we can sort it. 61 | templateNames := []string{} 62 | for t := range templates { 63 | templateNames = append(templateNames, t) 64 | } 65 | sort.SliceStable(templateNames, func(i, j int) bool { 66 | return i < j 67 | }) 68 | 69 | // Print the sorted templates and the template types from the map 70 | for _, t := range templateNames { 71 | fmt.Printf("%s (%s)\r\n", t, strings.Join(templates[t], ", ")) 72 | } 73 | 74 | }, 75 | } 76 | -------------------------------------------------------------------------------- /cmd/unusedumwl/cmd.go: -------------------------------------------------------------------------------- 1 | package unusedumwl 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/brian1917/illumioapi" 7 | "github.com/brian1917/workloader/utils" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var pce illumioapi.PCE 12 | var err error 13 | var start, end, exclServiceCSV, outputFileName string 14 | var nonUni, includeAllUmwls bool 15 | var maxResults int 16 | 17 | func init() { 18 | UnusedUmwlCmd.Flags().BoolVarP(&includeAllUmwls, "all", "a", false, "export all umwls with traffic count. default only exports umwl with 0 traffic.") 19 | UnusedUmwlCmd.Flags().IntVarP(&maxResults, "max-results", "m", 1000, "max results in explorer. Maximum value is 100000. A higher maxiumum value is ") 20 | UnusedUmwlCmd.Flags().StringVarP(&start, "start", "s", time.Now().AddDate(0, 0, -88).In(time.UTC).Format("2006-01-02"), "start date in the format of yyyy-mm-dd.") 21 | UnusedUmwlCmd.Flags().StringVarP(&end, "end", "e", time.Now().Add(time.Hour*24).Format("2006-01-02"), "end date in the format of yyyy-mm-dd.") 22 | UnusedUmwlCmd.Flags().BoolVarP(&nonUni, "incl-non-unicast", "n", false, "includes non-unicast (broadcast and multicast) flows in the output. Default is unicast only.") 23 | UnusedUmwlCmd.Flags().StringVarP(&exclServiceCSV, "excl-svc-file", "x", "", "file location of csv with port/protocols to exclude. Port number in column 1 and IANA numeric protocol in Col 2. Headers optional.") 24 | UnusedUmwlCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename. If iterating through labels, the labels will be appended to the provided name before the provided file extension. To name the files for the labels, use just an extension (--output-file .csv).") 25 | UnusedUmwlCmd.Flags().SortFlags = false 26 | 27 | } 28 | 29 | // UnusedUmwlCmd produces a report of unmanaged workloads that do not have traffic to them 30 | var UnusedUmwlCmd = &cobra.Command{ 31 | Use: "unused-umwl", 32 | Short: "Create a report of unmanaged workloads with no traffic.", 33 | Long: ` 34 | Create a report of unmanaged workloads with no traffic. 35 | 36 | The update-pce and --no-prompt flags are ignored for this command.`, 37 | Run: func(cmd *cobra.Command, args []string) { 38 | 39 | pce, err = utils.GetTargetPCE(true) 40 | if err != nil { 41 | utils.LogError(err.Error()) 42 | } 43 | 44 | unusedUmwl() 45 | }, 46 | } 47 | -------------------------------------------------------------------------------- /cmd/unusedumwl/unusedUmwl.go: -------------------------------------------------------------------------------- 1 | package unusedumwl 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | "time" 8 | 9 | "github.com/brian1917/illumioapi" 10 | "github.com/brian1917/workloader/utils" 11 | ) 12 | 13 | func unusedUmwl() { 14 | 15 | // Get the unmanaged workloads 16 | umwls, a, err := pce.GetWklds(map[string]string{"managed": "false"}) 17 | utils.LogAPIResp("GetAllWorkloadsQP", a) 18 | if err != nil { 19 | utils.LogError(err.Error()) 20 | } 21 | 22 | // Create the default query struct and an "or" operator 23 | tq := illumioapi.TrafficQuery{QueryOperator: "or", PolicyStatuses: []string{}, ExcludeWorkloadsFromIPListQuery: true} 24 | 25 | // Check max results for valid value 26 | if maxResults < 1 || maxResults > 100000 { 27 | utils.LogError("max-results must be between 1 and 100000") 28 | } 29 | tq.MaxFLows = maxResults 30 | 31 | // Get the start date 32 | tq.StartTime, err = time.Parse("2006-01-02 MST", fmt.Sprintf("%s %s", start, "UTC")) 33 | if err != nil { 34 | utils.LogError(err.Error()) 35 | } 36 | tq.StartTime = tq.StartTime.In(time.UTC) 37 | 38 | // Get the end date 39 | tq.EndTime, err = time.Parse("2006-01-02 15:04:05 MST", fmt.Sprintf("%s 23:59:59 %s", end, "UTC")) 40 | if err != nil { 41 | utils.LogError(err.Error()) 42 | } 43 | tq.EndTime = tq.EndTime.In(time.UTC) 44 | 45 | // Get the services 46 | if exclServiceCSV != "" { 47 | tq.PortProtoExclude, err = utils.GetServicePortsCSV(exclServiceCSV) 48 | if err != nil { 49 | utils.LogError(err.Error()) 50 | } 51 | } 52 | 53 | // Exclude broadcast and multicast, unless flag set to include non-unicast flows 54 | if !nonUni { 55 | tq.TransmissionExcludes = []string{"broadcast", "multicast"} 56 | } 57 | 58 | // Start the CSV data 59 | csvData := [][]string{{"hostname", "name", "href", "role", "app", "env", "loc", "interfaces", "traffic_count"}} 60 | 61 | // Iterate over UMWLs 62 | for _, umwl := range umwls { 63 | tq.SourcesInclude = [][]string{{umwl.Href}} 64 | tq.DestinationsInclude = [][]string{{umwl.Href}} 65 | traffic, a, err := pce.GetTrafficAnalysis(tq) 66 | utils.LogAPIResp("GetTrafficAnalysis", a) 67 | if err != nil { 68 | utils.LogError(err.Error()) 69 | } 70 | 71 | // Get interfaces 72 | interfaces := []string{} 73 | for _, i := range umwl.Interfaces { 74 | ipAddress := fmt.Sprintf("%s:%s", i.Name, i.Address) 75 | if i.CidrBlock != nil && *i.CidrBlock != 0 { 76 | ipAddress = fmt.Sprintf("%s:%s/%s", i.Name, i.Address, strconv.Itoa(*i.CidrBlock)) 77 | } 78 | interfaces = append(interfaces, ipAddress) 79 | } 80 | 81 | // Append to the CSV 82 | if len(traffic) == 0 || includeAllUmwls { 83 | csvData = append(csvData, []string{umwl.Hostname, umwl.Name, umwl.Href, umwl.GetRole(pce.Labels).Value, umwl.GetApp(pce.Labels).Value, umwl.GetEnv(pce.Labels).Value, umwl.GetLoc(pce.Labels).Value, strings.Join(interfaces, ";"), strconv.Itoa(len(traffic))}) 84 | } 85 | 86 | // Log iteration 87 | str := "" 88 | if len(traffic) == maxResults { 89 | str = " (query max results)" 90 | } 91 | utils.LogInfo(fmt.Sprintf("href: %s - hostname: %s - name: %s - %d traffic records%s", umwl.Href, umwl.Hostname, umwl.Name, len(traffic), str), true) 92 | } 93 | 94 | // Output the CSV Data 95 | if len(csvData) > 1 { 96 | if outputFileName == "" { 97 | outputFileName = fmt.Sprintf("workloader-unused-umwl-%s.csv", time.Now().Format("20060102_150405")) 98 | } 99 | utils.WriteOutput(csvData, csvData, outputFileName) 100 | utils.LogInfo(fmt.Sprintf("%d umwls exported", len(csvData)-1), true) 101 | } else { 102 | // Log command execution for 0 results 103 | utils.LogInfo("no records exported matching criteria", true) 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /cmd/venexport/headers.go: -------------------------------------------------------------------------------- 1 | package venexport 2 | 3 | const ( 4 | HeaderHref = "href" 5 | HeaderName = "name" 6 | HeaderDescription = "description" 7 | HeaderVenType = "ven_type" 8 | HeaderHostname = "primary_workload_hostname" 9 | HeaderUID = "uid" 10 | HeaderStatus = "status" 11 | HeaderVersion = "version" 12 | HeaderActivationType = "activation_type" 13 | HeaderActivePceFqdn = "active_pce_fqdn" 14 | HeaderTargetPceFqdn = "target_pce_fqdn" 15 | HeaderWorkloads = "workloads" 16 | HeaderContainerCluster = "container_cluster" 17 | HeaderHealth = "ven_health" 18 | HeaderWkldHref = "wkld_href" 19 | ) 20 | 21 | func AllHeaders() []string { 22 | return []string{ 23 | HeaderHref, 24 | HeaderName, 25 | HeaderDescription, 26 | HeaderVenType, 27 | HeaderHostname, 28 | HeaderUID, 29 | HeaderStatus, 30 | HeaderVersion, 31 | HeaderActivationType, 32 | HeaderActivePceFqdn, 33 | HeaderTargetPceFqdn, 34 | HeaderWorkloads, 35 | HeaderContainerCluster, 36 | HeaderHealth, 37 | HeaderWkldHref, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cmd/virtualserviceexport/cmd.go: -------------------------------------------------------------------------------- 1 | package virtualserviceexport 2 | 3 | import ( 4 | "strconv" 5 | 6 | ia "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() {} 12 | 13 | // VsExportCmd runs the workload identifier 14 | var VsExportCmd = &cobra.Command{ 15 | Use: "virtualservice-export", 16 | Short: "Create a CSV export of all virtual services in the PCE.", 17 | Long: ` 18 | Create a CSV export of all virtual services in the PCE. 19 | 20 | The update-pce and --no-prompt flags are ignored for this command.`, 21 | Run: func(cmd *cobra.Command, args []string) { 22 | 23 | // Get the PCE. 24 | pce, err := utils.GetTargetPCEV2(false) 25 | if err != nil { 26 | utils.LogErrorf("getting pce - %s", err) 27 | } 28 | // Run the command 29 | vsexport(pce) 30 | }, 31 | } 32 | 33 | func vsexport(pce ia.PCE) { 34 | 35 | // Load the pce 36 | apiResps, err := pce.Load(ia.LoadInput{VirtualServices: true, LabelDimensions: true, Labels: true}, utils.UseMulti()) 37 | utils.LogMultiAPIRespV2(apiResps) 38 | if err != nil { 39 | utils.LogErrorf("loading pce - %s", err) 40 | } 41 | 42 | // Start the csv export 43 | labelKeys := []string{} 44 | for _, lk := range pce.LabelDimensionsSlice { 45 | labelKeys = append(labelKeys, lk.Key) 46 | } 47 | 48 | // Start the csv 49 | csvData := [][]string{append([]string{"name", "href", "service_ports"}, labelKeys...)} 50 | 51 | // Iterate over the virtual services 52 | for _, vs := range pce.VirtualServicesSlice { 53 | row := []string{vs.Name, vs.Href} 54 | 55 | // Service port 56 | spStr := "" 57 | for _, sp := range ia.PtrToVal(vs.ServicePorts) { 58 | if sp.ToPort == 0 { 59 | spStr = strconv.Itoa(ia.PtrToVal(sp.Port)) 60 | } else { 61 | spStr = strconv.Itoa(ia.PtrToVal(sp.Port)) + "-" + strconv.Itoa(sp.ToPort) 62 | } 63 | if sp.Protocol == 6 { 64 | spStr += " tcp" 65 | } else if sp.Protocol == 17 { 66 | spStr += " udp" 67 | } else { 68 | spStr += " " + strconv.Itoa(sp.Protocol) 69 | } 70 | } 71 | row = append(row, spStr) 72 | 73 | // Labels 74 | for _, lk := range labelKeys { 75 | row = append(row, vs.GetLabelByKey(lk, pce.Labels).Value) 76 | } 77 | csvData = append(csvData, row) 78 | } 79 | 80 | // Write out the CSV 81 | utils.WriteOutput(csvData, nil, utils.FileName("")) 82 | } 83 | -------------------------------------------------------------------------------- /cmd/vmsync/cmd.go: -------------------------------------------------------------------------------- 1 | package vmsync 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | var vcenter, datacenter, cluster, folder, userID, secret string 12 | 13 | var csvFile string 14 | var ignoreState, ignoreSubfolders, umwl, keepFile, keepFQDNHostname, deprecated, insecure, allIPs, vcName, ipv6 bool 15 | var updatePCE, noPrompt bool 16 | var vc VCenter 17 | var maxCreate, maxUpdate int 18 | 19 | // Init builds the commands 20 | func init() { 21 | 22 | // Disable sorting 23 | cobra.EnableCommandSorting = false 24 | 25 | //awsimport options 26 | VCenterSyncCmd.Flags().StringVarP(&vcenter, "vcenter", "v", "", "fqdn or ip of vcenter instance (e.g., vcenter.illumio.com)") 27 | VCenterSyncCmd.Flags().StringVarP(&userID, "user", "u", "", "vcenter username with access to vcenter api") 28 | VCenterSyncCmd.Flags().StringVarP(&secret, "password", "p", "", "vcenter password with access to vcenter api") 29 | VCenterSyncCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "ignore vcenter ssl certificate validation.") 30 | VCenterSyncCmd.Flags().StringVarP(&datacenter, "datacenter", "d", "", "limit sync a specific vcenter catacenter.") 31 | VCenterSyncCmd.Flags().StringVarP(&cluster, "cluster", "c", "", "limit sync to a specific vcenter cluster.") 32 | VCenterSyncCmd.Flags().StringVarP(&folder, "folder", "f", "", "limit sync to a specific vcenter folder and subfolders.") 33 | VCenterSyncCmd.Flags().BoolVarP(&umwl, "umwl", "", false, "create unmanaged workloads for VMs that do not exist in the PCE. future updtaes will only update labels.") 34 | VCenterSyncCmd.Flags().BoolVarP(&allIPs, "all-int", "a", false, "enable syncing multiple interfaces") 35 | VCenterSyncCmd.Flags().BoolVarP(&ipv6, "ipv6", "", false, "used with all-int to sync ipv6 addresses.") 36 | VCenterSyncCmd.Flags().BoolVarP(&ignoreState, "ignore-state", "", false, "sync workloads in states other than \"Powered_on\".") 37 | VCenterSyncCmd.Flags().BoolVarP(&ignoreSubfolders, "ignore-subfolders", "", false, "only searches the 'folders' for VMs. Doesnt look in subfolders. ") 38 | VCenterSyncCmd.Flags().BoolVarP(&vcName, "vcentername", "", false, "match on vcenter VM name vs. using VMTools hostname.") 39 | VCenterSyncCmd.Flags().BoolVarP(&keepFile, "keep-file", "", false, "keep the csv file used for assigning labels.") 40 | VCenterSyncCmd.Flags().BoolVarP(&keepFQDNHostname, "keep-fqdn", "", false, "keep fqdn vs. removing the domain from the hostname.") 41 | //VCenterSyncCmd.Flags().BoolVarP(&deprecated, "deprecated", "", false, "Use this option if you are running an older version of the API (VCenter 6.5-7.0.u2") 42 | VCenterSyncCmd.Flags().IntVar(&maxCreate, "max-create", -1, "maximum number of unmanaged workloads that can be created. -1 is unlimited.") 43 | VCenterSyncCmd.Flags().IntVar(&maxUpdate, "max-update", -1, "maximum number of workloads that can be updated. -1 is unlimited.") 44 | 45 | VCenterSyncCmd.MarkFlagRequired("userID") 46 | VCenterSyncCmd.MarkFlagRequired("secret") 47 | VCenterSyncCmd.MarkFlagRequired("vcenter") 48 | VCenterSyncCmd.Flags().SortFlags = false 49 | 50 | } 51 | 52 | // VCenterSyncCmd checks if the keyfilename is entered. 53 | var VCenterSyncCmd = &cobra.Command{ 54 | Use: "vmsync [csv with mapping]", 55 | Short: "Sync VCenter VM tags with PCE workload labels.", 56 | Long: ` 57 | Sync VCenter VM tags with PCE workload labels. 58 | 59 | A csv file is needed to map VCenter Categories to PCE label keys. The csv skips the first row expecting headers. 60 | The VCenter category should be in the first column and the corredsponding illumio label key in the second. 61 | 62 | For all VCenter object (datacenter, cluster, folder) you can enter more than one. They need to be seperated by commas without spaces. 63 | 64 | Support VCenter version > 7.0.u2`, 65 | 66 | Run: func(cmd *cobra.Command, args []string) { 67 | 68 | // Set the CSV file 69 | if len(args) != 1 { 70 | fmt.Println("Command requires 1 argument for the csv file. See usage help.") 71 | os.Exit(0) 72 | } 73 | csvFile = args[0] 74 | 75 | if (!umwl && (allIPs || ipv6)) || (umwl && (ipv6 && !allIPs)) { 76 | fmt.Println("Cannot use \"--allintf\" or \"--ipv6\" without \"--uwml\" with \"vmsync\". \"--ipv6\" requires \"--allintf\"") 77 | os.Exit(0) 78 | } 79 | //Get the debug value from viper 80 | //debug = viper.Get("debug").(bool) 81 | updatePCE = viper.Get("update_pce").(bool) 82 | noPrompt = viper.Get("no_prompt").(bool) 83 | 84 | //load keymapfile, This file will have the Catagories to Label Type mapping 85 | keyMap := readKeyFile(csvFile) 86 | 87 | vc.KeyMap = keyMap 88 | vc.VCenterURL = vcenter 89 | vc.User = userID 90 | vc.Secret = secret 91 | vc.DisableTLSChecking = insecure 92 | vc.Header = make(map[string]string) 93 | 94 | vc.setupVCenterSession() 95 | 96 | vc.compileVMData(keyMap) 97 | //Sync VMs to Workloads or create UMWL VMs for all machines in VCenter not running VEN 98 | 99 | }, 100 | } 101 | -------------------------------------------------------------------------------- /cmd/vmsync/headers.go: -------------------------------------------------------------------------------- 1 | package vmsync 2 | 3 | // vcenterTags - container for tags and their category, categoryId and pce labeltype once matched 4 | type vcenterTags struct { 5 | LabelType string 6 | CategoryID string `json:"category_id"` 7 | Category string `json:"category"` 8 | Tag string `json:"tag"` 9 | } 10 | 11 | // categoryDetail - used to get the Category Name which equates to LabelType 12 | type categoryDetail struct { 13 | Name string `json:"name"` 14 | Cardinality string `json:"cardinality"` 15 | Description string `json:"description"` 16 | ID string `json:"id"` 17 | AssociableTypes []string `json:"associable_types"` 18 | UsedBy []string `json:"used_by"` 19 | } 20 | 21 | // tagDetail - Struct used to get the Tag Name from the TagID. 22 | type tagDetail struct { 23 | Name string `json:"name"` 24 | CategoryID string `json:"category_id"` 25 | Description string `json:"description"` 26 | ID string `json:"id"` 27 | UsedBy []string `json:"used_by"` 28 | } 29 | 30 | // vcenterVM - Struct used to gather all VM informatoin to be the basis of the wkld.import file 31 | type vcenterVM struct { 32 | VMID string `json:"vm"` 33 | Name string `json:"name"` 34 | VCName string 35 | PowerState string `json:"power_state"` 36 | Tags map[string]string 37 | Interfaces [][]string 38 | IPs map[string]bool 39 | VMInterfaces []Netinterfaces 40 | } 41 | type VMIdentity struct { 42 | Family string `json:"family"` 43 | FullName struct { 44 | Args []string `json:"args"` 45 | DefaultMessage string `json:"default_message"` 46 | ID string `json:"id"` 47 | Localized string `json:"localized"` 48 | } `json:"full_name"` 49 | HostName string `json:"host_name"` 50 | IPAddress string `json:"ip_address"` 51 | Name string `json:"name"` 52 | } 53 | 54 | // vcenterObjects - Struct that is used for filtering VMs. 55 | type vcenterObjects struct { 56 | Name string `json:"name"` 57 | Datacenter string `json:"datacenter"` 58 | Cluster string `json:"cluster"` 59 | Folder string `json:"folder"` 60 | } 61 | 62 | // RequestObject for getting all tags for a set of VMs 63 | type objects struct { 64 | Type string `json:"type"` 65 | ID string `json:"id"` 66 | } 67 | type requestObject struct { 68 | ObjectId []objects `json:"object_ids"` 69 | } 70 | 71 | // ResponseObject for what comes back when requesting all tags for a set of VMs 72 | type responseObject struct { 73 | TagIds []string `json:"tag_ids"` 74 | ObjectId objects `json:"object_id"` 75 | } 76 | 77 | // Netinterface struct for GetVMDetail Call. This is the network data discovered by VMTools 78 | type Netinterfaces struct { 79 | IP struct { 80 | IPAddresses []struct { 81 | IPAddress string `json:"ip_address"` 82 | Origin string `json:"origin"` 83 | PrefixLength int `json:"prefix_length"` 84 | State string `json:"state"` 85 | } `json:"ip_addresses"` 86 | } `json:"ip"` 87 | Nic string `json:"nic"` 88 | MacAddress string `json:"mac_address"` 89 | } 90 | 91 | // VCenter getVersion API 92 | type VCVersion struct { 93 | Build string `json:"build"` 94 | InstallTime string `json:"install_time"` 95 | Product string `json:"product"` 96 | Releasedate string `json:"releasedate"` 97 | Summary string `json:"summary"` 98 | Type string `json:"type"` 99 | Version string `json:"version"` 100 | } 101 | 102 | // VCenter represents a VMware VCenter environment 103 | // API calls are methods on VCenter 104 | type VCenter struct { 105 | VCenterURL string 106 | User string 107 | Secret string 108 | DisableTLSChecking bool 109 | VCVersion VCVersion 110 | KeyMap map[string]string 111 | Categories []string 112 | VCTags map[string]vcenterTags 113 | VCVMs map[string]vcenterVM 114 | VCVMSlice []vcenterVM 115 | Header map[string]string 116 | } 117 | -------------------------------------------------------------------------------- /cmd/wkldcleanup/cmd.go: -------------------------------------------------------------------------------- 1 | package wkldcleanup 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | ia "github.com/brian1917/illumioapi/v2" 8 | "github.com/brian1917/workloader/utils" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var outputFileName string 13 | 14 | func init() { 15 | WkldCleanUpCmd.Flags().StringVar(&outputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 16 | } 17 | 18 | var WkldCleanUpCmd = &cobra.Command{ 19 | Use: "wkld-cleanup", 20 | Short: "Create a csv export of VENs that should be removed.", 21 | Long: ` 22 | Create a csv export of VENs that should be removed. 23 | 24 | The criteria for identifying for removal are below: 25 | 1) Multiple VENs with the same hostname 26 | 2) Other VENs with the same hostname have a more recent heartbeat 27 | 28 | The output of this command can be fed into the wkld-unpair command. 29 | 30 | The update-pce and --no-prompt flags are ignored for this command.`, 31 | Run: func(cmd *cobra.Command, args []string) { 32 | 33 | // Get the PCE 34 | pce, err := utils.GetTargetPCEV2(true) 35 | if err != nil { 36 | utils.LogErrorf("getting target pce - %s", err) 37 | } 38 | 39 | // Create the WkldCleanUp object 40 | w := WkldCleanUp{PCE: pce} 41 | w.Execute() 42 | }, 43 | } 44 | 45 | // wkldCleanUp struct represents the object to do a workload cleanup 46 | type WkldCleanUp struct { 47 | PCE ia.PCE 48 | } 49 | 50 | func (w *WkldCleanUp) Execute() { 51 | 52 | // Load input from the pce 53 | apiResps, err := w.PCE.Load(ia.LoadInput{Workloads: true, VENs: true, LabelDimensions: true}, utils.UseMulti()) 54 | utils.LogMultiAPIRespV2(apiResps) 55 | if err != nil { 56 | utils.LogErrorf("error loading the pce - %s", err) 57 | } 58 | 59 | // Create a map with the hostname as the key and the value as a slice of workloads 60 | venHostnameMap := make(map[string][]ia.VEN) 61 | for _, ven := range w.PCE.VENsSlice { 62 | venHostnameMap[ia.PtrToVal(ven.Hostname)] = append(venHostnameMap[ia.PtrToVal(ven.Hostname)], ven) 63 | } 64 | 65 | // Create the results output 66 | type result struct { 67 | mostRecentVENHref string 68 | mostRecentHeartbeat time.Time 69 | ven ia.VEN 70 | wkld ia.Workload 71 | } 72 | 73 | removeResults := []result{} 74 | 75 | // Iterate through the map to find the one with the most recent heartbeat 76 | for _, venSlice := range venHostnameMap { 77 | // Do not need to process if there is only one VEN 78 | if len(venSlice) == 1 { 79 | continue 80 | } 81 | 82 | // Iterate one time to get the most recent VEN 83 | mostRecentTimeStamp := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) 84 | mostRecentVENHref := "" 85 | for _, ven := range venSlice { 86 | lastHbTime, err := time.Parse("2006-01-02T15:04:05.000Z", ven.LastHeartBeatAt) 87 | if err != nil { 88 | utils.LogErrorf("parsing last heartbeat time - %s", err) 89 | } 90 | if lastHbTime.After(mostRecentTimeStamp) { 91 | mostRecentTimeStamp, err = time.Parse("2006-01-02T15:04:05.000Z", ven.LastHeartBeatAt) 92 | if err != nil { 93 | utils.LogErrorf("parsing new most recent heartbeat time - %s", err) 94 | } 95 | mostRecentVENHref = ven.Href 96 | } 97 | } 98 | 99 | // Iterate a second time to produce the output 100 | for _, ven := range venSlice { 101 | // Skip the most recent VEN 102 | if ven.Href == mostRecentVENHref { 103 | continue 104 | } 105 | // Get the workload 106 | if len(ia.PtrToVal(ven.Workloads)) != 1 { 107 | utils.LogErrorf("ven %s does not have 1 workload attached", ven.Href) 108 | } 109 | if val, ok := w.PCE.Workloads[ia.PtrToVal(ven.Workloads)[0].Href]; !ok { 110 | utils.LogErrorf("cannot find %s attached to %s", ia.PtrToVal(ven.Workloads)[0].Href, ven.Href) 111 | } else { 112 | removeResults = append(removeResults, result{mostRecentVENHref: mostRecentVENHref, mostRecentHeartbeat: mostRecentTimeStamp, ven: ven, wkld: val}) 113 | } 114 | } 115 | } 116 | 117 | // Create the label dimensions slice 118 | labelDimensions := []string{} 119 | for _, ld := range w.PCE.LabelDimensionsSlice { 120 | labelDimensions = append(labelDimensions, ld.Key) 121 | } 122 | 123 | // Process the CSV output 124 | outputData := [][]string{{"href", "hostname", "ven_type"}} 125 | outputData[0] = append(outputData[0], labelDimensions...) 126 | outputData[0] = append(outputData[0], "removal_reason") 127 | for _, result := range removeResults { 128 | row := []string{result.ven.Href, ia.PtrToVal(result.ven.Hostname), result.ven.VenType} 129 | for _, ld := range labelDimensions { 130 | row = append(row, result.wkld.GetLabelByKey(ld, w.PCE.Labels).Value) 131 | } 132 | row = append(row, fmt.Sprintf("most recent heartbeat at %s. %s has a more recent heartbeat at %s", result.ven.LastHeartBeatAt, result.mostRecentVENHref, result.mostRecentHeartbeat.String())) 133 | outputData = append(outputData, row) 134 | } 135 | 136 | // Write the CSV 137 | if len(outputData) == 1 { 138 | utils.LogInfo("no workloads to clean up", true) 139 | return 140 | } 141 | 142 | // Write the output 143 | if outputFileName == "" { 144 | outputFileName = fmt.Sprintf("workloader-label-export-%s.csv", time.Now().Format("20060102_150405")) 145 | } 146 | 147 | utils.WriteOutput(outputData, nil, outputFileName) 148 | } 149 | -------------------------------------------------------------------------------- /cmd/wkldexport/cmd.go: -------------------------------------------------------------------------------- 1 | package wkldexport 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/utils" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | // Declare global variables 12 | var managedOnly, unmanagedOnly, onlineOnly, noHref, includeVuln, removeDescNewLines, labelSummary, addLabelPrefix bool 13 | var headers, labelFile, uniqueLabelKeys, globalOutputFileName, subnetInclude string 14 | 15 | func init() { 16 | WkldExportCmd.Flags().StringVar(&headers, "headers", "", "comma-separated list of headers for export. default is all headers.") 17 | WkldExportCmd.Flags().StringVar(&labelFile, "label-file", "", "csv file with labels to filter query. see description below.") 18 | WkldExportCmd.Flags().BoolVarP(&managedOnly, "managed-only", "m", false, "only export managed workloads.") 19 | WkldExportCmd.Flags().BoolVarP(&unmanagedOnly, "unmanaged-only", "u", false, "only export unmanaged workloads.") 20 | WkldExportCmd.Flags().BoolVarP(&onlineOnly, "online-only", "o", false, "only export online workloads.") 21 | WkldExportCmd.Flags().BoolVarP(&addLabelPrefix, "add-label-prefix", "a", false, "add the \"label:\" prefix in the header for any label values.") 22 | WkldExportCmd.Flags().StringVarP(&subnetInclude, "subnet", "s", "", "subnet filter to only export workloads with an interface in that subnet. multiple subnets should be comma-separated (e.g., \"10.0.0.64/26,10.0.0.128/26\")") 23 | WkldExportCmd.Flags().BoolVarP(&includeVuln, "incude-vuln-data", "v", false, "include vulnerability data.") 24 | WkldExportCmd.Flags().BoolVar(&noHref, "no-href", false, "do not export href column. use this when exporting data to import into different pce.") 25 | WkldExportCmd.Flags().BoolVar(&labelSummary, "label-summary", false, "include an export of unique label combinations.") 26 | WkldExportCmd.Flags().StringVar(&uniqueLabelKeys, "label-summary-keys", "", "comma-separated list of keys to include for determining uniqueness. blank uses all keys.") 27 | WkldExportCmd.Flags().StringVar(&globalOutputFileName, "output-file", "", "optionally specify the name of the output file location. default is current location with a timestamped filename.") 28 | WkldExportCmd.Flags().BoolVar(&removeDescNewLines, "remove-desc-newline", false, "will remove new line characters in description field.") 29 | 30 | WkldExportCmd.Flags().SortFlags = false 31 | 32 | } 33 | 34 | // WkldExportCmd runs the workload identifier 35 | var WkldExportCmd = &cobra.Command{ 36 | Use: "wkld-export", 37 | Short: "Create a CSV export of all workloads in the PCE.", 38 | Long: ` 39 | Create a CSV export of all workloads in the PCE. 40 | 41 | If only workloads with certain labels, the first row of the label-file should be label keys. The workload query uses an AND operator for entries on the same row and an OR operator for the separate rows. An example label file is below: 42 | +------+-----+-----+-----+----+ 43 | | role | app | env | loc | bu | 44 | +------+-----+-----+-----+----+ 45 | | web | erp | | | | 46 | | | | | bos | it | 47 | | | crm | | | | 48 | +------+-----+-----+-----+----+ 49 | This example queries all workloads that are 50 | - web (role) AND erp (app) 51 | - OR bos(loc) AND it (bu) 52 | - OR CRM (app) 53 | 54 | The update-pce and --no-prompt flags are ignored for this command.`, 55 | Run: func(cmd *cobra.Command, args []string) { 56 | 57 | // Get the PCE 58 | var err error 59 | wkldExport := WkldExport{PCE: &illumioapi.PCE{}, IncludeVuln: includeVuln, RemoveDescNewLines: removeDescNewLines, IncludeLabelSummary: labelSummary, LabelSummaryKeys: uniqueLabelKeys, LabelPrefix: addLabelPrefix} 60 | *wkldExport.PCE, err = utils.GetTargetPCEV2(false) 61 | if err != nil { 62 | utils.LogError(err.Error()) 63 | } 64 | 65 | if headers != "" { 66 | wkldExport.Headers = strings.Split(strings.Replace(headers, " ", "", -1), ",") 67 | } 68 | 69 | preLoadLabels := false 70 | if labelFile != "" { 71 | preLoadLabels = true 72 | api, err := wkldExport.PCE.GetLabels(nil) 73 | utils.LogAPIRespV2("GetLabels", api) 74 | if err != nil { 75 | utils.LogError(err.Error()) 76 | } 77 | } 78 | 79 | // Load the PCE 80 | load := illumioapi.LoadInput{Workloads: true, Labels: !preLoadLabels} 81 | load.WorkloadsQueryParameters = make(map[string]string) 82 | 83 | // Check the label file 84 | if labelFile != "" { 85 | labelCsvData, err := utils.ParseCSV(labelFile) 86 | if err != nil { 87 | utils.LogErrorf("parsing labelFile - %s", err) 88 | } 89 | 90 | labelQuery, err := wkldExport.PCE.WorkloadQueryLabelParameter(labelCsvData) 91 | if err != nil { 92 | utils.LogErrorf("getting label parameter query - %s", err) 93 | } 94 | if len(labelQuery) > 10000 { 95 | utils.LogErrorf("the query is too large. the total character count is %d and the limit for this command is 10,000", len(labelQuery)) 96 | } 97 | load.WorkloadsQueryParameters["labels"] = labelQuery 98 | } 99 | 100 | if unmanagedOnly { 101 | load.WorkloadsQueryParameters["managed"] = "false" 102 | } 103 | if managedOnly { 104 | load.WorkloadsQueryParameters["managed"] = "true" 105 | } 106 | if includeVuln { 107 | load.WorkloadsQueryParameters["representation"] = "workload_labels_vulnerabilities" 108 | } 109 | if onlineOnly { 110 | load.WorkloadsQueryParameters["online"] = "true" 111 | } 112 | 113 | apiResps, err := wkldExport.PCE.Load(load, utils.UseMulti()) 114 | utils.LogMultiAPIRespV2(apiResps) 115 | if err != nil { 116 | utils.LogError(err.Error()) 117 | } 118 | 119 | wkldExport.WriteToCsv(globalOutputFileName) 120 | 121 | }, 122 | } 123 | -------------------------------------------------------------------------------- /cmd/wkldexport/headers.go: -------------------------------------------------------------------------------- 1 | package wkldexport 2 | 3 | const ( 4 | HeaderHostname = "hostname" 5 | HeaderName = "name" 6 | HeaderInterfaces = "interfaces" 7 | HeaderPublicIP = "public_ip" 8 | HeaderDistinguishedName = "distinguished_name" 9 | HeaderIPWithDefaultGw = "ip_with_default_gw" 10 | HeaderNetmaskOfIPWithDefGw = "netmask_of_ip_with_def_gw" 11 | HeaderDefaultGw = "default_gw" 12 | HeaderDefaultGwNetwork = "default_gw_network" 13 | HeaderHref = "href" 14 | HeaderDescription = "description" 15 | HeaderEnforcement = "enforcement" 16 | HeaderVisibility = "visibility" 17 | HeaderOnline = "online" 18 | HeaderAgentStatus = "agent_status" 19 | HeaderSecurityPolicySyncState = "security_policy_sync_state" 20 | HeaderSecurityPolicyAppliedAt = "security_policy_applied_at" 21 | HeaderSecurityPolicyReceivedAt = "security_policy_received_at" 22 | HeaderSecurityPolicyRefreshAt = "security_policy_refresh_at" 23 | HeaderLastHeartbeatOn = "last_heartbeat_on" 24 | HeaderHoursSinceLastHeartbeat = "hours_since_last_heartbeat" 25 | HeaderOsID = "os_id" 26 | HeaderOsDetail = "os_detail" 27 | HeaderVenHref = "ven_href" 28 | HeaderAgentVersion = "agent_version" 29 | HeaderAgentID = "agent_id" 30 | HeaderActivePceFqdn = "active_pce_fqdn" 31 | HeaderServiceProvider = "service_provider" 32 | HeaderDataCenter = "data_center" 33 | HeaderDataCenterZone = "data_center_zone" 34 | HeaderCloudInstanceID = "cloud_instance_id" 35 | HeaderExternalDataSet = "external_data_set" 36 | HeaderExternalDataReference = "external_data_reference" 37 | HeaderCreatedAt = "created_at" 38 | HeaderAgentHealth = "agent_health" 39 | HeaderSPN = "spn" 40 | HeaderManaged = "managed" 41 | HeaderVulnExposureScore = "vuln_exposure_score" 42 | HeaderNumVulns = "num_vulns" 43 | HeaderMaxVulnScore = "max_vuln_score" 44 | HeaderVulnScore = "vuln_score" 45 | HeaderVulnPortExposure = "vuln_port_exposure" 46 | HeaderAnyVulnExposure = "any_ip_vuln_exposure" 47 | HeaderIpListVulnExposure = "ip_list_vuln_exposure" 48 | HeaderRansomewareExposure = "ransomware_exposure" 49 | HeaderProtectionCoverageScore = "protection_coverage_score" 50 | ) 51 | 52 | func AllHeaders(inclVuln bool, inclHref bool) []string { 53 | headers := []string{ 54 | HeaderHostname, 55 | HeaderName, 56 | HeaderInterfaces, 57 | HeaderPublicIP, 58 | HeaderDistinguishedName, 59 | HeaderIPWithDefaultGw, 60 | HeaderNetmaskOfIPWithDefGw, 61 | HeaderDefaultGw, 62 | HeaderDefaultGwNetwork, 63 | } 64 | if inclHref { 65 | headers = append(headers, HeaderHref) 66 | } 67 | headers = append(headers, 68 | HeaderDescription, 69 | HeaderEnforcement, 70 | HeaderOnline, 71 | HeaderAgentStatus, 72 | HeaderSecurityPolicySyncState, 73 | HeaderSecurityPolicyAppliedAt, 74 | HeaderSecurityPolicyReceivedAt, 75 | HeaderSecurityPolicyRefreshAt, 76 | HeaderLastHeartbeatOn, 77 | HeaderHoursSinceLastHeartbeat, 78 | HeaderOsID, 79 | HeaderOsDetail, 80 | HeaderRansomewareExposure, 81 | HeaderProtectionCoverageScore, 82 | HeaderVenHref, 83 | HeaderAgentVersion, 84 | HeaderAgentID, 85 | HeaderActivePceFqdn, 86 | HeaderServiceProvider, 87 | HeaderDataCenter, 88 | HeaderDataCenterZone, 89 | HeaderCloudInstanceID, 90 | HeaderCreatedAt, 91 | HeaderAgentHealth, 92 | HeaderVisibility, 93 | HeaderSPN, 94 | HeaderManaged) 95 | if inclVuln { 96 | headers = append(headers, 97 | HeaderVulnExposureScore, 98 | HeaderNumVulns, 99 | HeaderMaxVulnScore, 100 | HeaderVulnScore, 101 | HeaderVulnPortExposure, 102 | HeaderAnyVulnExposure, 103 | HeaderIpListVulnExposure) 104 | } 105 | headers = append(headers, HeaderExternalDataSet, HeaderExternalDataReference) 106 | 107 | return headers 108 | } 109 | 110 | func ImportHeaders() []string { 111 | return []string{ 112 | HeaderHostname, 113 | HeaderName, 114 | HeaderInterfaces, 115 | HeaderPublicIP, 116 | HeaderDistinguishedName, 117 | HeaderSPN, 118 | HeaderEnforcement, 119 | HeaderVisibility, 120 | HeaderDescription, 121 | HeaderOsID, 122 | HeaderOsDetail, 123 | HeaderDataCenter, 124 | HeaderExternalDataSet, 125 | HeaderExternalDataReference} 126 | } 127 | 128 | func FieldMapping() map[string]string { 129 | 130 | // Get all the headers 131 | allHeaders := AllHeaders(true, true) 132 | 133 | // Check for the existing of the headers 134 | fieldMapping := make(map[string]string) 135 | 136 | // Assign defaults 137 | for _, h := range allHeaders { 138 | fieldMapping[h] = h 139 | } 140 | 141 | // Alternate names for hostname 142 | fieldMapping["host"] = "hostname" 143 | fieldMapping["host_name"] = "hostname" 144 | fieldMapping["host name"] = "hostname" 145 | 146 | // Alternate names for interfaces 147 | fieldMapping["interface"] = "interfaces" 148 | fieldMapping["ifaces"] = "interfaces" 149 | fieldMapping["iface"] = "interfaces" 150 | fieldMapping["ip"] = "interfaces" 151 | fieldMapping["ip_address"] = "interfaces" 152 | fieldMapping["ips"] = "interfaces" 153 | 154 | // Description 155 | fieldMapping["desc"] = "description" 156 | 157 | return fieldMapping 158 | } 159 | -------------------------------------------------------------------------------- /cmd/wkldimport/headers.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/brian1917/workloader/cmd/wkldexport" 9 | 10 | "github.com/brian1917/workloader/utils" 11 | ) 12 | 13 | func (i *Input) ProcessHeaders(headers []string) { 14 | 15 | // Convert the first row into a map 16 | csvHeaderMap := make(map[string]int) 17 | for i, header := range headers { 18 | csvHeaderMap[header] = i 19 | } 20 | 21 | // Get the fieldMap 22 | fieldMap := wkldexport.FieldMapping() 23 | 24 | // Initiate the map 25 | i.Headers = make(map[string]int) 26 | 27 | // Look to see if we have alternatve values for the provided header 28 | for header, col := range csvHeaderMap { 29 | if _, ok := fieldMap[header]; ok { 30 | i.Headers[fieldMap[header]] = col 31 | } else { 32 | // If there is no alternative value, use the provided value 33 | i.Headers[header] = col 34 | } 35 | 36 | } 37 | 38 | if i.MatchString != "" { 39 | if i.MatchString != "href" && i.MatchString != "hostname" && i.MatchString != "name" && i.MatchString != "external_data" { 40 | utils.LogError("invalid match value. must be href, hostname, name, or external_data") 41 | } 42 | return 43 | } 44 | 45 | // If href is provided and UMWL is not set, use href 46 | if val, ok := i.Headers[wkldexport.HeaderHref]; ok && !i.Umwl { 47 | i.MatchString = wkldexport.HeaderHref 48 | utils.LogInfo(fmt.Sprintf("match column set to %d because href header is present and unmanaged workload flag is not set.", val), false) 49 | return 50 | } 51 | 52 | // If hostname is set, use that. 53 | if val, ok := i.Headers[wkldexport.HeaderHostname]; ok { 54 | i.MatchString = wkldexport.HeaderHostname 55 | utils.LogInfo(fmt.Sprintf("match column set to hostname column (%d)", val), false) 56 | return 57 | } 58 | 59 | // If name is set, use that. 60 | if val, ok := i.Headers[wkldexport.HeaderName]; ok { 61 | i.MatchString = wkldexport.HeaderName 62 | utils.LogInfo(fmt.Sprintf("match column set to name column (%d)", val), false) 63 | return 64 | } 65 | 66 | utils.LogError("cannot set a match column based on provided input") 67 | } 68 | 69 | func (i *Input) log() { 70 | 71 | v := reflect.ValueOf(*i) 72 | 73 | logEntry := []string{} 74 | for a := 0; a < v.NumField(); a++ { 75 | if v.Type().Field(a).Name == "PCE" || v.Type().Field(a).Name == "KeepAllPCEInterfaces" || v.Type().Field(a).Name == "FQDNtoHostname" { 76 | continue 77 | } 78 | logEntry = append(logEntry, fmt.Sprintf("%s: %v", v.Type().Field(a).Name, v.Field(a).Interface())) 79 | } 80 | 81 | utils.LogInfo(strings.Join(logEntry, "; "), false) 82 | } 83 | -------------------------------------------------------------------------------- /cmd/wkldimport/hostname.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/cmd/wkldexport" 8 | "github.com/brian1917/workloader/utils" 9 | ) 10 | 11 | func (w *importWkld) hostname(input Input) { 12 | if index, ok := input.Headers[wkldexport.HeaderHostname]; ok { 13 | // It has to either be a new workload or not matching on hostname 14 | if w.wkld.Href == "" || (input.MatchString != wkldexport.HeaderHostname) { 15 | if illumioapi.PtrToVal(w.wkld.Hostname) != w.csvLine[index] { 16 | if w.wkld.Href != "" && input.UpdateWorkloads { 17 | w.change = true 18 | utils.LogInfo(fmt.Sprintf("csv line %d - %s - hostname to be changed from %s to %s", w.csvLineNum, w.compareString, utils.LogBlankValue(illumioapi.PtrToVal(w.wkld.Hostname)), w.csvLine[index]), false) 19 | } 20 | w.wkld.Hostname = &w.csvLine[index] 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cmd/wkldimport/labels.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | "github.com/brian1917/workloader/cmd/wkldexport" 9 | "github.com/brian1917/workloader/utils" 10 | ) 11 | 12 | // checkLabels validates if a label exists. 13 | // If the label exists it returns the label. 14 | // If the label does not exist it creates a temporary label for later 15 | func checkLabel(pce illumioapi.PCE, label illumioapi.Label, newLabels []illumioapi.Label) (illumioapi.Label, []illumioapi.Label) { 16 | 17 | // Check if it exists or not 18 | if _, ok := pce.Labels[label.Key+label.Value]; ok { 19 | return pce.Labels[label.Key+label.Value], newLabels 20 | } 21 | 22 | // If the label doesn't exist, create a placeholder for it 23 | label.Href = fmt.Sprintf("wkld-import-temp-%s-%s", label.Key, label.Value) 24 | newLabels = append(newLabels, label) 25 | utils.LogInfo(fmt.Sprintf("%s label %s needs to be created", label.Key, label.Value), false) 26 | 27 | // Append the label back to the map 28 | pce.Labels[label.Key+label.Value] = label 29 | pce.Labels[label.Href] = label 30 | 31 | return label, newLabels 32 | } 33 | 34 | func (w *importWkld) labels(input Input, newLabels []illumioapi.Label, labelKeysMap map[string]bool) []illumioapi.Label { 35 | 36 | // Create a copy of the workload before editing it 37 | originalWkld := *w.wkld 38 | 39 | // Initialize a variable to clear the labels only if we are processing them 40 | labelsCleared := false 41 | 42 | // Put all labels that don't have a header back in 43 | nonProcessedLabels := []illumioapi.Label{} 44 | if w.wkld.Labels != nil { 45 | for _, l := range illumioapi.PtrToVal(w.wkld.Labels) { 46 | if _, ok := input.Headers[input.PCE.Labels[l.Href].Key]; !ok { 47 | if _, ok := input.Headers["label:"+input.PCE.Labels[l.Href].Key]; !ok { 48 | nonProcessedLabels = append(nonProcessedLabels, l) 49 | } 50 | } 51 | } 52 | } 53 | 54 | // Process all headers to see if they are labels 55 | for headerValue, index := range input.Headers { 56 | 57 | // If it's a protected header value, skip 58 | if _, ok := wkldexport.FieldMapping()[headerValue]; ok { 59 | continue 60 | } 61 | 62 | headerValue = strings.TrimPrefix(headerValue, "label:") 63 | 64 | // Skip if the header is not a label 65 | if !labelKeysMap[headerValue] { 66 | continue 67 | } 68 | 69 | // If the input has a label header, we clear the original labels. 70 | if !labelsCleared { 71 | // Clear the labels 72 | w.wkld.Labels = &[]illumioapi.Label{} 73 | labelsCleared = true 74 | } 75 | 76 | // Get the current label 77 | currentLabel := originalWkld.GetLabelByKey(headerValue, input.PCE.Labels) 78 | 79 | // If the value is blank and the current label exists keep the current label. 80 | // Or, if the CSV entry equals the current value, keep it 81 | if (w.csvLine[index] == "" && currentLabel.Href != "") || (w.csvLine[index] == currentLabel.Value && w.csvLine[index] != "") { 82 | *w.wkld.Labels = append(*w.wkld.Labels, illumioapi.Label{Href: currentLabel.Href}) 83 | continue 84 | } 85 | 86 | // If the value is the delete value, the value is not blank, and the current label is not already blank, log a change without putting any label in. 87 | if w.csvLine[index] == input.RemoveValue && w.csvLine[index] != "" && currentLabel.Href != "" { 88 | // Log if updating 89 | if w.wkld.Href != "" && input.UpdateWorkloads { 90 | w.change = true 91 | utils.LogInfo(fmt.Sprintf("csv line %d - %-s - %s label of %s to be removed.", w.csvLineNum, w.compareString, currentLabel.Key, currentLabel.Value), false) 92 | } 93 | // Stop processing this label 94 | continue 95 | } 96 | 97 | // If the value does not equal the current value and it does not equal the remove value, add the new label. 98 | if w.csvLine[index] != currentLabel.Value && w.csvLine[index] != input.RemoveValue { 99 | // Add that label to the new labels slice] 100 | var retrievedLabel illumioapi.Label 101 | retrievedLabel, newLabels = checkLabel(input.PCE, illumioapi.Label{Key: headerValue, Value: w.csvLine[index]}, newLabels) 102 | *w.wkld.Labels = append(*w.wkld.Labels, illumioapi.Label{Href: retrievedLabel.Href}) 103 | 104 | // Log if updating 105 | if w.wkld.Href != "" && input.UpdateWorkloads { 106 | w.change = true 107 | // Log change required 108 | currentlLabelLogValue := currentLabel.Value 109 | if currentLabel.Value == "" { 110 | currentlLabelLogValue = "" 111 | } 112 | if !input.DoNotLogEachCSVRow { 113 | utils.LogInfo(fmt.Sprintf("csv line %d - %s - %s label to be changed from %s to %s.", w.csvLineNum, w.compareString, headerValue, currentlLabelLogValue, w.csvLine[index]), false) 114 | } 115 | } 116 | } 117 | } 118 | // Add the unprocessed labels if they were cleared 119 | if labelsCleared { 120 | *w.wkld.Labels = append(*w.wkld.Labels, nonProcessedLabels...) 121 | } 122 | 123 | return newLabels 124 | } 125 | -------------------------------------------------------------------------------- /cmd/wkldimport/mode.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | "github.com/brian1917/workloader/cmd/wkldexport" 9 | "github.com/brian1917/workloader/utils" 10 | ) 11 | 12 | func (w *importWkld) enforcement(input Input) { 13 | if input.AllowEnforcementChanges { 14 | // Update the enforcement 15 | if index, ok := input.Headers[wkldexport.HeaderEnforcement]; ok && strings.ToLower(w.csvLine[index]) != "unmanaged" && w.csvLine[index] != "" { 16 | m := strings.ToLower(w.csvLine[index]) 17 | if m != "visibility_only" && m != "full" && m != "selective" && m != "idle" && m != "" { 18 | utils.LogWarning(fmt.Sprintf("csv line %d - %s - invalid mode state. values must be blank, visibility_only, full, selective, or idle. skipping line.", w.csvLineNum, w.compareString), true) 19 | return 20 | } 21 | if illumioapi.PtrToVal(w.wkld.EnforcementMode) != m { 22 | if w.wkld.Href != "" && input.UpdateWorkloads { 23 | w.change = true 24 | utils.LogInfo(fmt.Sprintf("csv line %d - %s enforcement to be changed from %s to %s", w.csvLineNum, w.compareString, illumioapi.PtrToVal(w.wkld.EnforcementMode), w.csvLine[index]), false) 25 | } 26 | w.wkld.EnforcementMode = &m 27 | } 28 | } 29 | } 30 | } 31 | 32 | func (w *importWkld) visibility(input Input) { 33 | if input.AllowEnforcementChanges { 34 | if index, ok := input.Headers[wkldexport.HeaderVisibility]; ok && strings.ToLower(w.csvLine[index]) != "unmanaged" && w.csvLine[index] != "" { 35 | v := strings.ToLower(w.csvLine[index]) 36 | if v != "blocked_allowed" && v != "blocked" && v != "off" && v != "" && v != "enhanced_data_collection" { 37 | utils.LogWarning(fmt.Sprintf("csv line %d - %s - invalid visibility state. values must be blank, blocked_allowed, blocked, enhanced_data_collection, or off. skipping line.", w.csvLineNum, w.compareString), true) 38 | return 39 | } 40 | if w.wkld.GetVisibilityLevel() != v { 41 | if w.wkld.Href != "" && input.UpdateWorkloads { 42 | w.change = true 43 | utils.LogInfo(fmt.Sprintf("csv line %d - %s visibility to be changed from %s to %s", w.csvLineNum, w.compareString, illumioapi.PtrToVal(w.wkld.VisibilityLevel), w.csvLine[index]), false) 44 | } 45 | w.wkld.SetVisibilityLevel(v) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cmd/wkldimport/name.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/cmd/wkldexport" 8 | "github.com/brian1917/workloader/utils" 9 | ) 10 | 11 | func (w *importWkld) name(input Input) { 12 | if index, ok := input.Headers[wkldexport.HeaderName]; ok { 13 | // It has to either be a new workload or not matching on name 14 | if illumioapi.PtrToVal(w.wkld.Name) == "" || (input.MatchString != wkldexport.HeaderName) { 15 | if illumioapi.PtrToVal(w.wkld.Name) != w.csvLine[index] { 16 | if w.wkld.Href != "" && input.UpdateWorkloads { 17 | w.change = true 18 | utils.LogInfo(fmt.Sprintf("csv line %d - %s - name to be changed from %s to %s", w.csvLineNum, w.compareString, utils.LogBlankValue(illumioapi.PtrToVal(w.wkld.Name)), w.csvLine[index]), false) 19 | } 20 | w.wkld.Name = &w.csvLine[index] 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cmd/wkldimport/publicip.go: -------------------------------------------------------------------------------- 1 | package wkldimport 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/brian1917/workloader/cmd/wkldexport" 8 | "github.com/brian1917/workloader/utils" 9 | ) 10 | 11 | func (w *importWkld) publcIP(input Input) { 12 | if index, ok := input.Headers[wkldexport.HeaderPublicIP]; ok { 13 | if w.csvLine[index] != illumioapi.PtrToVal(w.wkld.PublicIP) { 14 | // Validate it first 15 | if !publicIPIsValid(w.csvLine[index]) { 16 | utils.LogError(fmt.Sprintf("csv line %d - invalid Public IP address format.", w.csvLineNum)) 17 | } 18 | if w.wkld.Href != "" && input.UpdateWorkloads { 19 | w.change = true 20 | utils.LogInfo(fmt.Sprintf("csv line %d - %s- public ip to be changed from %s to %s", w.csvLineNum, w.compareString, utils.LogBlankValue(illumioapi.PtrToVal(w.wkld.PublicIP)), w.csvLine[index]), false) 21 | } 22 | w.wkld.PublicIP = &w.csvLine[index] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cmd/wkldlabel/cmd.go: -------------------------------------------------------------------------------- 1 | package wkldlabel 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/brian1917/illumioapi/v2" 8 | "github.com/brian1917/workloader/cmd/wkldimport" 9 | "github.com/brian1917/workloader/utils" 10 | "github.com/spf13/cobra" 11 | "github.com/spf13/viper" 12 | ) 13 | 14 | var labels, hostname string 15 | 16 | func init() { 17 | WkldLabelCmd.Flags().StringVarP(&hostname, "hostname", "n", "", "hostname of the workload to label") 18 | WkldLabelCmd.Flags().StringVarP(&labels, "labels", "l", "", "comma-separated list of labels to apply to the workload. labels should be in the format of key:value (e.g., loc:bos,env:prod)") 19 | 20 | WkldLabelCmd.MarkFlagRequired("hostname") 21 | WkldLabelCmd.MarkFlagRequired("labels") 22 | 23 | WkldLabelCmd.Flags().SortFlags = false 24 | } 25 | 26 | // WkldLabelCmd runs wkld-label 27 | var WkldLabelCmd = &cobra.Command{ 28 | Use: "wkld-label", 29 | Short: "Label a specific workload.", 30 | Long: ` 31 | Label a specific workload. 32 | 33 | The command leverages the wkld-import command. The workloader.log file will log as if it is a single entry in a csv. 34 | `, 35 | Run: func(cmd *cobra.Command, args []string) { 36 | 37 | // Get the PCE 38 | pce, err := utils.GetTargetPCEV2(false) 39 | if err != nil { 40 | utils.LogError(fmt.Sprintf("error getting pce - %s", err.Error())) 41 | } 42 | 43 | updatePCE := viper.Get("update_pce").(bool) 44 | noPrompt := viper.Get("no_prompt").(bool) 45 | 46 | LabelWkld(&pce, hostname, labels, updatePCE, noPrompt) 47 | }, 48 | } 49 | 50 | func LabelWkld(pce *illumioapi.PCE, hostname, labels string, updatePCE, noPrompt bool) { 51 | 52 | // Get the hostname 53 | wkld, api, err := pce.GetWkldByHostname(hostname) 54 | utils.LogAPIRespV2("GetWkldByHostname", api) 55 | if err != nil { 56 | utils.LogError(err.Error()) 57 | } 58 | if illumioapi.PtrToVal(wkld.Hostname) == "" { 59 | utils.LogError(fmt.Sprintf("no workload with hostname %s found", hostname)) 60 | } 61 | 62 | // Load the PCEs labels 63 | api, err = pce.GetLabels(nil) 64 | utils.LogAPIRespV2("GetLabels", api) 65 | if err != nil { 66 | utils.LogError(err.Error()) 67 | } 68 | 69 | // Create wkld-import data 70 | wkldImportData := [][]string{{"hostname"}, {illumioapi.PtrToVal(wkld.Hostname)}} 71 | 72 | // Parse the labels 73 | labels = strings.Replace(labels, ", ", ",", -1) 74 | for _, keyValue := range strings.Split(labels, ",") { 75 | wkldImportData[0] = append(wkldImportData[0], strings.Split(keyValue, ":")[0]) 76 | wkldImportData[1] = append(wkldImportData[1], strings.Split(keyValue, ":")[1]) 77 | } 78 | 79 | // Call wkld-import 80 | wkldimport.ImportWkldsFromCSV(wkldimport.Input{ 81 | PCE: *pce, 82 | ImportData: wkldImportData, 83 | RemoveValue: "nil", 84 | Umwl: false, 85 | UpdateWorkloads: true, 86 | UpdatePCE: updatePCE, 87 | NoPrompt: noPrompt, 88 | MaxUpdate: -1, 89 | MaxCreate: -1, 90 | }) 91 | 92 | } 93 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/brian1917/workloader 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go v1.44.243 7 | github.com/brian1917/illumioapi v1.85.0 8 | github.com/brian1917/illumioapi/v2 v2.0.6 9 | github.com/brian1917/ns v1.2.0 10 | github.com/brian1917/workloader/utils v1.6.0 11 | github.com/google/uuid v1.1.2 12 | github.com/olekukonko/tablewriter v0.0.5 13 | github.com/pkg/errors v0.9.1 14 | github.com/spf13/cobra v1.7.0 15 | github.com/spf13/viper v1.15.0 16 | golang.org/x/term v0.5.0 17 | ) 18 | 19 | require ( 20 | github.com/fsnotify/fsnotify v1.6.0 // indirect 21 | github.com/hashicorp/hcl v1.0.0 // indirect 22 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 23 | github.com/jmespath/go-jmespath v0.4.0 // indirect 24 | github.com/kr/pretty v0.3.1 // indirect 25 | github.com/magiconair/properties v1.8.7 // indirect 26 | github.com/mattn/go-runewidth v0.0.14 // indirect 27 | github.com/mitchellh/mapstructure v1.5.0 // indirect 28 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 29 | github.com/rivo/uniseg v0.4.4 // indirect 30 | github.com/spf13/afero v1.9.3 // indirect 31 | github.com/spf13/cast v1.5.0 // indirect 32 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 33 | github.com/spf13/pflag v1.0.5 // indirect 34 | github.com/subosito/gotenv v1.4.2 // indirect 35 | golang.org/x/sys v0.7.0 // indirect 36 | golang.org/x/text v0.7.0 // indirect 37 | gonum.org/v1/gonum v0.12.0 // indirect 38 | gopkg.in/ini.v1 v1.67.0 // indirect 39 | gopkg.in/yaml.v3 v3.0.1 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /go.work: -------------------------------------------------------------------------------- 1 | go 1.19 2 | 3 | use ./utils 4 | 5 | use ./ 6 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/brian1917/workloader/cmd" 10 | "github.com/brian1917/workloader/cmd/pcemgmt" 11 | "github.com/brian1917/workloader/utils" 12 | ) 13 | 14 | func main() { 15 | 16 | // Setup logging 17 | utils.SetUpLogging() 18 | 19 | // Process target-pces and all-pces 20 | if len(os.Args) > 1 { 21 | if os.Args[1] == "target-pces" && os.Args[2] != "-h" && os.Args[2] != "--help" { 22 | 23 | // Parse CSV data 24 | csvData, err := utils.ParseCSV(os.Args[2]) 25 | if err != nil { 26 | utils.LogError(err.Error()) 27 | } 28 | 29 | // Create PCE map 30 | pceMap := make(map[string]bool) 31 | for _, row := range csvData { 32 | pceMap[row[0]] = true 33 | } 34 | 35 | for _, pce := range pcemgmt.GetAllPCENames() { 36 | 37 | if pceMap[pce] { 38 | utils.LogInfo(fmt.Sprintf("running %s", strings.Join(append(os.Args[3:], "--pce", pce), " ")), true) 39 | command := exec.Command(os.Args[0], append(os.Args[3:], "--pce", pce)...) 40 | stdout, err := command.Output() 41 | if err != nil { 42 | utils.LogError(err.Error()) 43 | } 44 | fmt.Println(string(stdout)) 45 | } 46 | } 47 | return 48 | } 49 | 50 | // Process all-pces 51 | if os.Args[1] == "all-pces" && os.Args[2] != "-h" && os.Args[2] != "--help" { 52 | for _, pce := range pcemgmt.GetAllPCENames() { 53 | utils.LogInfof(true, "running %s", strings.Join(append(os.Args[2:], "--pce", pce), " ")) 54 | command := exec.Command(os.Args[0], append(os.Args[2:], "--pce", pce)...) 55 | stdout, err := command.Output() 56 | if err != nil { 57 | utils.LogError(err.Error()) 58 | } 59 | fmt.Println(string(stdout)) 60 | } 61 | return 62 | } 63 | 64 | // Explorer renamed to traffic 65 | if os.Args[1] == "explorer" { 66 | // utils.LogWarning("this command has been renamed to traffic. please use \"workloader traffic\" in the future", true) 67 | command := exec.Command(os.Args[0], append([]string{"legacy-explorer"}, os.Args[2:]...)...) 68 | utils.LogInfof(false, "executing the following: %s", command.String()) 69 | stdout, err := command.Output() 70 | if err != nil { 71 | utils.LogError(err.Error()) 72 | } 73 | fmt.Println(string(stdout)) 74 | return 75 | } 76 | 77 | // Moved flow summary 78 | if os.Args[1] == "flowsummary" { 79 | utils.LogWarning("this command has been renamed to appgroup-flow-summary. please use \"workloader appgroup-flow-summary\" in the future", true) 80 | if len(os.Args) > 2 && os.Args[2] == "appgroup" { 81 | command := exec.Command(os.Args[0], append([]string{"appgroup-flow-summary"}, os.Args[3:]...)...) 82 | utils.LogInfof(true, "executing the following: %s", command.String()) 83 | stdout, err := command.Output() 84 | if err != nil { 85 | utils.LogError(err.Error()) 86 | } 87 | fmt.Println(string(stdout)) 88 | } 89 | return 90 | } 91 | 92 | // EB change to deny rule 93 | if os.Args[1] == "eb-import" { 94 | utils.LogWarning("this command has been renamed to deny-rule-import. please use \"workloader deny-rule-import\" in the future", true) 95 | command := exec.Command(os.Args[0], append([]string{"deny-rule-import"}, os.Args[2:]...)...) 96 | command.Stdin = os.Stdin 97 | command.Stdout = os.Stdout 98 | command.Stderr = os.Stderr 99 | utils.LogInfof(false, "executing the following: %s", command.String()) 100 | if err := command.Run(); err != nil { 101 | utils.LogError(err.Error()) 102 | } 103 | // fmt.Println(string(stdout)) 104 | return 105 | } 106 | if os.Args[1] == "eb-export" { 107 | utils.LogWarning("this command has been renamed to deny-rule-export. please use \"workloader deny-rule-export\" in the future", true) 108 | command := exec.Command(os.Args[0], append([]string{"deny-rule-export"}, os.Args[2:]...)...) 109 | command.Stdin = os.Stdin 110 | command.Stdout = os.Stdout 111 | command.Stderr = os.Stderr 112 | utils.LogInfof(false, "executing the following: %s", command.String()) 113 | if err := command.Run(); err != nil { 114 | utils.LogError(err.Error()) 115 | } 116 | return 117 | } 118 | // Change to deny rules 119 | if os.Args[1] == "deny-rule-import" || os.Args[1] == "deny-rule-export" { 120 | utils.LogWarning("deny rules as a separate object are deprecated in the newest PCEs (version 24+). use rule-import and rule-export to manage all allow and deny rules.", true) 121 | } 122 | 123 | // Process Mode 124 | if os.Args[1] == "mode" { 125 | utils.LogWarning("this command has been removed. use wkld-import with the --allow-enforcement-changes flag", true) 126 | return 127 | } 128 | } 129 | 130 | // Run command for all other scenarios 131 | cmd.Execute() 132 | } 133 | -------------------------------------------------------------------------------- /utils/bom.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | ) 7 | 8 | // ClearBOM returns an io.Reader that will skip over initial UTF-8 byte order marks. 9 | func ClearBOM(r io.Reader) io.Reader { 10 | buf := bufio.NewReader(r) 11 | b, err := buf.Peek(3) 12 | if err != nil { 13 | // not enough bytes 14 | return buf 15 | } 16 | if b[0] == 0xef && b[1] == 0xbb && b[2] == 0xbf { 17 | buf.Discard(3) 18 | } 19 | return buf 20 | } 21 | -------------------------------------------------------------------------------- /utils/compare.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func SliceComare(slice1 []string, slice2 []string, slice1Name, slice2Name string) (equal bool, logMsg string) { 9 | logs := []string{} 10 | 11 | // Build Maps 12 | map1 := make(map[string]bool) 13 | map2 := make(map[string]bool) 14 | for _, elem := range slice1 { 15 | map1[elem] = true 16 | } 17 | for _, elem := range slice2 { 18 | map2[elem] = true 19 | } 20 | 21 | // Set equal to true and flip it when necessary 22 | equal = true 23 | 24 | // Check slice 1 25 | for _, elem := range slice1 { 26 | if _, ok := map2[elem]; !ok { 27 | equal = false 28 | logs = append(logs, fmt.Sprintf("%s is in %s but not in %s", elem, slice1Name, slice2Name)) 29 | } 30 | } 31 | 32 | // Check slice 2 33 | for _, elem := range slice2 { 34 | if _, ok := map1[elem]; !ok { 35 | equal = false 36 | logs = append(logs, fmt.Sprintf("%s is in %s but not in %s", elem, slice2Name, slice1Name)) 37 | } 38 | } 39 | 40 | return equal, strings.Join(logs, ";") 41 | } 42 | -------------------------------------------------------------------------------- /utils/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/brian1917/workloader/utils 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/brian1917/illumioapi v1.85.0 7 | github.com/brian1917/illumioapi/v2 v2.0.0-beta.30 8 | github.com/olekukonko/tablewriter v0.0.5 9 | github.com/spf13/viper v1.15.0 10 | ) 11 | 12 | require ( 13 | github.com/frankban/quicktest v1.14.4 // indirect 14 | github.com/fsnotify/fsnotify v1.6.0 // indirect 15 | github.com/hashicorp/hcl v1.0.0 // indirect 16 | github.com/magiconair/properties v1.8.7 // indirect 17 | github.com/mattn/go-runewidth v0.0.14 // indirect 18 | github.com/mitchellh/mapstructure v1.5.0 // indirect 19 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 20 | github.com/rivo/uniseg v0.4.4 // indirect 21 | github.com/spf13/afero v1.9.3 // indirect 22 | github.com/spf13/cast v1.5.0 // indirect 23 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 24 | github.com/spf13/pflag v1.0.5 // indirect 25 | github.com/stretchr/testify v1.8.2 // indirect 26 | github.com/subosito/gotenv v1.4.2 // indirect 27 | golang.org/x/sys v0.7.0 // indirect 28 | golang.org/x/text v0.7.0 // indirect 29 | gonum.org/v1/gonum v0.12.0 // indirect 30 | gopkg.in/ini.v1 v1.67.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /utils/logout.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "runtime" 4 | 5 | //LogOutDesc returns the text of the logout command based on runtime 6 | func LogOutDesc() string { 7 | if runtime.GOOS == "windows" { 8 | return "Removes login information from pce.yaml and optionally removes all workloader generated API keys from PCE." 9 | } 10 | return "Removes pce.yaml file and optionally removes all workloader generated API keys from PCE." 11 | } 12 | -------------------------------------------------------------------------------- /utils/logv2.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/brian1917/illumioapi/v2" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | // LogAPIResp will log the HTTP Requset, Request Header, Response Status Code, and Response Body 11 | // The callType should be the name of call: GetAllLabels, GetAllWorkloads, etc. This is just for logging purposes and any string will be accepted. 12 | // The log type will be DEBUG. 13 | // This call will not do anything if the debug flag isn't set. A debug conditional is not required in app code. 14 | func LogAPIRespV2(callType string, apiResp illumioapi.APIResponse) { 15 | 16 | // Get the original logging status in case it's flipped for a non-200 status code 17 | orginalDebug := viper.Get("debug").(bool) 18 | 19 | // If we have a bad API response, set the debug to true 20 | if apiResp.StatusCode > 299 { 21 | viper.Set("debug", true) 22 | } 23 | 24 | if apiResp.Request != nil { 25 | LogInfof(false, "%s http request: %s %v", callType, apiResp.Request.Method, apiResp.Request.URL) 26 | if apiResp.ReqBody != "" { 27 | LogInfof(false, "%s request body: %s", callType, apiResp.ReqBody) 28 | } 29 | } 30 | LogInfo(fmt.Sprintf("%s response status code: %d", callType, apiResp.StatusCode), false) 31 | if viper.Get("verbose").(bool) || apiResp.StatusCode > 299 { 32 | LogDebug(fmt.Sprintf("%s response body: %s", callType, apiResp.RespBody)) 33 | } 34 | 35 | for _, w := range apiResp.Warnings { 36 | LogWarning(w, true) 37 | } 38 | 39 | // Put the logging back to the original status in case it was flipped for a non-200 status code 40 | viper.Set("debug", orginalDebug) 41 | } 42 | 43 | func LogMultiAPIRespV2(APIResps map[string]illumioapi.APIResponse) { 44 | for k, v := range APIResps { 45 | LogAPIRespV2(k, v) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /utils/newlinereplace.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "strings" 4 | 5 | // ReplaceNewLine replaces the \r and \n with a space 6 | func ReplaceNewLine(s string) string { 7 | x := strings.Replace(s, "\r", " ", -1) 8 | x = strings.Replace(x, "\n", " ", -1) 9 | return x 10 | } 11 | -------------------------------------------------------------------------------- /utils/output.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "os" 7 | "time" 8 | 9 | "github.com/olekukonko/tablewriter" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | // WriteOutput will write the CSV and/or stdout data based on the viper configuration 14 | func WriteOutput(csvData, stdOutData [][]string, csvFileName string) { 15 | 16 | // Get the output format 17 | outFormat := viper.Get("output_format").(string) 18 | 19 | // Write stdout if output format dictates it 20 | if outFormat == "stdout" || outFormat == "both" { 21 | if len(stdOutData) < viper.Get("max_entries_for_stdout").(int) { 22 | table := tablewriter.NewWriter(os.Stdout) 23 | table.SetHeader(stdOutData[0]) 24 | for i := 1; i <= len(stdOutData)-1; i++ { 25 | table.Append(stdOutData[i]) 26 | } 27 | table.SetAlignment(tablewriter.ALIGN_LEFT) 28 | table.SetRowLine(true) 29 | table.Render() 30 | } 31 | } 32 | 33 | // Write CSV data if output format dictates it 34 | if outFormat == "csv" || outFormat == "both" { 35 | 36 | // Create CSV 37 | outFile, err := os.Create(csvFileName) 38 | if err != nil { 39 | LogError(fmt.Sprintf("creating csv - %s\n", err)) 40 | } 41 | 42 | // Write CSV data 43 | writer := csv.NewWriter(outFile) 44 | if os.Getenv("WORKLOADER_CSV_DELIMITER") != "" { 45 | writer.Comma = rune(os.Getenv("WORKLOADER_CSV_DELIMITER")[0]) 46 | } 47 | writer.WriteAll(csvData) 48 | if err := writer.Error(); err != nil { 49 | LogError(fmt.Sprintf("writing csv - %s\n", err)) 50 | } 51 | // Log 52 | LogInfo(fmt.Sprintf("output file: %s", outFile.Name()), true) 53 | } 54 | } 55 | 56 | // WriteLineOutput will write the CSV one line at a time 57 | func WriteLineOutput(csvLine []string, csvFileName string) { 58 | 59 | var outFile *os.File 60 | 61 | // Create CSV if it doesn't exist 62 | if _, err := os.Stat(csvFileName); err != nil { 63 | outFile, err = os.Create(csvFileName) 64 | if err != nil { 65 | LogError(fmt.Sprintf("creating csv - %s\n", err)) 66 | } 67 | LogInfo(fmt.Sprintf("output file started: %s", outFile.Name()), true) 68 | 69 | } else { 70 | outFile, err = os.OpenFile(csvFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 71 | if err != nil { 72 | LogError(fmt.Sprintf("opening csv - %s\n", err)) 73 | } 74 | 75 | } 76 | defer outFile.Close() 77 | 78 | // Write CSV data 79 | writer := csv.NewWriter(outFile) 80 | if os.Getenv("WORKLOADER_CSV_DELIMITER") != "" { 81 | writer.Comma = rune(os.Getenv("WORKLOADER_CSV_DELIMITER")[0]) 82 | } 83 | defer writer.Flush() 84 | 85 | if err := writer.Write(csvLine); err != nil { 86 | LogError(fmt.Sprintf("error writing csv line - %s", err)) 87 | } 88 | } 89 | 90 | func FileName(suffix string) string { 91 | if suffix != "" { 92 | return fmt.Sprintf("workloader-%s-%s-%s.csv", os.Args[1], suffix, time.Now().Format("20060102_150405")) 93 | } 94 | return fmt.Sprintf("workloader-%s-%s.csv", os.Args[1], time.Now().Format("20060102_150405")) 95 | } 96 | -------------------------------------------------------------------------------- /utils/parsecsv.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "encoding/csv" 6 | "io" 7 | "os" 8 | ) 9 | 10 | // ParseCSV parses a file and returns a slice of slice of strings 11 | func ParseCSV(filename string) ([][]string, error) { 12 | 13 | // Open CSV File and create the reader 14 | file, err := os.Open(filename) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer file.Close() 19 | reader := csv.NewReader(ClearBOM(bufio.NewReader(file))) 20 | if os.Getenv("WORKLOADER_CSV_DELIMITER") != "" { 21 | reader.Comma = rune(os.Getenv("WORKLOADER_CSV_DELIMITER")[0]) 22 | } 23 | 24 | // Create our slice to return 25 | var data [][]string 26 | 27 | // Iterate through CSV entries 28 | for { 29 | 30 | // Read the line 31 | line, err := reader.Read() 32 | if err == io.EOF { 33 | break 34 | } 35 | if err != nil { 36 | return nil, err 37 | } 38 | // Append 39 | data = append(data, line) 40 | } 41 | 42 | return data, nil 43 | } 44 | 45 | // ParseCsvHeaders parses a file and returns a slice of slice of strings and header map 46 | // The header map points to the header index in the slice 47 | func ParseCsvHeaders(filename string) (csvData [][]string, headerMap map[string]int, err error) { 48 | headerMap = make(map[string]int) 49 | csvData, err = ParseCSV(filename) 50 | for i, column := range csvData[0] { 51 | headerMap[column] = i 52 | } 53 | return csvData, headerMap, err 54 | } 55 | -------------------------------------------------------------------------------- /utils/pce.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/brian1917/illumioapi" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | // GetTargetPCE gets the target PCE for a command 13 | func GetTargetPCE(GetLabelMaps bool) (illumioapi.PCE, error) { 14 | 15 | // Get the PCE name 16 | var name string 17 | if viper.Get("target_pce") != nil && viper.Get("target_pce").(string) != "" { 18 | name = viper.Get("target_pce").(string) 19 | } else if viper.Get("default_pce_name") != nil && viper.Get("default_pce_name").(string) != "" { 20 | name = viper.Get("default_pce_name").(string) 21 | } else { 22 | LogError("there is no pce set using the --pce flag and there is no default pce. either run workloader pce-add to add your first pce or workloader set-default to set an existing PCE as default.") 23 | } 24 | 25 | // Get the PCE 26 | pce, err := GetPCEbyName(name, GetLabelMaps) 27 | if err != nil { 28 | return illumioapi.PCE{}, err 29 | } 30 | 31 | // Adjust PCE for when no auth 32 | 33 | if pce.User == "" { 34 | if os.Getenv("WORKLOADER_API_USER") == "" { 35 | return pce, fmt.Errorf("%s does not have an api user and the WORKLOADER_API_USER env variable is not set", name) 36 | } 37 | pce.User = os.Getenv("WORKLOADER_API_USER") 38 | } 39 | 40 | if pce.Key == "" { 41 | if os.Getenv("WORKLOADER_API_KEY") == "" { 42 | return pce, fmt.Errorf("%s does not have an api key and the WORKLOADER_API_KEY env variable is not set", name) 43 | } 44 | pce.Key = os.Getenv("WORKLOADER_API_KEY") 45 | } 46 | 47 | if pce.Org == 0 { 48 | if os.Getenv("WORKLOADER_ORG") == "" { 49 | return pce, fmt.Errorf("%s does not have an org and the WORKLOADER_ORG env variable is not set", name) 50 | } 51 | pce.Org, err = strconv.Atoi(os.Getenv("WORKLOADER_ORG")) 52 | if err != nil { 53 | return pce, fmt.Errorf("%s is not valid org for WORKLOADER_ORG env variable", os.Getenv("WORKLOADER_ORG")) 54 | } 55 | } 56 | 57 | return pce, nil 58 | } 59 | 60 | // GetPCEbyName gets a PCE by it's provided name 61 | func GetPCEbyName(name string, GetLabelMaps bool) (illumioapi.PCE, error) { 62 | var pce illumioapi.PCE 63 | if viper.IsSet(name + ".fqdn") { 64 | pce = illumioapi.PCE{FriendlyName: name, FQDN: viper.Get(name + ".fqdn").(string), Port: viper.Get(name + ".port").(int), Org: viper.Get(name + ".org").(int), User: viper.Get(name + ".user").(string), Key: viper.Get(name + ".key").(string), DisableTLSChecking: viper.Get(name + ".disableTLSChecking").(bool)} 65 | if viper.Get(name+".proxy") != nil { 66 | pce.Proxy = viper.Get(name + ".proxy").(string) 67 | } 68 | if GetLabelMaps { 69 | apiResps, err := pce.Load(illumioapi.LoadInput{Labels: true}) 70 | LogMultiAPIResp(apiResps) 71 | if err != nil { 72 | LogError(err.Error()) 73 | } 74 | } 75 | _, api, err := pce.GetVersion() 76 | if err != nil { 77 | return illumioapi.PCE{}, fmt.Errorf("error getting pce version - %s - %s - %d", err, api.RespBody, api.StatusCode) 78 | } 79 | viper.Set(name+".pce_version", fmt.Sprintf("%d.%d.%d-%d", pce.Version.Major, pce.Version.Minor, pce.Version.Patch, pce.Version.Build)) 80 | if err := viper.WriteConfig(); err != nil { 81 | LogError(err.Error()) 82 | } 83 | return pce, nil 84 | } 85 | 86 | return illumioapi.PCE{}, fmt.Errorf("could not retrieve %s PCE information", name) 87 | } 88 | 89 | // GetPCEbyName gets a PCE by it's provided name 90 | func GetPCENoAPI(name string) (illumioapi.PCE, error) { 91 | var pce illumioapi.PCE 92 | if viper.IsSet(name + ".fqdn") { 93 | pce = illumioapi.PCE{FriendlyName: name, FQDN: viper.Get(name + ".fqdn").(string), Port: viper.Get(name + ".port").(int), Org: viper.Get(name + ".org").(int), User: viper.Get(name + ".user").(string), Key: viper.Get(name + ".key").(string), DisableTLSChecking: viper.Get(name + ".disableTLSChecking").(bool)} 94 | if viper.Get(name+".proxy") != nil { 95 | pce.Proxy = viper.Get(name + ".proxy").(string) 96 | } 97 | return pce, nil 98 | } 99 | 100 | return illumioapi.PCE{}, fmt.Errorf("could not retrieve %s PCE information", name) 101 | } 102 | 103 | func UseMulti() bool { 104 | if viper.Get("get_api_behavior") == nil || viper.Get("get_api_behavior").(string) == "multi" { 105 | LogInfo("using multi get api behavior", false) 106 | return true 107 | } 108 | LogInfo("using single get api behavior", false) 109 | return false 110 | } 111 | -------------------------------------------------------------------------------- /utils/pcev2.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/brian1917/illumioapi/v2" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | // GetTargetPCE gets the target PCE for a command 13 | func GetTargetPCEV2(GetLabelMaps bool) (illumioapi.PCE, error) { 14 | 15 | // Get the PCE name 16 | var name string 17 | if viper.Get("target_pce") != nil && viper.Get("target_pce").(string) != "" { 18 | name = viper.Get("target_pce").(string) 19 | } else if viper.Get("default_pce_name") != nil && viper.Get("default_pce_name").(string) != "" { 20 | name = viper.Get("default_pce_name").(string) 21 | } else { 22 | LogError("there is no pce set using the --pce flag and there is no default pce. either run workloader pce-add to add your first pce or workloader set-default to set an existing PCE as default.") 23 | } 24 | 25 | // Get the PCE 26 | pce, err := GetPCEbyNameV2(name, GetLabelMaps) 27 | if err != nil { 28 | return illumioapi.PCE{}, err 29 | } 30 | 31 | // Adjust PCE for when no auth 32 | 33 | if pce.User == "" { 34 | if os.Getenv("WORKLOADER_API_USER") == "" { 35 | return pce, fmt.Errorf("%s does not have an api user and the WORKLOADER_API_USER env variable is not set", name) 36 | } 37 | pce.User = os.Getenv("WORKLOADER_API_USER") 38 | } 39 | 40 | if pce.Key == "" { 41 | if os.Getenv("WORKLOADER_API_KEY") == "" { 42 | return pce, fmt.Errorf("%s does not have an api key and the WORKLOADER_API_KEY env variable is not set", name) 43 | } 44 | pce.Key = os.Getenv("WORKLOADER_API_KEY") 45 | } 46 | 47 | if pce.Org == 0 { 48 | if os.Getenv("WORKLOADER_ORG") == "" { 49 | return pce, fmt.Errorf("%s does not have an org and the WORKLOADER_ORG env variable is not set", name) 50 | } 51 | pce.Org, err = strconv.Atoi(os.Getenv("WORKLOADER_ORG")) 52 | if err != nil { 53 | return pce, fmt.Errorf("%s is not valid org for WORKLOADER_ORG env variable", os.Getenv("WORKLOADER_ORG")) 54 | } 55 | } 56 | 57 | return pce, nil 58 | } 59 | 60 | // GetPCEbyName gets a PCE by it's provided name 61 | func GetPCEbyNameV2(name string, GetLabelMaps bool) (illumioapi.PCE, error) { 62 | var pce illumioapi.PCE 63 | if viper.IsSet(name + ".fqdn") { 64 | pce = illumioapi.PCE{FriendlyName: name, FQDN: viper.Get(name + ".fqdn").(string), Port: viper.Get(name + ".port").(int), Org: viper.Get(name + ".org").(int), User: viper.Get(name + ".user").(string), Key: viper.Get(name + ".key").(string), DisableTLSChecking: viper.Get(name + ".disableTLSChecking").(bool)} 65 | if viper.Get(name+".proxy") != nil { 66 | pce.Proxy = viper.Get(name + ".proxy").(string) 67 | } 68 | if GetLabelMaps { 69 | apiResp, err := pce.GetLabels(nil) 70 | LogAPIRespV2("GetLabels", apiResp) 71 | if err != nil { 72 | LogError(err.Error()) 73 | } 74 | } 75 | _, api, err := pce.GetVersion() 76 | LogAPIRespV2("GetVersion", api) 77 | if err != nil { 78 | return illumioapi.PCE{}, fmt.Errorf("error getting pce version - %s - %s - %d", err, api.RespBody, api.StatusCode) 79 | } 80 | viper.Set(name+".pce_version", fmt.Sprintf("%d.%d.%d-%d", pce.Version.Major, pce.Version.Minor, pce.Version.Patch, pce.Version.Build)) 81 | if err := viper.WriteConfig(); err != nil { 82 | LogError(err.Error()) 83 | } 84 | return pce, nil 85 | } 86 | 87 | return illumioapi.PCE{}, fmt.Errorf("could not retrieve %s PCE information", name) 88 | } 89 | -------------------------------------------------------------------------------- /utils/ptr.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // PtrToStr converts a pointer to a string. nil pointer returns an empty string 4 | func PtrToStr(ptr *string) string { 5 | if ptr == nil { 6 | return "" 7 | } 8 | return *ptr 9 | } 10 | 11 | // StrToPtr returns a pointer from a string value 12 | func StrToPtr(str string) *string { 13 | return &str 14 | } 15 | -------------------------------------------------------------------------------- /utils/rfc1918.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "net" 4 | 5 | // IsRFC1918 returns true if an IP address is in the RFC1918 range 6 | func IsRFC1918(ipAddress string) bool { 7 | 8 | ip := net.ParseIP(ipAddress) 9 | if ip == nil { 10 | return false 11 | } 12 | 13 | // Define the private IP ranges 14 | privateIPBlocks := []*net.IPNet{ 15 | { 16 | IP: net.IPv4(10, 0, 0, 0), 17 | Mask: net.CIDRMask(8, 32), 18 | }, 19 | { 20 | IP: net.IPv4(172, 16, 0, 0), 21 | Mask: net.CIDRMask(12, 32), 22 | }, 23 | { 24 | IP: net.IPv4(192, 168, 0, 0), 25 | Mask: net.CIDRMask(16, 32), 26 | }, 27 | } 28 | 29 | // Check if the IP is within any of the private IP ranges 30 | for _, privateIPBlock := range privateIPBlocks { 31 | if privateIPBlock.Contains(ip) { 32 | return true 33 | } 34 | } 35 | 36 | return false 37 | } 38 | -------------------------------------------------------------------------------- /utils/serviceports.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "encoding/csv" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strconv" 10 | 11 | "github.com/brian1917/illumioapi" 12 | ) 13 | 14 | // GetServicePortsPCE returns PortProto list and PortRangeProto for use in a traffic query from a service object in the PCE 15 | func GetServicePortsPCE(pce illumioapi.PCE, serviceName string) ([][2]int, [][3]int) { 16 | 17 | // Create our return 18 | portProtoExcl := [][2]int{} 19 | portRangeProtoExcl := [][3]int{} 20 | 21 | // Get all services 22 | svcs, _, err := pce.GetServices(nil, "draft") 23 | if err != nil { 24 | LogError(err.Error()) 25 | } 26 | 27 | // Find our service of interest 28 | for _, s := range svcs { 29 | if s.Name == serviceName { 30 | for _, sp := range s.ServicePorts { 31 | if sp.ToPort != 0 { 32 | portRangeProtoExcl = append(portRangeProtoExcl, [3]int{sp.Port, sp.ToPort, sp.Protocol}) 33 | } else { 34 | portProtoExcl = append(portProtoExcl, [2]int{sp.Port, sp.Protocol}) 35 | } 36 | } 37 | return portProtoExcl, portRangeProtoExcl 38 | } 39 | } 40 | return nil, nil 41 | } 42 | 43 | // GetServicePortsCSV returns port proto list from a CSV 44 | func GetServicePortsCSV(filename string) ([][2]int, error) { 45 | // Open CSV File 46 | csvFile, err := os.Open(filename) 47 | if err != nil { 48 | return nil, err 49 | } 50 | defer csvFile.Close() 51 | 52 | reader := csv.NewReader(ClearBOM(bufio.NewReader(csvFile))) 53 | if os.Getenv("WORKLOADER_CSV_DELIMITER") != "" { 54 | reader.Comma = rune(os.Getenv("WORKLOADER_CSV_DELIMITER")[0]) 55 | } 56 | 57 | svcList := [][2]int{} 58 | 59 | // Start the CSV line counter 60 | n := 0 61 | 62 | // Iterate over lines 63 | for { 64 | 65 | // Increase the counter 66 | n++ 67 | 68 | // Read the line 69 | line, err := reader.Read() 70 | if err == io.EOF { 71 | break 72 | } 73 | if err != nil { 74 | return nil, fmt.Errorf("reading CSV File for port/protocol list - %s", err) 75 | } 76 | 77 | // Check the first line and skip if it's not integers 78 | if n == 1 { 79 | _, err = strconv.Atoi(line[0]) 80 | if err != nil { 81 | continue 82 | } 83 | _, err = strconv.Atoi(line[1]) 84 | if err != nil { 85 | continue 86 | } 87 | } 88 | 89 | // Convert the port 90 | port, err := strconv.Atoi(line[0]) 91 | if err != nil { 92 | return nil, fmt.Errorf("non-integer port value on line %d - %s", n, err) 93 | } 94 | 95 | // Convert the protocol 96 | protocol, err := strconv.Atoi(line[1]) 97 | if err != nil { 98 | return nil, fmt.Errorf("non-integer protocol value on line %d - %s", n, err) 99 | } 100 | 101 | // Append to the list 102 | svcList = append(svcList, [2]int{port, protocol}) 103 | } 104 | 105 | return svcList, nil 106 | } 107 | 108 | // GetProcesses returns a list of processes from an inputfile 109 | func GetProcesses(file string) ([]string, error) { 110 | csvData, err := ParseCSV(file) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | var processes []string 116 | for _, row := range csvData { 117 | processes = append(processes, row[0]) 118 | } 119 | 120 | return processes, nil 121 | } 122 | -------------------------------------------------------------------------------- /utils/version.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // Version is set by build variable 4 | var Version string 5 | 6 | // Commit is the latest commit 7 | var Commit string 8 | 9 | // GetVersion returns the version set by the build variable reads the PCE information from the JSON generated by the login command 10 | func GetVersion() string { 11 | return Version 12 | } 13 | 14 | // GetCommit returns the latest commit set by the build variable 15 | func GetCommit() string { 16 | return Commit 17 | } 18 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 12.0.17 -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Read the version from the 'version' file 4 | VERSION=$(cat version) 5 | 6 | # Create the version.rc file 7 | cat < version.rc 8 | #include 9 | 10 | VS_VERSION_INFO VERSIONINFO 11 | FILEVERSION 1,0,0,0 12 | PRODUCTVERSION 1,0,0,0 13 | FILEFLAGSMASK 0x3fL 14 | FILEFLAGS 0x0L 15 | FILEOS VOS_NT_WINDOWS32 16 | FILETYPE VFT_APP 17 | FILESUBTYPE VFT2_UNKNOWN 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904b0" 22 | BEGIN 23 | VALUE "FileDescription", "Workloader is an open-source CLI tool that leverages the Illumio API to manage resources and automate common tasks." 24 | VALUE "InternalName", "workloader" 25 | VALUE "OriginalFilename", "workloader.exe" 26 | VALUE "ProductName", "Workloader" 27 | VALUE "ProductVersion", "$VERSION" 28 | END 29 | END 30 | BLOCK "VarFileInfo" 31 | BEGIN 32 | VALUE "Translation", 0x0409, 1200 33 | END 34 | END 35 | EOF --------------------------------------------------------------------------------