@@ -28,6 +28,10 @@ const (
2828 fieldCommitmentsGCPCUDs = "gcp_cuds"
2929 fieldCommitmentsConfigs = "commitment_configs"
3030 fieldCommitmentsOrganizationId = "organization_id"
31+ fieldCommitmentsImportMode = "import_mode"
32+
33+ importModeOverwrite = "OVERWRITE"
34+ importModeAppend = "APPEND"
3135)
3236
3337var (
@@ -156,6 +160,13 @@ func resourceCommitments() *schema.Resource {
156160 Optional : true ,
157161 Description : "Organization ID. If not provided, will be fetched from the API using the authentication token." ,
158162 },
163+ fieldCommitmentsImportMode : {
164+ Type : schema .TypeString ,
165+ Optional : true ,
166+ Default : importModeOverwrite ,
167+ Description : "Import mode. OVERWRITE replaces all commitments for the CSP. APPEND upserts without deleting existing ones." ,
168+ ValidateDiagFunc : validation .ToDiagFunc (validation .StringInSlice ([]string {importModeOverwrite , importModeAppend }, false )),
169+ },
159170 // Input configurations
160171 fieldCommitmentsConfigs : {
161172 Type : schema .TypeList ,
@@ -563,21 +574,42 @@ func resourceCastaiCommitmentsUpsert(ctx context.Context, data *schema.ResourceD
563574 return diag .FromErr (err )
564575 }
565576
577+ mode := data .Get (fieldCommitmentsImportMode ).(string )
578+
566579 var imported []sdk.CastaiInventoryV1beta1Commitment
567580 switch {
568581 case reservationsOk :
569- if err := importReservations (ctx , meta , reservations ); err != nil {
582+ if err := importReservations (ctx , meta , reservations , mode ); err != nil {
570583 return diag .FromErr (err )
571584 }
572585 orgCommitments , err := getOrganizationCommitments (ctx , meta )
573586 if err != nil {
574587 return diag .FromErr (err )
575588 }
576- imported = lo .Filter (orgCommitments , func (c sdk.CastaiInventoryV1beta1Commitment , _ int ) bool {
577- return c .AzureReservationContext != nil
578- })
579- if len (imported ) != len (reservations ) {
580- return diag .Errorf ("expected %d Azure commitments, got %d" , len (reservations ), len (imported ))
589+
590+ if mode == importModeAppend {
591+ // Build set of reservation IDs from the import input
592+ importReservationIDs := make (map [string ]struct {})
593+ for _ , r := range reservations {
594+ if r .ReservationId != nil {
595+ importReservationIDs [* r .ReservationId ] = struct {}{}
596+ }
597+ }
598+ // Filter org commitments to only those matching our import input
599+ imported = lo .Filter (orgCommitments , func (c sdk.CastaiInventoryV1beta1Commitment , _ int ) bool {
600+ if c .AzureReservationContext == nil || c .AzureReservationContext .Id == nil {
601+ return false
602+ }
603+ _ , ok := importReservationIDs [* c .AzureReservationContext .Id ]
604+ return ok
605+ })
606+ } else {
607+ imported = lo .Filter (orgCommitments , func (c sdk.CastaiInventoryV1beta1Commitment , _ int ) bool {
608+ return c .AzureReservationContext != nil
609+ })
610+ if len (imported ) != len (reservations ) {
611+ return diag .Errorf ("expected %d Azure commitments, got %d" , len (reservations ), len (imported ))
612+ }
581613 }
582614 case cudsOk :
583615 if err := importCUDs (ctx , meta , cuds ); err != nil {
@@ -649,7 +681,7 @@ func importCUDs(ctx context.Context, meta any, imports []sdk.CastaiInventoryV1be
649681 res , err := meta .(* ProviderConfig ).api .CommitmentsAPIImportGCPCommitmentsWithResponse (
650682 ctx ,
651683 & sdk.CommitmentsAPIImportGCPCommitmentsParams {
652- Behaviour : lo.ToPtr [sdk.CommitmentsAPIImportGCPCommitmentsParamsBehaviour ]("OVERWRITE" ),
684+ Behaviour : lo.ToPtr [sdk.CommitmentsAPIImportGCPCommitmentsParamsBehaviour ](importModeOverwrite ),
653685 },
654686 imports ,
655687 )
@@ -659,11 +691,26 @@ func importCUDs(ctx context.Context, meta any, imports []sdk.CastaiInventoryV1be
659691 return nil
660692}
661693
662- func importReservations (ctx context.Context , meta any , imports []sdk.CastaiInventoryV1beta1AzureReservationImport ) error {
694+ func toAzureImportBehaviour (mode string ) (sdk.CommitmentsAPIImportAzureReservationsParamsBehaviour , error ) {
695+ switch mode {
696+ case importModeOverwrite :
697+ return sdk .CommitmentsAPIImportAzureReservationsParamsBehaviourOVERWRITE , nil
698+ case importModeAppend :
699+ return sdk .CommitmentsAPIImportAzureReservationsParamsBehaviourAPPEND , nil
700+ default :
701+ return "" , fmt .Errorf ("unsupported azure import mode: %q" , mode )
702+ }
703+ }
704+
705+ func importReservations (ctx context.Context , meta any , imports []sdk.CastaiInventoryV1beta1AzureReservationImport , mode string ) error {
706+ behaviour , err := toAzureImportBehaviour (mode )
707+ if err != nil {
708+ return err
709+ }
663710 res , err := meta .(* ProviderConfig ).api .CommitmentsAPIImportAzureReservationsWithResponse (
664711 ctx ,
665712 & sdk.CommitmentsAPIImportAzureReservationsParams {
666- Behaviour : lo.ToPtr [sdk. CommitmentsAPIImportAzureReservationsParamsBehaviour ]( "OVERWRITE" ),
713+ Behaviour : lo .ToPtr ( behaviour ),
667714 },
668715 imports ,
669716 )
@@ -733,6 +780,23 @@ func populateCommitmentsResourceData(ctx context.Context, d *schema.ResourceData
733780 if err != nil {
734781 return err
735782 }
783+
784+ mode := ""
785+ if v , ok := d .GetOk (fieldCommitmentsImportMode ); ok {
786+ mode = v .(string )
787+ }
788+ if mode == importModeAppend && reservationsOk {
789+ // Filter azureResources to only those matching our import identifiers
790+ importIDs := make (map [string ]struct {})
791+ for _ , r := range reservations {
792+ importIDs [r .ReservationID ] = struct {}{}
793+ }
794+ azureResources = lo .Filter (azureResources , func (r * azureReservationResource , _ int ) bool {
795+ _ , ok := importIDs [r .ReservationID ]
796+ return ok
797+ })
798+ }
799+
736800 if reservationsOk {
737801 sortCommitmentResources (azureResources , reservations )
738802 }
0 commit comments