@aws-cdk/cloudformation-diff
Version:
Utilities to diff CDK stacks against CloudFormation templates
375 lines (374 loc) • 13.5 kB
TypeScript
import { PropertyScrutinyType, ResourceScrutinyType } from '@aws-cdk/service-spec-types';
import { IamChanges } from '../iam/iam-changes';
import { SecurityGroupChanges } from '../network/security-group-changes';
export type PropertyMap = {
[key: string]: any;
};
export type ChangeSetResources = {
[logicalId: string]: ChangeSetResource;
};
/**
* @param beforeContext - is the BeforeContext field from the ChangeSet.ResourceChange.BeforeContext. This is the part of the CloudFormation template
* that defines what the resource is before the change is applied; that is, BeforeContext is CloudFormationTemplate.Resources[LogicalId] before the ChangeSet is executed.
*
* @param afterContext - same as beforeContext but for after the change is made; that is, AfterContext is CloudFormationTemplate.Resources[LogicalId] after the ChangeSet is executed.
*
* * Here is an example of what a beforeContext/afterContext looks like:
* '{"Properties":{"Value":"sdflkja","Type":"String","Name":"mySsmParameterFromStack"},"Metadata":{"aws:cdk:path":"cdk/mySsmParameter/Resource"}}'
*/
export interface ChangeSetResource {
resourceWasReplaced: boolean;
resourceType: string | undefined;
propertyReplacementModes: PropertyReplacementModeMap | undefined;
}
export type PropertyReplacementModeMap = {
[propertyName: string]: {
replacementMode: ReplacementModes | undefined;
};
};
/**
* 'Always' means that changing the corresponding property will always cause a resource replacement. Never means never. Conditionally means maybe.
*/
export type ReplacementModes = 'Always' | 'Never' | 'Conditionally';
/** Semantic differences between two CloudFormation templates. */
export declare class TemplateDiff implements ITemplateDiff {
awsTemplateFormatVersion?: Difference<string>;
description?: Difference<string>;
transform?: Difference<string>;
conditions: DifferenceCollection<Condition, ConditionDifference>;
mappings: DifferenceCollection<Mapping, MappingDifference>;
metadata: DifferenceCollection<Metadata, MetadataDifference>;
outputs: DifferenceCollection<Output, OutputDifference>;
parameters: DifferenceCollection<Parameter, ParameterDifference>;
resources: DifferenceCollection<Resource, ResourceDifference>;
/** The differences in unknown/unexpected parts of the template */
unknown: DifferenceCollection<any, Difference<any>>;
/**
* Changes to IAM policies
*/
readonly iamChanges: IamChanges;
/**
* Changes to Security Group ingress and egress rules
*/
readonly securityGroupChanges: SecurityGroupChanges;
constructor(args: ITemplateDiff);
get differenceCount(): number;
get isEmpty(): boolean;
/**
* Return true if any of the permissions objects involve a broadening of permissions
*/
get permissionsBroadened(): boolean;
/**
* Return true if any of the permissions objects have changed
*/
get permissionsAnyChanges(): boolean;
/**
* Return all property changes of a given scrutiny type
*
* We don't just look at property updates; we also look at resource additions and deletions (in which
* case there is no further detail on property values), and resource type changes.
*/
private scrutinizablePropertyChanges;
/**
* Return all resource changes of a given scrutiny type
*
* We don't just look at resource updates; we also look at resource additions and deletions (in which
* case there is no further detail on property values), and resource type changes.
*/
private scrutinizableResourceChanges;
private resourceIsScrutinizable;
}
/**
* A change in property values
*
* Not necessarily an update, it could be that there used to be no value there
* because there was no resource, and now there is (or vice versa).
*
* Therefore, we just contain plain values and not a PropertyDifference<any>.
*/
export interface PropertyChange {
/**
* Logical ID of the resource where this property change was found
*/
resourceLogicalId: string;
/**
* Type of the resource
*/
resourceType: string;
/**
* Scrutiny type for this property change
*/
scrutinyType: PropertyScrutinyType;
/**
* Name of the property that is changing
*/
propertyName: string;
/**
* The old property value
*/
oldValue?: any;
/**
* The new property value
*/
newValue?: any;
}
/**
* A resource change
*
* Either a creation, deletion or update.
*/
export interface ResourceChange {
/**
* Logical ID of the resource where this property change was found
*/
resourceLogicalId: string;
/**
* Scrutiny type for this resource change
*/
scrutinyType: ResourceScrutinyType;
/**
* The type of the resource
*/
resourceType: string;
/**
* The old properties value (might be undefined in case of creation)
*/
oldProperties?: PropertyMap;
/**
* The new properties value (might be undefined in case of deletion)
*/
newProperties?: PropertyMap;
}
export interface IDifference<ValueType> {
readonly oldValue: ValueType | undefined;
readonly newValue: ValueType | undefined;
readonly isDifferent: boolean;
readonly isAddition: boolean;
readonly isRemoval: boolean;
readonly isUpdate: boolean;
}
/**
* Models an entity that changed between two versions of a CloudFormation template.
*/
export declare class Difference<ValueType> implements IDifference<ValueType> {
readonly oldValue: ValueType | undefined;
readonly newValue: ValueType | undefined;
/**
* Whether this is an actual different or the values are actually the same
*
* isDifferent => (isUpdate | isRemoved | isUpdate)
*/
isDifferent: boolean;
/**
* @param oldValue - the old value, cannot be equal (to the sense of +deepEqual+) to +newValue+.
* @param newValue - the new value, cannot be equal (to the sense of +deepEqual+) to +oldValue+.
*/
constructor(oldValue: ValueType | undefined, newValue: ValueType | undefined);
/** @returns +true+ if the element is new to the template. */
get isAddition(): boolean;
/** @returns +true+ if the element was removed from the template. */
get isRemoval(): boolean;
/** @returns +true+ if the element was already in the template and is updated. */
get isUpdate(): boolean;
}
export declare class PropertyDifference<ValueType> extends Difference<ValueType> {
changeImpact?: ResourceImpact;
constructor(oldValue: ValueType | undefined, newValue: ValueType | undefined, args: {
changeImpact?: ResourceImpact;
});
}
export declare class DifferenceCollection<V, T extends IDifference<V>> {
private readonly diffs;
constructor(diffs: {
[logicalId: string]: T;
});
get changes(): {
[logicalId: string]: T;
};
get differenceCount(): number;
get(logicalId: string): T;
remove(logicalId: string): void;
get logicalIds(): string[];
/**
* Returns a new TemplateDiff which only contains changes for which `predicate`
* returns `true`.
*/
filter(predicate: (diff: T | undefined) => boolean): DifferenceCollection<V, T>;
/**
* Invokes `cb` for all changes in this collection.
*
* Changes will be sorted as follows:
* - Removed
* - Added
* - Updated
* - Others
*
*/
forEachDifference(cb: (logicalId: string, change: T) => any): void;
}
/**
* Arguments expected by the constructor of +TemplateDiff+, extracted as an interface for the sake
* of (relative) conciseness of the constructor's signature.
*/
export interface ITemplateDiff {
awsTemplateFormatVersion?: IDifference<string>;
description?: IDifference<string>;
transform?: IDifference<string>;
conditions?: DifferenceCollection<Condition, ConditionDifference>;
mappings?: DifferenceCollection<Mapping, MappingDifference>;
metadata?: DifferenceCollection<Metadata, MetadataDifference>;
outputs?: DifferenceCollection<Output, OutputDifference>;
parameters?: DifferenceCollection<Parameter, ParameterDifference>;
resources?: DifferenceCollection<Resource, ResourceDifference>;
unknown?: DifferenceCollection<any, IDifference<any>>;
}
export type Condition = any;
export declare class ConditionDifference extends Difference<Condition> {
}
export type Mapping = any;
export declare class MappingDifference extends Difference<Mapping> {
}
export type Metadata = any;
export declare class MetadataDifference extends Difference<Metadata> {
}
export type Output = any;
export declare class OutputDifference extends Difference<Output> {
}
export type Parameter = any;
export declare class ParameterDifference extends Difference<Parameter> {
}
export declare enum ResourceImpact {
/** The existing physical resource will be updated */
WILL_UPDATE = "WILL_UPDATE",
/** A new physical resource will be created */
WILL_CREATE = "WILL_CREATE",
/** The existing physical resource will be replaced */
WILL_REPLACE = "WILL_REPLACE",
/** The existing physical resource may be replaced */
MAY_REPLACE = "MAY_REPLACE",
/** The existing physical resource will be destroyed */
WILL_DESTROY = "WILL_DESTROY",
/** The existing physical resource will be removed from CloudFormation supervision */
WILL_ORPHAN = "WILL_ORPHAN",
/** The existing physical resource will be added to CloudFormation supervision */
WILL_IMPORT = "WILL_IMPORT",
/** There is no change in this resource */
NO_CHANGE = "NO_CHANGE"
}
export interface Resource {
Type: string;
Properties?: {
[name: string]: any;
};
[key: string]: any;
}
export interface Move {
readonly direction: 'from' | 'to';
readonly stackName: string;
readonly resourceLogicalId: string;
}
/**
* Change to a single resource between two CloudFormation templates
*
* This class can be mutated after construction.
*/
export declare class ResourceDifference implements IDifference<Resource> {
readonly oldValue: Resource | undefined;
readonly newValue: Resource | undefined;
/**
* Whether this resource was added
*/
readonly isAddition: boolean;
/**
* Whether this resource was removed
*/
readonly isRemoval: boolean;
/**
* Whether this resource was imported
*/
isImport?: boolean;
move?: Move;
/** Property-level changes on the resource */
private readonly propertyDiffs;
/** Changes to non-property level attributes of the resource */
private readonly otherDiffs;
/** The resource type (or old and new type if it has changed) */
private readonly resourceTypes;
constructor(oldValue: Resource | undefined, newValue: Resource | undefined, args: {
resourceType: {
oldType?: string;
newType?: string;
};
propertyDiffs: {
[key: string]: PropertyDifference<any>;
};
otherDiffs: {
[key: string]: Difference<any>;
};
});
get oldProperties(): PropertyMap | undefined;
get newProperties(): PropertyMap | undefined;
/**
* Whether this resource was modified at all
*/
get isDifferent(): boolean;
/**
* Whether the resource was updated in-place
*/
get isUpdate(): boolean;
get oldResourceType(): string | undefined;
get newResourceType(): string | undefined;
/**
* All actual property updates
*/
get propertyUpdates(): {
[key: string]: PropertyDifference<any>;
};
/**
* All actual "other" updates
*/
get otherChanges(): {
[key: string]: Difference<any>;
};
/**
* Return whether the resource type was changed in this diff
*
* This is not a valid operation in CloudFormation but to be defensive we're going
* to be aware of it anyway.
*/
get resourceTypeChanged(): boolean;
/**
* Return the resource type if it was unchanged
*
* If the resource type was changed, it's an error to call this.
*/
get resourceType(): string | undefined;
/**
* Replace a PropertyChange in this object
*
* This affects the property diff as it is summarized to users, but it DOES
* NOT affect either the "oldValue" or "newValue" values; those still contain
* the actual template values as provided by the user (they might still be
* used for downstream processing).
*/
setPropertyChange(propertyName: string, change: PropertyDifference<any>): void;
/**
* Replace a OtherChange in this object
*
* This affects the property diff as it is summarized to users, but it DOES
* NOT affect either the "oldValue" or "newValue" values; those still contain
* the actual template values as provided by the user (they might still be
* used for downstream processing).
*/
setOtherChange(otherName: string, change: PropertyDifference<any>): void;
get changeImpact(): ResourceImpact;
/**
* Count of actual differences (not of elements)
*/
get differenceCount(): number;
/**
* Invoke a callback for each actual difference
*/
forEachDifference(cb: (type: 'Property' | 'Other', name: string, value: Difference<any> | PropertyDifference<any>) => any): void;
}
export declare function isPropertyDifference<T>(diff: Difference<T>): diff is PropertyDifference<T>;